diff --git a/.gitignore b/.gitignore index 0419a80..729f8b6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ *.swp .settings/ *.project + +# jekyll related files +_site +.sass-cache +.jekyll-metadata +Gemfile.lock diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..295430c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "files.exclude": { + "**/.classpath": true, + "**/.project": true, + "**/.settings": true, + "**/.factorypath": true + }, + "sarif-viewer.connectToGithubCodeScanning": "off" +} \ No newline at end of file diff --git a/AboutJekyll.md b/AboutJekyll.md new file mode 100644 index 0000000..c58cc88 --- /dev/null +++ b/AboutJekyll.md @@ -0,0 +1,27 @@ +# Minimal Mistakes remote theme starter + +Click [**Use this template**](https://github.com/mmistakes/mm-github-pages-starter/generate) button above for the quickest method of getting started with the [Minimal Mistakes Jekyll theme](https://github.com/mmistakes/minimal-mistakes). + +Contains basic configuration to get you a site with: + +- Sample posts. +- Sample top navigation. +- Sample author sidebar with social links. +- Sample footer links. +- Paginated home page. +- Archive pages for posts grouped by year, category, and tag. +- Sample about page. +- Sample 404 page. +- Site wide search. + +Replace sample content with your own and [configure as necessary](https://mmistakes.github.io/minimal-mistakes/docs/configuration/). + +--- + +## Troubleshooting + +If you have a question about using Jekyll, start a discussion on the [Jekyll Forum](https://talk.jekyllrb.com/) or [StackOverflow](https://stackoverflow.com/questions/tagged/jekyll). Other resources: + +- [Ruby 101](https://jekyllrb.com/docs/ruby-101/) +- [Setting up a Jekyll site with GitHub Pages](https://jekyllrb.com/docs/github-pages/) +- [Configuring GitHub Metadata](https://github.com/jekyll/github-metadata/blob/master/docs/configuration.md#configuration) to work properly when developing locally and avoid `No GitHub API authentication could be found. Some fields may be missing or have incorrect data.` warnings. diff --git "a/Android\345\255\246\344\271\240\344\271\213\350\267\257.md" "b/Android\345\255\246\344\271\240\344\271\213\350\267\257.md" deleted file mode 100644 index 446f7aa..0000000 --- "a/Android\345\255\246\344\271\240\344\271\213\350\267\257.md" +++ /dev/null @@ -1,331 +0,0 @@ -# Andriod学习之路 - -* -* Intent: -* Configure Android Studio: -* Managing the System UI: -* SDK版本关系: -* Android P 开发者预览版: -* 设置应用 ID: -* (新)配置构建: -* App Shortcuts: -* dumpsys: - -* AOSP: -* 代号、标记和细分版本号: -* Repo command reference: - -* androidxref: -* grepcode: - -* Git repositories on android: -* -* -* Android Support Overview: - -* 安卓巴士: -* eoeandroid: -* -* andbook: -* -* -* -* - -* Xamarin: - -## Libraries - -* butterknife: -* xUtils2: -* xUtils3: -* SlidingMenu: -* ViewPagerIndicator: -* [DEPRECATED]ActionBarSherlock: -* jpinyin: -* Android-Universal-Image-Loader: -* hugo: -* 极光推送: - -## Tools - -* busybox: -* godeyes: -* Test DPC APK Download for Android: -* fastlane screenshots for Android: -* Android 应用开发调试利器——开发助手,数十倍效率提升: -* 奇兔刷机: -* dex-method-counts: -* dexcount-gradle-plugin: -* leakcanary: - -## Android模拟器 - -* genymotion: -* genymotion个人免费版: -* genymotion-idea-plugin: -* 夜神模拟器: - -## Android Studio插件 - -* ADB Idea 方便卸载apk,删除缓存: -* Android ButterKnife Zelezny ButterKnife对应的插件: -* Android Code Generator 生成ViewHolder,生成initView方法: -* Codota 搜索代码块: -* GsonFormat jsonString自动转bean插件: -* SelectorChapek for Android 帮助生成selector: -* Sexy Editor 代码区域加背景: -* Android Drawable Importer 同一张图片生成多个自动生成多分辨率图片: -* Android Layout ID Converter xml到控件的转换: -* Android Postfix Completion toast和log加强: -* Android Studio Prettify 帮助findViewById: -* .ignore git忽略文件高亮效果,文件夹颜色提示: -* Android Parcelable code generator 生成Parcelable代码: -* Gradle Dependencies Helper gradle帮助插件: -* Android Toolbox Plugin 生成ViewHolder,意义不大: - -## Article - -* 《Android开发艺术探索》 -* Handler内存泄漏详解及其解决方案: -* Android中Handler的使用: -* 深入源码解析Android中的Handler,Message,MessageQueue,Looper: -* Android异步更新UI的几种方法: -* AsyncTaskLoader vs AsyncTask: - -* Android 编程与兼容性问题: Android Programming The Big Nerd Ranch Guide 3rd[Android编程权威指南(第3版) 6.2 7.8 7.9 -* Android O Preview 之 通知渠道(Notification Channels): -* Android Studio移动鼠标显示悬浮提示的设置方法: -* Android真机安装sqlite3的方法: -* 使用 Intel HAXM 为 Android 模拟器加速,媲美真机: -* Android核心分析: -* ANDROID STUDIO详细教程汇总: -* Android AM命令行启动程序的方法: -* Android系统开篇: -* 对于android.intent.action.MAIN和android.intent.category.LAUNCHER的理解: -* lntentfilter的匹配规则: Android开发艺术探索 1.3 -* Android加密之文件级加密: -* Android属性allowBackup安全风险浅析: -* Android Bander设计与实现 - 设计篇: -* Android Application的使用及其生命周期: -* Android ActionBar完全解析,使用官方推荐的最佳导航栏(上): -* 详解 Android 通信: -* 什么是android.R.id.content? -* Android Partitions Explained: boot, system, recovery, data, cache & misc: -* Android for work总结(上): -* Android for work总结(下): -* android获取内置和外置SD卡路径: -* Android之Adapter用法总结: -* Android签名机制之---签名过程详解: -* android文件读写以及不同应用之间的文件读写操作: -* [Android] 为Android安装BusyBox —— 完整的bash shell: -* Android 使用android-support-multidex解决Dex超出方法数的限制问题,让你的应用不再爆棚: -* Android应用坐标系统全面详解: -* Android命令行/c语言/java设置获取系统属性的方法: -* Android中visibility属性VISIBLE、INVISIBLE、GONE的区别: -* 如何在Root的手机上开启ViewServer,使得HierachyViewer能够连接: -* Formatting and Plurals: Android UI Fundamentals[Android UI基础教程](Formatting and Plurals) -* Android vector标签 PathData 画图超详解: -* Implementation vs API dependency: -* Android软键盘遮挡的四种解决方案: -* repo 下载Android源码(国内镜像): -* android怎样调用@hide和internal API: -* android下的SuppressLint与TargetApi: -* Android完美获取状态栏高度、标题栏高度、编辑区域高度的获取: -* Android开发中dip,dpi,density,px等详解: -* 高斯模糊: -* 总结一下Android中主题(Theme)的正确玩法: -* Android中`@`和`?`区别以及`?attr/**`与`@style/**`等的区别: -* Android:去掉系统自带的Activity跳转动画,跳转无动画,返回无动画: -* Android 权限的一些细节: -* Android 将自己的应用改为系统应用: -* 用ColorFilter为安卓按钮增加效果: -* What is the difference between system apps and privileged apps on Android? -* 全面的Android文件目录解析和获取方法(包含对6.0系统的说明): -* Android 那些年,处理getActivity()为null的日子: -* Android 汉字转拼音的多种实现方式: -* MaterialDesign之SearchView解锁 仿网易云音乐搜索: -* Android下setTextSize的正确使用姿势: -* Android Notification常见样式总结: -* Mipmap drawables for icons: -* Android 修改阿拉伯语数字显示: -* 安卓应用在各大应用市场上架方法整理: -* Android 5.0 在优先模式下依然能收到微信的提醒是什么原理? -* 一大波 Android 刘海屏来袭,全网最全适配技巧! -* Android百分比布局: -* 安卓约束控件(ConstraintLayout)扁平化布局入门: -* Android之分屏模式解析(上): -* Android之分屏模式解析(下): -* Android中app进程ABI确定过程: -* Context, What Context? -* Getting the null pointer exception in the getActionBar method: -* INSTALL_FAILED_TEST_ONLY: -* setEnabled() vs setClickable(), what is the difference? -* Android兼容性测试CTS Verifier-环境搭建、测试执行、结果分析: -* Why do most fields (class members) in Android tutorial start with `m`? -* Android多语言国际化适配(兼容7.0): -* ListView中getView的原理与解决多轮重复调用的方法: -* Android控件--ProgressBar: -* Android逆向之旅---解析编译之后的AndroidManifest文件格式: -* .nomedia: 《解析 Google Android SDK-智能手机开发范例手册》5.3 -* android程序自动化生成apk的过程: -* Android DEX安全攻防战: -* Android Dex文件格式(一): -* 配置方法数超过 64K 的应用: - -* android存储访问框架Storage Access Framework: -* 存储访问框架android Storage Access Framework(SAF): -* Android 7.0 行为变更 通过FileProvider在应用间共享文件吧: -* FileProvider文件分享: - -* Fragments:Pro Android 5[精通Android 3] Chapter 8 -* Android Fragment的用法(一): -* Fragment相关: Pro Android 5 -* Fragment相关源码解析一——FragmentManagerImpl和BackStackRecord: -* Fragment相关源码解析二——生命周期: -* Fragment相关源码解析三——状态保存与恢复: -* 为什么要用Fragment.setArguments(Bundle bundle)来传递参数: -* IllegalStateException: Can not perform this action after onSaveInstanceState with ViewPager: - -* Android Studio系列教程一--下载与安装: -* Android Studio系列教程二--基本设置与运行: -* Android Studio系列教程三--快捷键: -* Android Studio系列教程四--Gradle基础: -* ANDROID STUDIO系列教程五--GRADLE命令详解与导入第三方包: -* ANDROID STUDIO系列教程六--GRADLE多渠道打包: - -* Android Studio优化技巧: -* Android Studio相关目录解析: - -* MIUI ROM适配之旅第一天——认识Android手机: -* H60-L01 EMUI3.0 ROOT 傻瓜教程: - -## Jack - -* Android 新一代编译 toolchain Jack & Jill 简介: -* Google 又弃坑了,Jack+Jill vs. javac+dx: - -## 升级相关 - -* Android A/B System OTA分析(一)概览: -* Android A/B System OTA分析(二)系统image的生成: -* Android A/B System OTA分析(三)主系统和bootloader的通信: -* Android A/B System OTA分析(四)系统的启动和升级: - -* Android OTA升级原理和流程分析(一)--update.zip包的制作: -* Android OTA升级原理和流程分析(二)---update.zip差分包问题的解决: -* Android OTA升级原理和流程分析(三)---Android系统的三种启动模式: -* Android OTA升级原理和流程分析(四)---Android系统Recovery模式的工作原理: -* Android OTA升级原理和流程分析(五)---update.zip包从上层进入Recovery服务: -* Android OTA升级原理和流程分析(六)---Recovery服务流程细节: -* Android OTA升级原理和流程分析(七)---Recovery服务的核心install_package函数: -* Android OTA升级原理和流程分析(八)---升级程序update_binary的执行过程: -* Android OTA升级原理和流程分析(九)---updater-script脚本语法简介以及执行流程: - -## ADB - -* awesome-adb: -* ADB: -* 解决adb push时出现的"Read-only file system"问题: -* android adb push 与 adb install的比较(两种安装APK的方法): - -## Eclipse - -* Android Development Tools for Eclipse: -* eclipse搭建android开发环境: -* Windows7部署Android开发环境傻瓜式教程(Eclipse+ADT): -* AndroidManifest.xml file missing! - -* Android程序开发初级教程(一): -* Android程序开发初级教程(二): -* Android程序开发初级教程(三): - -## Gradle And Android Plugin - -* -* Gradle User Guide: -* Chapter 6. The Gradle Wrapper: -* Getting Started With Gradle: - -* 彻底搞懂Gradle、Gradle Wrapper与Android Plugin for Gradle的区别和联系: - -* Gradle学习系列之一——Gradle快速入门: -* gradle使用文档: -* Gradle修改本地仓库的位置: -* gradle cache目录(.gradle)剖析: - -* (老)Gradle Plugin User Guide: -* (老)Gradle Plugin User Guide 翻译: -* 加速Android Studio/Gradle构建: -* 用Gradle 构建你的android程序: -* 用Gradle 构建你的android程序-依赖管理篇: - -## NDK - -* - -## Bug跟踪 - -* 网易云捕: -* bugly: -* BugTags: -* 蒲公英: - -## XMPP - -* - -* rfc3920: -* rfc3920翻译: - -* - -* smack: -* smack source: -* asmack: -* smack4-doc: -* smack4-doc-zh: - -* 环信即时通讯云: -* Android之基于XMPP协议即时通讯软件(一): -* Android之基于XMPP协议即时通讯软件(二): -* Android之基于XMPP协议即时通讯软件(三): - -* 基于xmpp openfire smack开发之openfire介绍和部署[1]: -* 基于xmpp openfire smack开发之smack类库介绍和使用[2]: -* 基于xmpp openfire smack开发之Android客户端开发[3]: -* 基于xmpp openfire smack开发之Android消息推送技术原理分析和实践[4]: - -* 基于XMPP协议的Android聊天IM客户端: - -* Openfire 的安装和配置: -* Jabber 协议 概述: -* Smack编写jabber客户端: -* Subscriptions 运行机制: -* XMPP协议分析-原理篇: - -## Player - -* ApolloMod: -* android-visualizer: - -## Vitamio - -* Vitamio: -* 帮助文档: -* 4.2.2版本之前的源码: -* 5.0.0版本之后的源码: -* vplayer: -* 一下科技: -* 使用Vitamio打造自己的Android万能播放器: - -## 百度媒体云 - -* - -## Gerrit - -* -* -* -* 如何搭建开源code review gerrit服务器: \ No newline at end of file diff --git "a/Basic\345\255\246\344\271\240\344\271\213\350\267\257.md" "b/Basic\345\255\246\344\271\240\344\271\213\350\267\257.md" deleted file mode 100644 index c4b5ca3..0000000 --- "a/Basic\345\255\246\344\271\240\344\271\213\350\267\257.md" +++ /dev/null @@ -1,37 +0,0 @@ -# Basic语言学习之路 - -* BASIC: - -## FreeBASIC - -* freeBASIC: -* FreeBASIC: -* FBIde - editor for FreeBASIC: -* FbEdit FreeBASIC code editor: -* FBWiki: -* freebasic.vim: -* Freebasic-vim-syntax-file: -* fbc.vim: -* IrrlichtWrapper Library: -* Free BASIC 试用: - -## QuickBASIC QBasic - -* QuickBASIC: -* QBasic: -* QBasic讨论区: -* Features of QuickBasic: -* 文章分类 - Quick Basic: -* Quick Basic 常用的语句: -* All about QBasic and QuickBasic: - -* - -## Other - -* BASIC-256: -* PowerBASIC: - -* Developing Automated Tests Using NUnit and VB.NET: -* Differences between VB.NET and VB: -* Comparison of Visual Basic and Visual Basic .NET: diff --git "a/CSharp\345\255\246\344\271\240\344\271\213\350\267\257.md" "b/CSharp\345\255\246\344\271\240\344\271\213\350\267\257.md" deleted file mode 100644 index cf73da9..0000000 --- "a/CSharp\345\255\246\344\271\240\344\271\213\350\267\257.md" +++ /dev/null @@ -1,6 +0,0 @@ -# C#学习之路 - -* Mono: -* MonoDevelop: -* SharpDevelop: -* Emonic: diff --git "a/C\344\270\216C++\345\255\246\344\271\240\344\271\213\350\267\257.md" "b/C\344\270\216C++\345\255\246\344\271\240\344\271\213\350\267\257.md" deleted file mode 100644 index 857f24c..0000000 --- "a/C\344\270\216C++\345\255\246\344\271\240\344\271\213\350\267\257.md" +++ /dev/null @@ -1,274 +0,0 @@ -# C_CPP学习之路 [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/sindresorhus/awesome) - -## Reference - -* -* -* -* C standard library: -* C++ Standard Library: - -* comp.lang.c Frequently Asked Questions: -* C 语言常见问题集: -* The C++ Standard Library - A Tutorial and Reference: - -## Tools - -* cdecl: -* Visual Leak Detector for Visual C++ : -* Valgrind: -* PC-lint for C/C++: -* cppcheck: -* C and C++ Code Counter(CCCC): -* Dev-C++: - -## Libraries - -* boost: -* Ncurses: -* GMP: -* Crypto++: -* OGLplus: -* POSIX Threads for Win32: -* Borland Graphics Interface (BGI) for Windows: -* libcstl: -* SWIG: -* EasyX Library for C++: -* EGE(Easy Graphics Engine): -* CLucene - a C++ search engine: -* Translate STL 2 C Language: -* Log library for C++: -* log4cplus: -* Borland-style CONIO: -* DISLIN: -* ICU - International Components for Unicode: -* libevent: -* List of numerical libraries: -* Standard Template Library Programmer's Guide: -* Cairo: - -## Windows C++ - -* CRT Library Features: -* Security Features in the CRT: -* Linker Tools Warning LNK4098: -* LINK : fatal error LNK1104: 无法打开文件“LIBCD.lib”: -* 链接器工具错误 LNK2026: -* DUMPBIN Reference: -* atexit和onexit的主要用法和区别: -* _onexit, _onexit_m: -* 为 Visual C++ 项目创建的文件类型: -* VC知识库: -* VC 常用数据类型总结 俩篇: -* 预编译头文件介绍和说明: -* 预编译头文件解析: -* VC 预编译头文件的使用: -* VC++的Unicode编程: -* VC++,掀起你的盖头来——谈VC++对象模型: -* Visual C++ 入门精解: -* visual studio 2008中头文件和库文件路径设置: -* Useful enhancements for Visual Studio .NET: -* Windows动态库与Linux共享对象比较: -* ODBC中的FX/Bulk RFX数据交换机制分析: -* VC++6.0编译时总死机,安装vcsp6补丁: -* DEBUG和RELEASE 版本差异及调试相关问题: -* Debug和Release有什么区别: -* MSVC vs. MinGW 之 (lib,dll,def,obj,exe) vs (a,dll,def,o,exe): - -## Linux C++ - -* GNU C Library: -* C POSIX library: -* POSIX.1-2017: - -* The LLVM Compiler Infrastructure: -* clang: a C language family frontend for LLVM: - -* Linux内核中无名管道pipe和有名管道fifo的分析: -* 应用 Valgrind 发现 Linux 程序的内存问题: -* 如何在linux下检测内存泄漏: -* Linux下的时间概念(主要是其中的计时器的使用): -* Linux 桌面应用技术专题: -* Linux系统调用列表: -* GNU GCC手册-1: -* gcc的基本用法: -* Linux 套接字编程中的 5 个隐患: -* gcc和g++的区别: -* 使用 GLib 工具集管理 C 数据帖子发表于: -* Linux静态/动态链接库的创建和使用: -* 基于X的GNOME、GTK、GDK、XLib、GLib等之间的关系: -* Comparison of Diagnostics between GCC and Clang: - -## Windows/Linux简不单理还乱 - -* MinGW: -* MinGW-w64: -* TDM-GCC: -* cygwin: -* 对话 UNIX: 在 Windows 上使用 Cygwin: -* GTK+与MFC不完全对比: -* 将 MFC 应用程序移植到 Linux: - -## Eclipse CDT - -* -* eclipse 配置 TDM-GCC 64位版方法: -* eclipse写C++控制台程序,不见输出: - -## CMake - -* - -## 包管理器 - -* Does C++ need a universal package manager? -* vcpkg(C++ Library Manager for Windows, Linux, and MacOS): -* Conan(the C / C++ Package Manager for Developers): -* Buckaroo: -* cget: -* Conda: -* CPM: -* CPPAN: -* Hunter: - -## Glib/GTK+/Gnome - -* GLib Reference Manual: -* Glib Test Framework: -* GLIB 常用数据结构介绍: -* glib库简介: -* glib库异步队列和线程池代码分析: - -* GTK+: -* Part II. GTK+ Widgets and Objects: -* Migrating from GTK+ 2.x to GTK+ 3: -* GTK+ 2.0 Tutorial: -* GTK+ 2.0 Tutorial(中文版): -* GTK-Doc: -* 在Windows下使用GTK+开发GUI应用程序: -* Ubuntu下GTK的安装、编译和测试: -* 《GTK+》编程基础: -* 在gtk+程序中显示中文说明: -* Gtk对于通常的gui程序,大家想做的事就是做一点事件处理(包括各种计算、文件操作等),然后在界面上显示出来: -* GTK+2.0 中的容器控件与布局技巧: -* GTK编程: -* GTK+ 2.0 教程--信号和回调函数的原理: -* ubuntu 14.04 中找不到libgtk-x11-2.0.so: -* GTK v1.2 Tutorial: - -* GNOME 开发者中心: -* Gnome下载地址: -* Port your application from GNOME 2 to GNOME 3: -* Vala - Compiler for the GObject type system: - -* Anjuta(难用): -* Glade(难用): -* Glade User Interface Designer Reference Manual: -* 用Glade和libGlade设计Gtk+图形界面: - -### GTK中的delete_event和destroy - -* delete_event 事件一般由用户或者说用户通过窗口管理器产生,即点击窗口右上角的退出按钮。假如不做任何特殊处理,窗口管理器会自动产生destroy信号;如果我们自 定义了处理delete_event事件的回调函数,是否产生destroy信号就和函数的返回值有关,如果是FALSE就产生,反之则没有效果。 -* destroy,除了可以由delete_event事件产生之外,还可以通过gtk_widget_destroy函数与其它信号发生交换。同样,如果不加指定,默认结果是关闭所指向的窗口但并不结束进程。如果我们希望主窗口和进程一起关闭,必须使用gtk_main_quit()。 - -## QT - -* -* Qter开源社区: -* qtcn: - -## wxWidgets - -* - -## Doxygen - -* -* 学习用 doxygen 生成源码文档: -* 使用doxygen为C/C++程序生成中文文档(上): -* Doxygen + Graphviz + Htmlhelp, 成为文档好手: - -## Unit Test - -* Google Test: -* 玩转Google开源C++单元测试框架Google Test系列(gtest)(总): -* Code Blocks+gtest环境配置: -* C++ unit test start guide, how to set up Google Test (gtest) in Eclipse?: - -* CppUnit(C++ port of JUnit): -* 使用CppUnit(Windows): -* cppunit helloworld详尽篇(Linux): -* 开放源码 C/C++ 单元测试工具,第 3 部分: 了解 CppTest: -* CppUnit源码解读: - -* C Unit Testing Framework: - -* Parasoft C/C++test: - -## XML - -* TinyXML: -* Libxml2: - -## INI - -* GetPrivateProfileString/WritePrivateProfileString: - -## JSON - -* jsoncpp: - -## 其它配置格式 - -* libconfig: - -## Ctags - -* - -## CLIPS - - CLIPS is a productive development and delivery expert system tool which provides a complete environment for the construction of rule and/or object based expert systems. - -* - -## Xapian - - Xapian is an Open Source Search Engine Library, released under the GPL v2+. It's written in C++, with bindings to allow use from Perl, Python, PHP, Java, Tcl, C#, Ruby, Lua, Erlang and Node.js (so far!) - -* - -## Others - -* The International Obfuscated C Code Contest: - -* Comparing Two High-Performance I/O Design Patterns: -* 使您的软件运行起来——防止缓冲区溢出: -* 屏幕输出VS文件输出: -* C/C++内存泄漏及检测: -* 浅谈C/C++内存泄露及其检测工具: -* 内存泄露检测工具比较: -* 亲密接触C可变参数函数 : -* TCP连接中的TIME_WAIT状态: -* see also: 《TCP-IP详解卷1:协议》第十八章 -* TCP可靠传输及流量控制系列六:TCP连接中的TIME_WAIT状态: -* C++项目中的extern "C" {}: -* 由函数clock想到的: -* 理解 pkg-config 工具: -* C/C++中的abort、atexit、exit和_Exit: -* setjmp()/longjmp()的使用方法和场合: -* C++ 工程实践(7):iostream 的用途与局限: -* 指针的大小: -* C/C++指针原理: -* C++ STL轻松导学: -* undefined reference问题总结: -* Enabling string conversion functions in MinGW: -* C++ Rocks!: -* 减少C++代码编译时间的方法: -* C++编译错误cannot have cv-qualifier: -* 在 console mode 中使用 C/C++ 编译器: -* 基于对象和面向对象的区别: -* const 不再迷茫: -* C语言中随机数相关问题: -* C/C++大数库简介: -* C++资源之不完全导引: C++资源之不完全导引.docx \ No newline at end of file diff --git "a/EditorAndIDE\345\255\246\344\271\240\344\271\213\350\267\257.md" "b/EditorAndIDE\345\255\246\344\271\240\344\271\213\350\267\257.md" deleted file mode 100644 index c8b6f3b..0000000 --- "a/EditorAndIDE\345\255\246\344\271\240\344\271\213\350\267\257.md" +++ /dev/null @@ -1,79 +0,0 @@ -# Editor And IDE - -## VSCode - -* Visual Studio Code: -* Settings Sync: - -## VIM - -* -* -* Vim documentation: usr_41: -* map.txt: -* Vim FAQ: -* Vim of AlloVince: -* 奉献一个vim自动加载模板的插件: -* vim安装YouCompleteMe 插件: -* 使用脚本编写 Vim 编辑器,第 1 部分: 变量、值和表达式: -* 把VIM打造成一个真正的IDE(1): 把vim打造成一个真正的ide1.html -* Vim 中使用 OmniComplete 为 C/C++ 自动补全: -* VIM下利用ctags自动补全C/C++标准库和操作系统调用(windows+MinGW版): -* Vim使用笔记: -* vim配置(自动补全,自动生成tag,一些使用插件taglist,nerdtree): -* Vim 的补全模式加速器,轻松玩转全部 15 种自动补全模式: -* Learn essential Vim skills: -* Learn Vimscript the Hard Way: -* Vim 复制粘贴探秘: -* VIMAdventures: -* Vimium - The Hacker's Browser: -* Vimperator: -* VIM常见用法总结: - -## Emacs - -* emacswiki: -* GNU Emacs Manuals Online: - -## ultraedit - -* -* User-submitted wordfiles for UltraEdit/UEStudio: - -## Sublime Text - -* -* -* - -## sourceinsight - -* - -## CodeBlocks - -* -* cbp2make: -* cbMakefileGen plugin: -* CodeBlocks插件开发指南(一): -* 用UTF-8编写的代码在codeblocks中编译后汉字不能正常显示: - -### CodeBlocks自定义自动补全 - -1、依次打开 Project -> Properties -> C/C++ parser options 来到 Additional search paths; -2、点 Add 选择头文件的路径后点确定; -3、在源文件中添加相应的头文件后即可实现自动补全。 - -## Others - -* geany: -* Lime Text: -* Scintilla: -* xmlspy: -* Bluefish: -* Understand: -* codelite: -* kite: -* kdevelop: -* xcode: -* QtCreator: diff --git "a/Erlang\345\255\246\344\271\240\344\271\213\350\267\257.md" "b/Erlang\345\255\246\344\271\240\344\271\213\350\267\257.md" deleted file mode 100644 index 92b1a8b..0000000 --- "a/Erlang\345\255\246\344\271\240\344\271\213\350\267\257.md" +++ /dev/null @@ -1,4 +0,0 @@ -# Erlang学习之路 - -* -* diff --git "a/Fortran\345\255\246\344\271\240\344\271\213\350\267\257.md" "b/Fortran\345\255\246\344\271\240\344\271\213\350\267\257.md" deleted file mode 100644 index 9ca55f0..0000000 --- "a/Fortran\345\255\246\344\271\240\344\271\213\350\267\257.md" +++ /dev/null @@ -1,13 +0,0 @@ -# Fortran学习之路 - -* -* Welcome to the home of GNU Fortran: -* Fortran中文网: -* Fortran77和90/95编程入门: -* Approximatrix: -* Fortran Coder: - -## Photran - -* 项目地址: -* 安装说明: diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..5c41b36 --- /dev/null +++ b/Gemfile @@ -0,0 +1,17 @@ +source "https://rubygems.org" + +gem "github-pages", group: :jekyll_plugins + +gem "tzinfo-data" +gem "wdm", "~> 0.1.0" if Gem.win_platform? + +# If you have any plugins, put them here! +group :jekyll_plugins do + gem "jekyll-paginate" + gem "jekyll-sitemap" + gem "jekyll-gist" + gem "jekyll-feed" + gem "jemoji" + gem "jekyll-include-cache" + gem "jekyll-algolia" +end diff --git "a/Groovy\345\255\246\344\271\240\344\271\213\350\267\257.md" "b/Groovy\345\255\246\344\271\240\344\271\213\350\267\257.md" deleted file mode 100644 index 0a9d573..0000000 --- "a/Groovy\345\255\246\344\271\240\344\271\213\350\267\257.md" +++ /dev/null @@ -1,8 +0,0 @@ -# Groovy学习之路 - -* -* source: -* Groovy with Eclipse - Tutorial: -* groovy-eclipse: -* 实战 Groovy: 在 Java 应用程序中加一些 Groovy 进来: -* Grape: diff --git "a/Haskell\345\255\246\344\271\240\344\271\213\350\267\257.md" "b/Haskell\345\255\246\344\271\240\344\271\213\350\267\257.md" deleted file mode 100644 index 85968a0..0000000 --- "a/Haskell\345\255\246\344\271\240\344\271\213\350\267\257.md" +++ /dev/null @@ -1,3 +0,0 @@ -# Haskell学习之路 - -* diff --git "a/IO\345\255\246\344\271\240\344\271\213\350\267\257.md" "b/IO\345\255\246\344\271\240\344\271\213\350\267\257.md" deleted file mode 100644 index 09a17f0..0000000 --- "a/IO\345\255\246\344\271\240\344\271\213\350\267\257.md" +++ /dev/null @@ -1,5 +0,0 @@ -# IO学习之路 - - - -* diff --git "a/Lua\345\255\246\344\271\240\344\271\213\350\267\257.md" "b/Lua\345\255\246\344\271\240\344\271\213\350\267\257.md" deleted file mode 100644 index 6a6840d..0000000 --- "a/Lua\345\255\246\344\271\240\344\271\213\350\267\257.md" +++ /dev/null @@ -1,3 +0,0 @@ -# Lua学习之路 - -* diff --git "a/Nginx\345\255\246\344\271\240\344\271\213\350\267\257.md" "b/Nginx\345\255\246\344\271\240\344\271\213\350\267\257.md" deleted file mode 100644 index aa28a54..0000000 --- "a/Nginx\345\255\246\344\271\240\344\271\213\350\267\257.md" +++ /dev/null @@ -1,4 +0,0 @@ -# Nginx学习之路 - -* -* Pre-Built Packages for Stable version: diff --git "a/OJ\345\255\246\344\271\240\344\271\213\350\267\257.md" "b/OJ\345\255\246\344\271\240\344\271\213\350\267\257.md" deleted file mode 100644 index 04440ef..0000000 --- "a/OJ\345\255\246\344\271\240\344\271\213\350\267\257.md" +++ /dev/null @@ -1,22 +0,0 @@ -# OJ学习之路 - -* ACM竞赛之输入输出: -* awesome-algorithm: - -* codevs: -* 九度OJ: -* hihocoder: -* SPOJ (Sphere Online Judge): -* OpenJudge: -* 猿圈: -* codechef: -* topcoder: -* LeetCode Online Judge: -* LeetCode中国: -* PKU JudgeOnline: -* -* HDU Online Judge System: -* ACdream Online Judge: -* UVa Online Judge: -* codeforces: -* 洛谷: diff --git "a/PHP\345\255\246\344\271\240\344\271\213\350\267\257.md" "b/PHP\345\255\246\344\271\240\344\271\213\350\267\257.md" deleted file mode 100644 index 214df75..0000000 --- "a/PHP\345\255\246\344\271\240\344\271\213\350\267\257.md" +++ /dev/null @@ -1,13 +0,0 @@ -# PHP学习之路 - -* - -* phpStudy: -* WAMPSERVER: -* LNMP一键安装包: -* AppServ : Apache + PHP + MySQL: -* XAMPP Apache + MariaDB + PHP + Perl: - -## PHPUnit - -* diff --git "a/Pascal\344\270\216Delphi\345\255\246\344\271\240\344\271\213\350\267\257.md" "b/Pascal\344\270\216Delphi\345\255\246\344\271\240\344\271\213\350\267\257.md" deleted file mode 100644 index b4b9760..0000000 --- "a/Pascal\344\270\216Delphi\345\255\246\344\271\240\344\271\213\350\267\257.md" +++ /dev/null @@ -1,6 +0,0 @@ -# Pascal与Delphi学习之路 - -* Pascal (programming language): (programming_language) -* Delphi (programming language): (programming_language) -* free pascal: -* Lazarus: diff --git "a/Prolog\345\255\246\344\271\240\344\271\213\350\267\257.md" "b/Prolog\345\255\246\344\271\240\344\271\213\350\267\257.md" deleted file mode 100644 index 75f7e7d..0000000 --- "a/Prolog\345\255\246\344\271\240\344\271\213\350\267\257.md" +++ /dev/null @@ -1,5 +0,0 @@ -# Prolog学习之路 - -* Visual Prolog: -* SWI-Prolog: -* Amzi! Prolog + Logic Server™: diff --git "a/Python\345\255\246\344\271\240\344\271\213\350\267\257.md" "b/Python\345\255\246\344\271\240\344\271\213\350\267\257.md" deleted file mode 100644 index 9e024d3..0000000 --- "a/Python\345\255\246\344\271\240\344\271\213\350\267\257.md" +++ /dev/null @@ -1,51 +0,0 @@ -# Python学习之路 - -* -* -* PyPI - the Python Package Index: -* pip: -* setuptools: -* setuptools: -* spyder: -* Python Enhancement Proposals: - -* ActivePython: - -* IronPython: -* Jython: - -* The Eric Python IDE: -* Wing IDE: -* Python for Windows Extensions: -* Stani's Python Editor(SPE): -* Ulipad: - -* Python Tools for Visual Studio: -* Virtualenv: -* Pillow: -* pythonchallenge: -* Unofficial Windows Binaries for Python Extension Packages: -* SCons: A software construction tool: - -* PyUnit testing framework: -* PythonToolkit (PTK): - -* -* 啄木鸟Python社区: - -* 初探验证码识别: -* Python 程序员必知必会的开发者工具: -* Python自动单元测试框架: -* Python 3.0 抢“鲜”体验: -* Dive Into Python 3: -* Swaroop C H: -* JPype: - -* Python zipfile报错问题: - -## PyDev - -* 项目地址: -* 安装说明: -* 与之相关的LiClipse项目地址: -* PyDev Manual: \ No newline at end of file diff --git a/README.md b/README.md index c0483ce..cfe936b 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,18 @@ # cnblogs -[![Issue Count](https://codeclimate.com/github/jiangxincode/cnblogs/badges/issue_count.svg)](https://codeclimate.com/github/jiangxincode/cnblogs) [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/sindresorhus/awesome) -[![Maintenance](https://img.shields.io/maintenance/yes/2019.svg)](https://github.com/jiangxincode/cnblogs) +[![Maintenance](https://img.shields.io/maintenance/yes/2025.svg)](https://github.com/jiangxincode/cnblogs) [![GitHub license](https://img.shields.io/github/license/mashape/apistatus.svg)](http://mit-license.org/) 一些我在平时工作和学习中积累的编程相关的资源。每个程序员都有自己的学习之路,过程中会学习各种各样的网上知识,学习完最好记录下来,每隔一段时间看一看,会有不一样的收获。 +博客地址: + +* [原有"学习之路系列博客"](https://jiangxincode.github.io/cnblogs/categories/#the-way-of-learning) +* [Github](https://jiangxincode.github.io/cnblogs/) +* [博客园](https://www.cnblogs.com/jiangxinnju) + 如果大家有兴趣可以一起增加,修改。 + +* [文章源码目录](https://github.com/jiangxincode/cnblogs/tree/master/_posts) +* [文章依赖图床](https://github.com/jiangxincode/PicGo) diff --git "a/Rust\345\255\246\344\271\240\344\271\213\350\267\257.md" "b/Rust\345\255\246\344\271\240\344\271\213\350\267\257.md" deleted file mode 100644 index b20504e..0000000 --- "a/Rust\345\255\246\344\271\240\344\271\213\350\267\257.md" +++ /dev/null @@ -1,4 +0,0 @@ -# Rust学习之路 - -* -* Rust Documentation: diff --git "a/R\345\255\246\344\271\240\344\271\213\350\267\257.md" "b/R\345\255\246\344\271\240\344\271\213\350\267\257.md" deleted file mode 100644 index e2d60f9..0000000 --- "a/R\345\255\246\344\271\240\344\271\213\350\267\257.md" +++ /dev/null @@ -1,8 +0,0 @@ -# R学习之路 - -* -* The Comprehensive R Archive Network: - -## rstudio - -* diff --git "a/TCL\344\270\216TK\345\255\246\344\271\240\344\271\213\350\267\257.md" "b/TCL\344\270\216TK\345\255\246\344\271\240\344\271\213\350\267\257.md" deleted file mode 100644 index 03bced4..0000000 --- "a/TCL\344\270\216TK\345\255\246\344\271\240\344\271\213\350\267\257.md" +++ /dev/null @@ -1,11 +0,0 @@ -# TCL与TK学习之路 - -* Tloona Tcl/Tk IDE: -* tclsqueak: -* CrowTDE: -* MyTcl: -* ACTIVETCL: - -* Configuring Eclipse as TCL/TK IDE: - -* Tcler's Wiki: diff --git "a/TeX\345\255\246\344\271\240\344\271\213\350\267\257.md" "b/TeX\345\255\246\344\271\240\344\271\213\350\267\257.md" deleted file mode 100644 index 2207b57..0000000 --- "a/TeX\345\255\246\344\271\240\344\271\213\350\267\257.md" +++ /dev/null @@ -1,32 +0,0 @@ -# TeX学习之路 - -* TeX Live: -* winedt: - -* Word-to-LaTeX Converter: -* TeXpen - Your LaTeX editor: -* The easy to use, online, collaborative LaTeX editor: -* Texmaker - The universal LaTeX editor: -* TeXlipse: -* LEd: -* SciTE LaTeX IDE: -* TeXstudio: -* TeXmacs: -* jaxedit: -* TeXworks: -* BaKoMa TeX Word: -* Kile: -* Gummi: -* LyX: - -* The Comprehensive TEX Archive Network(CTAN): -* ChinaTeX: -* LaTeX – A document preparation system: -* ChinaTeX 论坛: -* Chinese TeX(CTEX): -* LaTeX Stack Exchange: -* LaTeX工作室: - -* LaTeX内容总结: -* LaTeX技巧68:TeX/LaTeX 常用宏包简介: -* Linux下Texlive的ctex包中文字体问题: diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..2812dfb --- /dev/null +++ b/_config.yml @@ -0,0 +1,129 @@ +# Welcome to Jekyll! +# +# This config file is meant for settings that affect your whole blog, values +# which you are expected to set up once and rarely edit after that. If you find +# yourself editing this file very often, consider using Jekyll's data files +# feature for the data you need to update frequently. +# +# For technical reasons, this file is *NOT* reloaded automatically when you use +# 'bundle exec jekyll serve'. If you change this file, please restart the server process. + +# Site settings +# These are used to personalize your new site. If you look in the HTML files, +# you will see them accessed via {{ site.title }}, {{ site.email }}, and so on. +# You can create any custom variable you would like, and they will be accessible +# in the templates via {{ site.myvariable }}. +title: Aloys's Blogs +email: +description: >- # this means to ignore newlines until "baseurl:" + Write an awesome description for your new site here. You can edit this + line in _config.yml. It will appear in your document head meta (for + Google search results) and in your feed.xml site description. +twitter_username: username +baseurl: "/cnblogs" +url: "https://jiangxincode.github.io" +github_username: jiangxincode +#minimal_mistakes_skin: default +minimal_mistakes_skin: sunrise +search: true + +comments: + provider: "disqus" + disqus: + shortname: "aloys-1" + +# Build settings +markdown: kramdown +remote_theme: mmistakes/minimal-mistakes +# Outputting +permalink: /:categories/:title/ +paginate: 5 # amount of posts to show +paginate_path: /page:num/ +timezone: # https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + +include: + - _pages + +# Exclude from processing. +# The following items will not be processed, by default. Create a custom list +# to override the default setting. +# exclude: +# - Gemfile +# - Gemfile.lock +# - node_modules +# - vendor/bundle/ +# - vendor/cache/ +# - vendor/gems/ +# - vendor/ruby/ + +# Plugins (previously gems:) +plugins: + - jekyll-paginate + - jekyll-sitemap + - jekyll-gist + - jekyll-feed + - jemoji + - jekyll-include-cache + +author: + name : "Aloys" + avatar : "/assets/images/bio-photo.jpg" + bio : "心若冰清,天塌不惊" + links: + - label: "GitHub" + icon: "fab fa-fw fa-github" + url: "https://github.com/jiangxincode" + - label: "知乎" + icon: "fas fa-fw fa-link" + url: "https://www.zhihu.com/people/jiangxinnju" + - label: "博客园" + icon: "fas fa-fw fa-link" + url: "https://www.cnblogs.com/jiangxinnju" + +footer: + links: + - label: "GitHub" + icon: "fab fa-fw fa-github" + url: "https://github.com/jiangxincode" + - label: "知乎" + icon: "fas fa-fw fa-link" + url: "https://www.zhihu.com/people/jiangxinnju" + - label: "博客园" + icon: "fas fa-fw fa-link" + url: "https://www.cnblogs.com/jiangxinnju" + +defaults: + # _posts + - scope: + path: "" + type: posts + values: + layout: single + author_profile: true + read_time: true + comments: true + share: true + related: true + # _pages + - scope: + path: "_pages" + type: pages + values: + layout: single + author_profile: true + +category_archive: + type: liquid + path: /categories/ +tag_archive: + type: liquid + path: /tags/ + +google_site_verification: "4LAS-hCkhl0WUaro31t6GDjLZGw-MY2U9watBogn7FQ" +bing_site_verification: "1F0D00B26293ADD828A9F5A0BBF6A630" + +analytics: + provider: "google-gtag" + google: + tracking_id: "G-VJG4XMXTJV" + anonymize_ip: false # default diff --git a/_data/navigation.yml b/_data/navigation.yml new file mode 100644 index 0000000..e3af8ae --- /dev/null +++ b/_data/navigation.yml @@ -0,0 +1,9 @@ +main: + - title: "Posts" + url: /posts/ + - title: "Categories" + url: /categories/ + - title: "Tags" + url: /tags/ + - title: "About" + url: /about/ \ No newline at end of file diff --git "a/_drafts/Android/202301072210Doze\346\250\241\345\274\217\346\272\220\347\240\201\350\247\243\346\236\220.md" "b/_drafts/Android/202301072210Doze\346\250\241\345\274\217\346\272\220\347\240\201\350\247\243\346\236\220.md" new file mode 100644 index 0000000..ab6f925 --- /dev/null +++ "b/_drafts/Android/202301072210Doze\346\250\241\345\274\217\346\272\220\347\240\201\350\247\243\346\236\220.md" @@ -0,0 +1,3 @@ + +请先阅读谷歌官网对Doze的基础性介绍: 。本文代码分析基于Android R版本。 + diff --git "a/_drafts/Android/202301081324Android\346\272\220\347\240\201\350\247\243\350\257\273-\350\247\246\346\221\270\344\275\215\347\275\256\346\230\276\347\244\272.md" "b/_drafts/Android/202301081324Android\346\272\220\347\240\201\350\247\243\350\257\273-\350\247\246\346\221\270\344\275\215\347\275\256\346\230\276\347\244\272.md" new file mode 100644 index 0000000..149c078 --- /dev/null +++ "b/_drafts/Android/202301081324Android\346\272\220\347\240\201\350\247\243\350\257\273-\350\247\246\346\221\270\344\275\215\347\275\256\346\230\276\347\244\272.md" @@ -0,0 +1,645 @@ +# Android源码解读-触摸位置显示 + +`本文基于Android T版本源码,梳理当用户在开发者选项中开启Show tabs功能后显示第点按操作的视觉反馈的原理,来进一步了解Android Input系统` + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/2023010813241.png) + +## Settings 写入设置 + +首先是设置应用(`Settings`)提供的开发者选项画面响应点击,将`Show taps`选项对应的设置`Key SHOW_TOUCHES`的 ON 值通过`android.provder.Settings`接口写入到保存系统设置数据的`SettingsProvier`中。 + +```java +// packages/apps/Settings/src/com/android/settings/development/ShowTapsPreferenceController.java +public class ShowTapsPreferenceController extends DeveloperOptionsPreferenceController ... { + ... + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + final boolean isEnabled = (Boolean) newValue; + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SHOW_TOUCHES, isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF); + return true; + } + ... +} +``` + +## InputManagerService监听设置 + +负责管理输入的系统服务`InputManagerService`在启动之际,会监听设置中的 `SHOW_TOUCHES`字段的变化,在设置产生变化的时候调用native侧的代码进行处理。 + +```java +// frameworks/base/services/core/java/com/android/server/input/InputManagerService.java +public class InputManagerService extends IInputManager.Stub... { + ... + public void start() { + ... + registerShowTouchesSettingObserver(); + ... + } + + private void registerShowTouchesSettingObserver() { + mContext.getContentResolver().registerContentObserver( + Settings.System.getUriFor(Settings.System.SHOW_TOUCHES), true, + new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange) { + updateShowTouchesFromSettings(); + } + }, UserHandle.USER_ALL); + } + + private void updateShowTouchesFromSettings() { + int setting = getShowTouchesSetting(0); + mNative.setShowTouches(setting != 0); + } + ... + +// frameworks/base/services/core/java/com/android/server/input/NativeInputManagerService.java +public interface NativeInputManagerService { + ... + void setShowTouches(boolean enabled); + ... +} +``` + +```cpp +// frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp +class NativeInputManager : public virtual RefBase, ...{ + ... + void setShowTouches(bool enabled); + ... +} + +void NativeInputManager::setShowTouches(bool enabled) { + { // acquire lock + AutoMutex _l(mLock); + + if (mLocked.showTouches == enabled) { + return; + } + + ALOGI("Setting show touches feature to %s.", enabled ? "enabled" : "disabled"); + mLocked.showTouches = enabled; + } // release lock + + mInputManager->getReader().requestRefreshConfiguration( + InputReaderConfiguration::CHANGE_SHOW_TOUCHES); +} +``` + +这里的`mInputManager`是`InputManagerInterface`对象实例,`InputManager`是`InputManagerInterface`和子类,所以通过`mInputManager`可以连接`NativeInputManager`和`InputReader`。 + +这里向负责读取事件的`InputReader`发出更新配置的请求,配置变更的`Type`为 `CHANGE_SHOW_TOUCHES`。 + +## 通过 InputReader 请求刷新配置 + +`InputReader`接收到配置变化的`Type`之后,会根据记录待刷新配置的变量 `mConfigurationChangesToRefresh`判断当前是否已经在刷新过程中。 +如果尚未处于刷新中,则更新`mConfigurationChangesToRefresh`的值,并唤醒`EventHub`进行配置刷新。 + +```cpp +// frameworks/native/services/inputflinger/reader/InputReader.cpp +void InputReader::requestRefreshConfiguration(uint32_t changes) { + std::scoped_lock _l(mLock); + + if (changes) { + bool needWake = !mConfigurationChangesToRefresh; + mConfigurationChangesToRefresh |= changes; + + if (needWake) { + mEventHub->wake(); + } + } +} +``` + +## EventHub 唤醒 InputReader 线程 + +`InputManagerService`过来的刷新请求最终需要`InputReader`线程来处理。 +可是 InputReader 线程处在从`EventHub`中读取事件和没有事件时便调用`epoll_wait`进入等待状态的循环当中。 +所以为了让其立即处理配置变化,需要`EventHub`的手动唤醒。 + +```cpp +// frameworks/native/services/inputflinger/reader/EventHub.cpp +void EventHub::wake() { + ALOGV("wake() called"); + + ssize_t nWrite; + do { + nWrite = write(mWakeWritePipeFd, "W", 1); + } while (nWrite == -1 && errno == EINTR); + + if (nWrite != 1 && errno != EAGAIN) { + ALOGW("Could not write wake signal: %s", strerror(errno)); + } +} + +size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) { + ... + for (;;) { + ... + int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis); + ... + } + ... +} +``` + +## InputReader线程刷新配置 + +`EventHub`唤醒后处于等待状态的`getEvents`会结束,之后`InputReader`线程会进入下次循环即`loopOnce`。 +其首先将检查是否存在待刷新的配置变化changes,存在的话调用`refreshConfigurationLocked`让`InputDevice`去重新适配变化。 + +```cpp +// frameworks/native/services/inputflinger/reader/InputReader.cpp +void InputReader::loopOnce() { + ... + std::vector inputDevices; + { // acquire lock + ... + uint32_t changes = mConfigurationChangesToRefresh; + if (changes) { + mConfigurationChangesToRefresh = 0; + timeoutMillis = 0; + refreshConfigurationLocked(changes); + } else if (mNextTimeout != LLONG_MAX) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout); + } + } // release lock + + size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); + ... +} +``` + +需要留意,`refreshConfigurationLocked`在调用`InputDevice`进一步处理之前需要先获取配置的变化放入`mConfig`中。 + +```cpp +// frameworks/native/services/inputflinger/reader/InputReader.cpp +void InputReader::refreshConfigurationLocked(uint32_t changes) { + mPolicy->getReaderConfiguration(&mConfig); + ... + + if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) { + mEventHub->requestReopenDevices(); + } else { + for (auto& devicePair : mDevices) { + std::shared_ptr& device = devicePair.second; + device->configure(now, &mConfig, changes); + } + } + ... +} +``` + +### InputDevice配置变化 + +`InputDevice`的`configure`需要处理很多配置变化,比如键盘布局、麦克风等。对于`Show taps`的变化关注调用 `InputMapper`的`congfigure`即可。 + +```cpp +// frameworks/native/services/inputflinger/reader/InputDevice.cpp +void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + ... + if (!isIgnored()) { + ... + for_each_mapper([this, when, config, changes](InputMapper& mapper) { + mapper.configure(when, config, changes); + mSources |= mapper.getSources(); + }); + ... + } +} +``` + +### TouchInputMapper 进一步处理 + +众多输入事件的物理数据需要对应的`InputMapper`来转化为上层能识别的事件类型。比如识别键盘输入的 `KeyboardInputMapper`、识别震动的`VibratorInputMapper`等等。 + +现在的触摸屏都支持多点触控,所以是`MultiTouchInputMapper`来处理的。可`MultiTouchInputMapper`没有复写 configure(),而是沿用由父类`TouchInputMapper`的共通处理。 + +```cpp +// frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp +void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + ... + bool resetNeeded = false; + if (!changes || + (changes & + (InputReaderConfiguration::CHANGE_DISPLAY_INFO | + InputReaderConfiguration::CHANGE_POINTER_CAPTURE | + InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT | + InputReaderConfiguration::CHANGE_SHOW_TOUCHES | + InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) { + // Configure device sources, display dimensions, orientation and + // scaling factors. + configureInputDevice(when, &resetNeeded); + } + ... +} +``` + +`TouchInputMapper`会依据`changes`的类型进行对应处理,对于`SHOW_TOUCHES`的变化需要调用`configureInputDevice`进一步处理。 + +## 创建和初始化 PointerController + +`configureInputDevice`进行多个参数的测量和配置,其中和`Show taps`相关的是`PointerController`的创建,该类是 Mouse、Taps、Pointer location 等系统 Touch 显示的专用类。 + +```cpp +// frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp +void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) { + ... + // Create pointer controller if needed, and keep it around if Pointer Capture is enabled to + // preserve the cursor position. + if (mDeviceMode == DeviceMode::POINTER || + (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches) || + (mParameters.deviceType == Parameters::DeviceType::POINTER && + mConfig.pointerCaptureRequest.enable)) { + if (mPointerController == nullptr) { + mPointerController = getContext()->getPointerController(getDeviceId()); + } + if (mConfig.pointerCaptureRequest.enable) { + mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE); + } + } else { + mPointerController.reset(); + } + ... +} +``` + +这里调用`InputReaderContext#getPointerController`,`InputReader::ContextImpl`是`InputReaderContext`的子类,所以会回调到`InputReader`开启`PointerController`的创建和初始化。 + +```cpp +// frameworks/native/services/inputflinger/reader/InputReader.cpp +std::shared_ptr InputReader::ContextImpl::getPointerController( + int32_t deviceId) { + // lock is already held by the input loop + return mReader->getPointerControllerLocked(deviceId); +} + +std::shared_ptr InputReader::getPointerControllerLocked( + int32_t deviceId) { + std::shared_ptr controller = mPointerController.lock(); + if (controller == nullptr) { + controller = mPolicy->obtainPointerController(deviceId); + mPointerController = controller; + updatePointerDisplayLocked(); + } + return controller; +} +``` + +这里调用`InputReaderPolicyInterface#obtainPointerController`,而`NativeInputManager`是`InputReaderPolicyInterface`的子类。 + +```cpp +// frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp +std::shared_ptr NativeInputManager::obtainPointerController( + int32_t /* deviceId */) { + ... + std::shared_ptr controller = mLocked.pointerController.lock(); + if (controller == nullptr) { + ensureSpriteControllerLocked(); + + controller = PointerController::create(this, mLooper, mLocked.spriteController); + mLocked.pointerController = controller; + updateInactivityTimeoutLocked(); + } + + return controller; +} +``` + +PointerController 构建的同时需要构建持有的 MouseCursorController。 + +```cpp +// frameworks/base/libs/input/PointerController.cpp +std::shared_ptr PointerController::create( ... ) { + std::shared_ptr controller = std::shared_ptr( + new PointerController(policy, looper, spriteController)); + ... + return controller; +} + +PointerController::PointerController( ... ) + : mContext(policy, looper, spriteController, *this), mCursorController(mContext) { + std::scoped_lock lock(mLock); + mLocked.presentation = Presentation::SPOT; + ... +} +``` + +`obtainPointerController`执行完之后调用`updatePointerDisplayLocked`执行`PointerController`的初始化。 + +### 初始化 PointerController + +调用`PointerController`的`setDisplayViewport`传入显示用的`DisplayViewPort`。 + +```cpp +// frameworks/native/services/inputflinger/reader/InputReader.cpp +void InputReader::updatePointerDisplayLocked() { + ... + std::optional viewport = + mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId); + if (!viewport) { + ... + viewport = mConfig.getDisplayViewportById(ADISPLAY_ID_DEFAULT); + } + ... + controller->setDisplayViewport(*viewport); +} +``` + +`setDisplayViewport`需要持有的`MouseCursorController`进一步初始化。 + +```cpp +// frameworks/base/libs/input/PointerController.cpp +void PointerController::setDisplayViewport(const DisplayViewport& viewport) { + ... + mCursorController.setDisplayViewport(viewport, getAdditionalMouseResources); +} +``` + +`MouseCursorController`需要获取Display相关的参数,并执行两个重要步骤:`loadResourcesLocked`/`updatePointerLocked` + +```cpp +// frameworks/base/libs/input/MouseCursorController.cpp +void MouseCursorController::setDisplayViewport(const DisplayViewport& viewport, + bool getAdditionalMouseResources) { + ... + // Reset cursor position to center if size or display changed. + if (oldViewport.displayId != viewport.displayId || oldDisplayWidth != newDisplayWidth || + oldDisplayHeight != newDisplayHeight) { + float minX, minY, maxX, maxY; + if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) { + mLocked.pointerX = (minX + maxX) * 0.5f; + mLocked.pointerY = (minY + maxY) * 0.5f; + // Reload icon resources for density may be changed. + loadResourcesLocked(getAdditionalMouseResources); + ... + } + } else if (oldViewport.orientation != viewport.orientation) { + ... + } + + updatePointerLocked(); +} +``` + +### 加载 Pointer 相关资源 + +```cpp +// frameworks/base/libs/input/MouseCursorController.cpp +void MouseCursorController::loadResourcesLocked(bool getAdditionalMouseResources) REQUIRES(mLock) { + ... + policy->loadPointerResources(&mResources, mLocked.viewport.displayId); + policy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId); + ... +} +``` + +省略诸多细节,`loadPointerResources`将通过`InputManagerService`的`JNI`端以及`PointerIcon`的`JNI`端创建`PointerIcon`实例,并读取显示的资源。 + +`getSystemIcon`则是负责的函数,其将读取系统资源里名为`Pointer`的`Style`,并读取指针对应的资源 ID。 + +```java +// frameworks/base/core/java/android/view/PointerIcon.java + public static PointerIcon getSystemIcon(@NonNull Context context, int type) { + ... + int typeIndex = getSystemIconTypeIndex(type); + if (typeIndex == 0) { + typeIndex = getSystemIconTypeIndex(TYPE_DEFAULT); + } + + int defStyle = sUseLargeIcons ? + com.android.internal.R.style.LargePointer : com.android.internal.R.style.Pointer; + TypedArray a = context.obtainStyledAttributes(null, + com.android.internal.R.styleable.Pointer, + 0, defStyle); + int resourceId = a.getResourceId(typeIndex, -1); + ... + icon = new PointerIcon(type); + if ((resourceId & 0xff000000) == 0x01000000) { + icon.mSystemIconResourceId = resourceId; + } else { + icon.loadResource(context, context.getResources(), resourceId); + } + systemIcons.append(type, icon); + return icon; + } + + private static int getSystemIconTypeIndex(int type) { + switch (type) { + ... + case TYPE_SPOT_TOUCH: + return com.android.internal.R.styleable.Pointer_pointerIconSpotTouch; + ... + default: + return 0; + } + } +``` + +资源 ID 为 pointer_spot_touch_icon。 + +```xml + + + +``` + +其指向的图片就是如下熟悉的 Spot png:`pointer_spot_touch.png`。之后的`loadPointerIcon`阶段会将该图片解析成 Bitmap 并被管理在`SpriteIcon`中。 + +而`SpriteIcon`在`updatePointerLocked`阶段会被存放到`SpriteController`中,等待显示的调度。 + +```cpp +// frameworks/base/libs/input/MouseCursorController.cpp +void MouseCursorController::updatePointerLocked() REQUIRES(mLock) { + if (!mLocked.viewport.isValid()) { + return; + } + sp spriteController = mContext.getSpriteController(); + spriteController->openTransaction(); + + ... + if (mLocked.updatePointerIcon) { + if (mLocked.requestedPointerType == mContext.getPolicy()->getDefaultPointerIconId()) { + mLocked.pointerSprite->setIcon(mLocked.pointerIcon); + ... + } + mLocked.updatePointerIcon = false; + } + + spriteController->closeTransaction(); +} +``` + +## 显示Tap + +点击的时候`EventHub#getEvents`会产生事件,`InputReader#loopOnce`会调用`processEventsLocked`处理事件。 + +```cpp +// frameworks/native/services/inputflinger/reader/InputReader.cpp +void InputReader::loopOnce() { + ... + size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); + + { // acquire lock + ... + if (count) { + processEventsLocked(mEventBuffer, count); + } + .... + } // release lock + ... +} +``` + +之后调用`InputMapper`开始加工事件,并在`TouchInputMapper#cookAndDispatch`的时候调用`updateTouchSpots`更新 `PointerController`的一些参数。 + +```cpp +// frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp +void TouchInputMapper::updateTouchSpots() { + ... + mPointerController->setPresentation(PointerControllerInterface::Presentation::SPOT); + mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); + + mPointerController->setButtonState(mCurrentRawState.buttonState); + setTouchSpots(mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mCurrentCookedState.cookedPointerData.touchingIdBits, mViewport.displayId); +} +``` + +其中比较关键的`setTouchSpots`是显示Taps的关键步骤,准备 x、y 坐标和压力值。 + +在 Reader 而不是 Dispatch、更不是 ViewRootImpl 的时候处理的原因在于:Read 到事件即显示可以更早地响,同时不用占用 App 进程。 + +```cpp +// frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp +void TouchInputMapper::setTouchSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, + BitSet32 spotIdBits, int32_t displayId) { + std::array outSpotCoords{}; + + for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) { + const uint32_t index = spotIdToIndex[idBits.clearFirstMarkedBit()]; + float x = spotCoords[index].getX(); + float y = spotCoords[index].getY(); + float pressure = spotCoords[index].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); + ... + } + + mPointerController->setSpots(outSpotCoords.data(), spotIdToIndex, spotIdBits, displayId); +} +``` + +其后`PointerController`会通过`TouchSpotController`创建`Spot`实例向其发送`updateSprite`请求。最后回调 `SpriteController`调用`setIcon`处理。 + +```cpp +// frameworks/base/libs/input/TouchSpotController.cpp +void TouchSpotController::Spot::updateSprite(const SpriteIcon* icon, float x, float y, + int32_t displayId) { + sprite->setLayer(Sprite::BASE_LAYER_SPOT + id); + ... + if (icon != mLastIcon) { + mLastIcon = icon; + if (icon) { + sprite->setIcon(*icon); + sprite->setVisible(true); + } else { + sprite->setVisible(false); + } + } +} +``` + +```cpp +// frameworks/base/libs/input/SpriteController.cpp +void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) { + AutoMutex _l(mController->mLock); + ... + invalidateLocked(dirty); +} + +void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) { + ... + if (!wasDirty) { + mController->invalidateSpriteLocked(this); + } +} + +void SpriteController::invalidateSpriteLocked(const sp& sprite) { + bool wasEmpty = mLocked.invalidatedSprites.isEmpty(); + mLocked.invalidatedSprites.push(sprite); + if (wasEmpty) { + if (mLocked.transactionNestingCount != 0) { + mLocked.deferredSpriteUpdate = true; + } else { + mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES)); + } + } +} +``` + +`MSG_UPDATE_SPRITES`经过 Handler 回调`doUpdateSprites`,将取出封装在`SpriteUpdate`中的`SpriteIcon`并执行 draw。 + +```cpp +// frameworks/base/libs/input/SpriteController.cpp +void SpriteController::doUpdateSprites() { + ... + for (size_t i = 0; i < numSprites; i++) { + SpriteUpdate& update = updates.editItemAt(i); + + if ((update.state.dirty & DIRTY_BITMAP) && update.state.surfaceDrawn) { + update.state.surfaceDrawn = false; + update.surfaceChanged = surfaceChanged = true; + } + + if (update.state.surfaceControl != NULL && !update.state.surfaceDrawn + && update.state.wantSurfaceVisible()) { + sp surface = update.state.surfaceControl->getSurface(); + if (update.state.icon.draw(surface)) { + update.state.surfaceDrawn = true; + update.surfaceChanged = surfaceChanged = true; + } + } + } + ... + updates.clear(); +} +``` + +最后,`SpriteIcon`将取出`Bitmap`描画到`Surface`的`Canvas`上去。 + +```cpp +// frameworks/base/libs/input/SpriteIcon.cpp +bool SpriteIcon::draw(sp surface) const { + ... + graphics::Paint paint; + paint.setBlendMode(ABLEND_MODE_SRC); + + graphics::Canvas canvas(outBuffer, (int32_t)surface->getBuffersDataSpace()); + canvas.drawBitmap(bitmap, 0, 0, &paint); + ... + status = surface->unlockAndPost(); + if (status) { + ALOGE("Error %d unlocking and posting sprite surface after drawing.", status); + } + return !status; +} +``` + +## 总体流程 + +通过一个框图简单回顾一下整个流程。 + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/2023010813242.jpg) + +可以看到,简简单单的 Show taps 功能,从设置、配置、刷新再到显示,经历了多个进程、多个模块的协力。 + +## 涉及的Input核心逻辑框图 + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/2023010813243.jpg) \ No newline at end of file diff --git "a/_drafts/Android/2025-05-25-AOSP\345\274\200\345\217\221\350\247\204\350\214\203.md" "b/_drafts/Android/2025-05-25-AOSP\345\274\200\345\217\221\350\247\204\350\214\203.md" new file mode 100644 index 0000000..c77a09d --- /dev/null +++ "b/_drafts/Android/2025-05-25-AOSP\345\274\200\345\217\221\350\247\204\350\214\203.md" @@ -0,0 +1,18 @@ +--- +title: "AOSP开发规范" +categories: + - Android +tags: + - AOSP + - 开发规范 + - Android +toc: true +--- + +* 在AOSP原生代码函数中增加侵入式修改,需要使用begin/end函数对其进行包裹,避免影响到原生代码的可读性和可维护性。 +* AOSP中尽量仅包含插装代码,避免包含业务逻辑代码。业务逻辑代码应该解耦到特定目录。 +* 禁止在AOSP原生AIDL中添加新的接口。 +* 新增加接口,注意是否需要进行权限控制。 +* 内部接口增加@hide注解,避免被外部反射调用。 +* 新增内容使用特性开关进行隔离。 +* 核心代码修改需要在特定评审会评审通过后才能进行代码提交。 diff --git "a/_drafts/Android/Android OTA\345\215\207\347\272\247.md" "b/_drafts/Android/Android OTA\345\215\207\347\272\247.md" new file mode 100644 index 0000000..58cabc4 --- /dev/null +++ "b/_drafts/Android/Android OTA\345\215\207\347\272\247.md" @@ -0,0 +1,18 @@ +## Android A/B System OTA分析 + +* Android A/B System OTA分析(一)概览: +* Android A/B System OTA分析(二)系统image的生成: +* Android A/B System OTA分析(三)主系统和bootloader的通信: +* Android A/B System OTA分析(四)系统的启动和升级: + +## Android OTA升级原理和流程分析 + +* Android OTA升级原理和流程分析(一)--update.zip包的制作: +* Android OTA升级原理和流程分析(二)---update.zip差分包问题的解决: +* Android OTA升级原理和流程分析(三)---Android系统的三种启动模式: +* Android OTA升级原理和流程分析(四)---Android系统Recovery模式的工作原理: +* Android OTA升级原理和流程分析(五)---update.zip包从上层进入Recovery服务: +* Android OTA升级原理和流程分析(六)---Recovery服务流程细节: +* Android OTA升级原理和流程分析(七)---Recovery服务的核心install_package函数: +* Android OTA升级原理和流程分析(八)---升级程序update_binary的执行过程: +* Android OTA升级原理和流程分析(九)---updater-script脚本语法简介以及执行流程: diff --git "a/_drafts/Android/Android Studio\345\270\270\347\224\250\346\217\222\344\273\266\346\261\207\346\200\273.md" "b/_drafts/Android/Android Studio\345\270\270\347\224\250\346\217\222\344\273\266\346\261\207\346\200\273.md" new file mode 100644 index 0000000..3284226 --- /dev/null +++ "b/_drafts/Android/Android Studio\345\270\270\347\224\250\346\217\222\344\273\266\346\261\207\346\200\273.md" @@ -0,0 +1,14 @@ +* Android Localizationer: +* ADB Idea 方便卸载apk,删除缓存: +* Android ButterKnife Zelezny ButterKnife对应的插件: +* Android Code Generator 生成ViewHolder,生成initView方法: +* Codota 搜索代码块: +* GsonFormat jsonString自动转bean插件: +* SelectorChapek for Android 帮助生成selector: +* Android Drawable Importer 同一张图片生成多个自动生成多分辨率图片: +* Android Layout ID Converter xml到控件的转换: +* Android Postfix Completion toast和log加强: +* Android Studio Prettify 帮助findViewById: +* Android Parcelable code generator 生成Parcelable代码: +* Gradle Dependencies Helper gradle帮助插件: +* Android Toolbox Plugin 生成ViewHolder,意义不大: \ No newline at end of file diff --git "a/_drafts/Android/Android Studio\347\233\270\345\205\263\347\233\256\345\275\225\350\247\243\346\236\220.md" "b/_drafts/Android/Android Studio\347\233\270\345\205\263\347\233\256\345\275\225\350\247\243\346\236\220.md" new file mode 100644 index 0000000..98d4439 --- /dev/null +++ "b/_drafts/Android/Android Studio\347\233\270\345\205\263\347\233\256\345\275\225\350\247\243\346\236\220.md" @@ -0,0 +1,11 @@ +## `%USERPROFILE%\.` + +其中`CONFIGURATION_FOLDER`与Android Studio版本相关,比如对于Android Studio 3.0.1来说,该目录是指`C:\Users\jiang\.AndroidStudio3.0`。这个目录中保存着用户对于Android Studio的配置修改情况,比如你修改了字体、JVM参数等,都会在该文件中体现。如果你想重置Android Studid的设置,直接删除该文件夹,并重启Android Studio即可。想了解更详细的情况可以参考:[Configure Android Studio: ] + +## `%USERPROFILE%\AppData\Local\Android\Sdk` + +该目录保存了下载的SDK,该目录可以修改:`Settings -> Appearance & Behavior -> System Settings -> Android SDK` + +## `%USERPROFILE%\.android` + +该目录中保存了你的自定义AVD的配置,这些配置会覆盖SDK目录中的相关配置。如果你想重置Android Studid的设置除了要删除`%USERPROFILE%\.`,最好把该目录也删除,然后重启Android Studio。 \ No newline at end of file diff --git "a/_drafts/Android/Android Studio\347\247\273\345\212\250\351\274\240\346\240\207\346\230\276\347\244\272\346\202\254\346\265\256\346\217\220\347\244\272\347\232\204\350\256\276\347\275\256\346\226\271\346\263\225.md" "b/_drafts/Android/Android Studio\347\247\273\345\212\250\351\274\240\346\240\207\346\230\276\347\244\272\346\202\254\346\265\256\346\217\220\347\244\272\347\232\204\350\256\276\347\275\256\346\226\271\346\263\225.md" new file mode 100644 index 0000000..337d823 --- /dev/null +++ "b/_drafts/Android/Android Studio\347\247\273\345\212\250\351\274\240\346\240\207\346\230\276\347\244\272\346\202\254\346\265\256\346\217\220\347\244\272\347\232\204\350\256\276\347\275\256\346\226\271\346\263\225.md" @@ -0,0 +1,12 @@ + +以Windows 10 + Android Studio 3.0.1为例 + +默认情况下,在Android Studio中将鼠标移动到函数位置处无法显示悬浮提示,需要进行如下设置: + +`File -> Settings -> Editor -> General` 选中`Show quick documentation on mouse move` 点击`OK` + +但是此时如果将鼠标移动到函数位置处,会显示"fetching documentation",如果你网络比较好的话,等一会后会显示文档,但是如果网络不好的话则永远不会显示。这是因为Android Studio发现你没有在本地下载对应的文档,所以去google官网进行下载,速度比较慢。将文档下载到本地的方式是: + +`Toos -> Android -> SDK Manager` 选择`SDK Tools`页签,勾选`Documentation for Android SDK` 点击OK,下载完成后重启Android Studio应该很快看到悬浮文档提示了。 + +但是如果还不行的话,可能原因是对应的Android Studio版本优先从网络获取文档,即使你在本地下载了文档也不行,此时需要参考下面的地址,将默认读取方法进行修改: \ No newline at end of file diff --git "a/_drafts/Android/Android\345\271\263\345\217\260OpenGL ES_Assimp_OpenCV_GLM\351\233\206\346\210\220\350\257\264\346\230\216.md" "b/_drafts/Android/Android\345\271\263\345\217\260OpenGL ES_Assimp_OpenCV_GLM\351\233\206\346\210\220\350\257\264\346\230\216.md" new file mode 100644 index 0000000..18a527c --- /dev/null +++ "b/_drafts/Android/Android\345\271\263\345\217\260OpenGL ES_Assimp_OpenCV_GLM\351\233\206\346\210\220\350\257\264\346\230\216.md" @@ -0,0 +1,80 @@ +# Android平台OpenGL ES/Assimp/OpenCV/GLM集成说明 + +本文代码见: + +## 集成Assimp + +* 下载Assimp 5.0.1版本: +* 解压后本地目录为`D:\Code\temp\assimp-5.0.1` +* 将`scripts\android_crosscompile\make_android.bat`拷贝为`scripts\android_crosscompile\make_android_self_defined.bat` +* 将`scripts\android_crosscompile\make_android_self_defined.bat`中的内容进行自定义配置,我的配置如下: + +```shell +@echo off + +set ASSIMP_PATH=D:\Code\temp\assimp-5.0.1 +set CMAKE_PATH="C:\Users\jiangxin\AppData\Local\Android\Sdk\cmake\3.6.4111459\bin\cmake.exe" +set ANDROID_NDK_PATH=C:\Users\jiangxin\AppData\Local\Android\Sdk\ndk\22.0.7026061 +set ANDROID_CMAKE_PATH=C:\Users\jiangxin\AppData\Local\Android\Sdk\ndk\22.0.7026061\build\cmake + +pushd %ASSIMP_PATH% + +rmdir /s /q build +mkdir build +cd build + +%CMAKE_PATH% .. ^ + -G"MinGW Makefiles" ^ + -DCMAKE_BUILD_TYPE=Release ^ + -DCMAKE_CXX_FLAGS_RELEASE="%CMAKE_CXX_FLAGS_RELEASE% -Os -Wall -s" ^ + -DCMAKE_TOOLCHAIN_FILE=%ANDROID_CMAKE_PATH%\android.toolchain.cmake ^ + -DCMAKE_MAKE_PROGRAM=%ANDROID_NDK_PATH%\prebuilt\windows-x86_64\bin\make.exe ^ + -DANDROID_NDK=%ANDROID_NDK_PATH% ^ + -DANDROID_NATIVE_API_LEVEL=android-16 ^ + -DASSIMP_ANDROID_JNIIOSYSTEM=ON ^ + -DANDROID_ABI=arm64-v8a ^ + -DASSIMP_BUILD_ZLIB=ON ^ + -DASSIMP_BUILD_TESTS=OFF ^ + -DASSIMP_BUILD_ASSIMP_TOOLS=OFF ^ + -DASSIMP_NO_EXPORT=ON + +%CMAKE_PATH% --build . + +popd +``` + +* 执行如下编译命令: + +```shell +cd D:\Code\temp\assimp-5.0.1\scripts\android_crosscompile +.\make_android_self_defined.bat +``` + +* 将`assimp-4.1.0\build\codelibassimp.so`放到`app\libs\` +* 将`assimp-4.1.0\include`中的目录放到`app\src\main\cpp\include` +* 将`assimp-4.1.0\build\include\assimp\config.h`拷贝到`app\src\main\cpp\assimp-4.1.0\include\assimp` + +## 集成OpenCV + +OpenCV的集成比较简单,官网提供了Android平台所需的动态库和C++头文件。 + +* 下载OpenCV 4.5.1版本: +* 解压后本地目录为`D:\Code\temp\opencv-4.5.1-android-sdk` +* 将`OpenCV-android-sdk\sdk\native\libs\arm64-v8a\libopencv_java4.so`拷贝到`app\libs\` +* 将`OpenCV-android-sdk\sdk\native\jni\include`中的内容拷贝到`app\src\main\cpp\include` + +## 集成GLM + +GLM的集成就更简单了,源码都是hpp文件(即定义和实现在同一个文件中)。 + +* 下载GLM 0.9.9.8版本: +* 解压后本地目录为`D:\Code\temp\glm-0.9.9.8` +* 将`glm-0.9.9.8\glm`中的内容拷贝到`app\src\main\cpp\include` + +## 参考 + +* Android: Use Assimp to load a 3D model: +* AssimpAndroid: +* 使用Android Studio+CMakeLists编译assimp: +* TestAssimp: +* Assimp编译实录: diff --git "a/_drafts/Android/Android\347\234\237\346\234\272\345\256\211\350\243\205sqlite3\347\232\204\346\226\271\346\263\225.md" "b/_drafts/Android/Android\347\234\237\346\234\272\345\256\211\350\243\205sqlite3\347\232\204\346\226\271\346\263\225.md" new file mode 100644 index 0000000..1a0516f --- /dev/null +++ "b/_drafts/Android/Android\347\234\237\346\234\272\345\256\211\350\243\205sqlite3\347\232\204\346\226\271\346\263\225.md" @@ -0,0 +1,57 @@ + +Android版本: 4.4.2 + +```powershell +PS C:\Users\jiang> adb shell +shell@hwH60:/ $ su - root + +# 此时输入sqlite3 发现命令无法使用 +root@hwH60:/ # sqlite3 +tmp-mksh: sqlite3: not found + +# find一下相关文件,确定到底需要安装哪些内容,如果已经找到则不需要安装对应文件 +root@hwH60:/ # find . -name "sqlite3" +root@hwH60:/ # find . -name "libsqlite.so" +root@hwH60:/ # find . -name "libsqlite_jni.so" + +root@hwH60:/ # exit +shell@hwH60:/ $ exit + +# 从https://files.cnblogs.com/files/jiangxinnju/sqlite3.zip处下载文件并解压。 + +# 将相关文件放到内置存储卡中,为什么不直接放到/system/xbin/和/system/lib/可以参考 +PS D:\> adb push sqlite3 /storage/emulated/0/ +PS D:\> adb push libsqlite.so /storage/emulated/0/ +PS D:\> adb push libsqlite_jni.so /storage/emulated/0/ + +PS D:\> adb shell +shell@hwH60:/ $ su - root + +# 为什么需要重新挂载/system分区可以参考 +root@hwH60:/ # mount -o remount rw /system + +# 将需要的文件从内置存储卡中转移到目标目录 +root@hwH60:/ # cp /storage/emulated/0/sqlite3 /system/xbin/ < +root@hwH60:/ # cp /storage/emulated/0/libsqlite.so /system/lib/ +root@hwH60:/ # cp /storage/emulated/0/libsqlite_jni.so /system/lib/ + +# 修改对应文件的权限 +root@hwH60:/ # chmod 4755 /system/xbin/sqlite3 +root@hwH60:/ # chmod 0644 /system/lib/libsqlite.so +root@hwH60:/ # chmod 0644 /system/lib/libsqlite_jni.so + +# 执行sqlite3命令,发现已经可以使用 +root@hwH60:/ # sqlite3 +SQLite version 3.7.11 2012-03-20 11:35:50 +Enter ".help" for instructions +Enter SQL statements terminated with a ";" +sqlite> .exit + +root@hwH60:/ # exit + +# 删除内置存储卡中的文件 +shell@hwH60:/ $ rm -rf /storage/emulated/0/sqlite3 +shell@hwH60:/ $ rm -rf /storage/emulated/0/libsqlite.so +shell@hwH60:/ $ rm -rf /storage/emulated/0/libsqlite_jni.so + +``` \ No newline at end of file diff --git "a/_drafts/Android/\350\247\243\345\206\263adb push\346\227\266\345\207\272\347\216\260\347\232\204"Read-only file system"\351\227\256\351\242\230.md" "b/_drafts/Android/\350\247\243\345\206\263adb push\346\227\266\345\207\272\347\216\260\347\232\204"Read-only file system"\351\227\256\351\242\230.md" new file mode 100644 index 0000000..b80fc5d --- /dev/null +++ "b/_drafts/Android/\350\247\243\345\206\263adb push\346\227\266\345\207\272\347\216\260\347\232\204"Read-only file system"\351\227\256\351\242\230.md" @@ -0,0 +1,17 @@ + +出现`Read-only file system`问题,不是因为文件或者文件夹的权限不对,而是要push的目录对应的分区是以只读方式挂载的,网上给出的解决办法是重新以读写方式挂载对应分区,以`/system`分区为例,使用命令:`mount -o remount rw /system`,当然如果你想重新挂载系统分区需要有root权限。 + +但是你会发现,当你在adb shell中使用该方式重新挂载分区后,退出adb shell后用adb push命令向`/system`分区推送文件时仍然报错`Read-only file system`,这是因为在adb shell中重新挂载分区只针对当前的shell有效,在退出后该挂载方式失效。所以即使你打开两个cmd/powershell窗口,一个窗口使用adb shell重新挂载分区,在另一个窗口adb push也是不行的。 + +我这边最终尝试可行的方法是通过手机内置存储卡(外置SD卡也可以,但是有的手机没有外置SD卡)中转下,例如我想把计算机上的SystemUI.apk文件拷贝到手机的/system/app目录下,可以按照下面方式操作: + +```powershell +PS C:\Users\jiang> adb push .\SystemUI.apk /sdcard +.\SystemUI.apk: 1 file pushed. 3.9 MB/s (2621700 bytes in 0.648s) +PS C:\Users\jiang> adb shell +shell@hwH60:/ $ su - root +130|root@hwH60:/ # mount -o remount rw /system +root@hwH60:/ # cp /sdcard/SystemUI.apk /system/app/ +shell@hwH60:/ $ cd /system/app +root@hwH60:/system/app # chmod 644 SystemUI.apk +``` \ No newline at end of file diff --git "a/_drafts/C_Cpp/ACM\347\253\236\350\265\233\344\271\213\350\276\223\345\205\245\350\276\223\345\207\272\357\274\210\344\273\245C\344\270\216C++\344\270\272\344\276\213\357\274\211.md" "b/_drafts/C_Cpp/ACM\347\253\236\350\265\233\344\271\213\350\276\223\345\205\245\350\276\223\345\207\272\357\274\210\344\273\245C\344\270\216C++\344\270\272\344\276\213\357\274\211.md" new file mode 100644 index 0000000..4999245 --- /dev/null +++ "b/_drafts/C_Cpp/ACM\347\253\236\350\265\233\344\271\213\350\276\223\345\205\245\350\276\223\345\207\272\357\274\210\344\273\245C\344\270\216C++\344\270\272\344\276\213\357\274\211.md" @@ -0,0 +1,329 @@ +本文转自互联网,内容、排版有修正。 + +在ACM程序设计竞赛中,一道题目的所有测试数据是放在一个文本文件中,选手将一道题目的程序提交给评判系统运行,程序从该文件中读取测试数据,再把运行结果输出到另一个文本文件中。系统把输出文件与标准答案比对,来评判程序编写得正确与否。ACM现场赛采用的输入输出形式有(1)文件输入、标准输出;(2)文件输入、文件输出;(3)标准的输入输出。而Web形式的ACM程序设计在线评判系统一般采用标准的输入输出,但输入结束有文件末尾标识(EOF),这可以用于确定输入结束。 + +## 一、四种基本输入形式 + +### 1. 一组输入数据 + +示例:整数求和 + + +C语法: + +```C +#include +int main() +{ + int a,b; + scanf("%d %d",&a, &b); + printf("%d\n",a+b); +} +``` + +注意:输入前不要打印提示信息。输出完毕后立即终止程序,不要等待用户按键。 + +C++语法: + +```C++ +#include +using namespace std; +int main() +{ + int a ,b; + cin>>a>>b; + cout< + +C语法: + +```C +#include +int main() +{ + int a,b; + while (scanf("%d %d",&a, &b) != EOF) + printf("%d\n",a+b); +} +``` + +说明:scanf函数返回值就是读出的变量个数,如:scanf( “%d %d”, &a, &b );如果只有一个整数输入,返回值是1,如果有两个整数输入,返回值是2,如果一个都没有,则返回值是EOF。EOF是一个预定义的常量,等于-1 + +C++语法: + +```C++ +#include +using namespace std; +int main() +{ + int a ,b; + while (cin>>a>>b) + cout<> m >> n在读入发生错误返回0,否则返回cin的地址。 + +### 3. 多组输入数据,不说明多少组,以某特殊输入为结束标志。 + +示例:A + B Problem (2) + + +C语法: + +```C +#include +int main() +{ + int a,b; + while(scanf("%d %d",&a, &b) &&(a||b)) + printf("%d\n",a+b); +} +``` + +C++语法: + +```C++ +#include +using namespace std; +int main() +{ + int a ,b; + while(cin>>a>>b&&(a||b)) + {cout< + +C语法: + +```C +#include +int main() +{ + int a ,b,n; + scanf("%d",&n); + while(n--) + { + scanf("%d %d",&a, &b); + printf("%d\n",a+b); + } + return 0; +} +``` + +C++语法: + +```C++ +#include +using namespace std; +int main() +{ + int a ,b,n; + cin>>n + while(n--) + { + cin>>a>>b; + cout< + +C语法: + +```C +#include +int main() +{ + int n,sum,a; + while(scanf("%d",&n) && n) + { + sum=0; + while(n--) + { + scanf("%d",&a); + sum+=a; + } + printf("%d\n",sum); +printf("\n"); + } + return 0; +} +``` + +C++语法: + +```C++ +#include +using namespace std; +int main() +{ + int n,sum,a; + while(cin>>n&&n) + { + sum=0; + while(n--) + { + cin>>a; + sum+=a; + } + cout< 0) + printf(" "); + printf("%d", a[j]); + } + puts(""); + } +``` + +如果是按列,就要把1行和3行交换。 + +### 5、模拟屏幕输出 + +在一些模拟题中,题目要求输出一幅画,只不过这个画是由字符组成的。对于这种情况,可以采用和带格式的字符串输出相似的方法,先开一个字符数组(在这里,是二维数组),然后把数组当成屏幕输出,屏幕的(i, j)点就是数组的(i, j)号元素。最后,输出这个二维数组就行了。一般来说,可以输出一个二维字符数组的方法和输出一般数组的方法是一样的,用双重循环来做。不过,可以只用一个循环就可以了,原因是在数组每行的恰当位置(一般是末尾)加了一个'\0',那么,数组的每一行就成了一个字符串,于是,输出程序就变成了: + +```C +int i; +char str[100][100]; +... +for (i = 0; i < nRow; i++) +puts(str); +``` \ No newline at end of file diff --git "a/_drafts/C_Cpp/C++\344\273\216\351\224\256\347\233\230\350\276\223\345\205\245\346\226\207\344\273\266\347\273\223\346\235\237\347\254\246.md" "b/_drafts/C_Cpp/C++\344\273\216\351\224\256\347\233\230\350\276\223\345\205\245\346\226\207\344\273\266\347\273\223\346\235\237\347\254\246.md" new file mode 100644 index 0000000..a8b8513 --- /dev/null +++ "b/_drafts/C_Cpp/C++\344\273\216\351\224\256\347\233\230\350\276\223\345\205\245\346\226\207\344\273\266\347\273\223\346\235\237\347\254\246.md" @@ -0,0 +1,55 @@ + 当我们使用一个istream对象作为条件时,其效果是检测流的状态。如果流是有效的,即流未遇到错误,那么检测成功。当遇到文件结束符,或遇到一个无效输入时(例如需要将输入读到一个int变量中,但实际从键盘输入的是字符),istream对象的状态会变成无效。处于无效的istream对象会是条件变为假。 + + 当从键盘向程序输入数据时,对于如何指出文件结束符,不同的操作系统有不同的实现。在Windows平台中,输入文件结束符的方法是:按Ctrl+z,然后按Enter。在Unix或Linux下是按Ctrl+d,无需Enter,当然,由于当你输入Ctrl+d后,它仍然停留在系统的输入缓冲区中,所以你还是需要使用一个Enter使其生效。下面是几个关于该用法的示例: + +```CPP +#include +#include + +using namespace std; +/* +测试标准输入cin和文件结束符 +测试平台:Windows +*/ +int test_string_one(); +int test_string_two(); +int test_string_three(); + +int main() +{ + //test_string_one(); + test_string_two(); +} +int test_string_one() //第一个程序:输入的是整数 +{ + int num; + while(cin>>num) + cout << num << " "; + return 0; +} +/* +输入:1 2 3 4 5 Ctrl+d Enter +输出:1 2 3 4 5 +此处之所以循环停止,是因为遇到一个无效输入(Ctrl+d),而不是遇到了文件结束符。 +因为在windows平台,结束符是Ctrl+z,然后按Enter。 +例如输入:1 2 3 4 5 a Enter +输出仍是:1 2 3 4 5 +当然如果该测试用例用于Linux下,那么由于系统的结束符是Ctrl+d,所以虽然输出一样, +但是之所以循环停止,是因为到达了文件结束,而不是得到了一个无效输入。 +*/ + + +int test_string_two() //第二个程序:输入的是字符串 +{ + string word; + while(cin>>word) + cout << word << " "; + return 0; +} +/* +输入:hello world Ctrl+z 回车 +输出:hello world +此处之所以循环停止,是因为遇到一个文件结束符。 +因为在windows平台,结束符是Ctrl+z,然后按Enter。 +*/ +``` \ No newline at end of file diff --git "a/_drafts/C_Cpp/C++\347\274\226\350\257\221\351\224\231\350\257\257cannot have cv-qualifier.md" "b/_drafts/C_Cpp/C++\347\274\226\350\257\221\351\224\231\350\257\257cannot have cv-qualifier.md" new file mode 100644 index 0000000..02b9367 --- /dev/null +++ "b/_drafts/C_Cpp/C++\347\274\226\350\257\221\351\224\231\350\257\257cannot have cv-qualifier.md" @@ -0,0 +1,62 @@ +const关键字放在非静态成员函数声明的尾部,表示该非静态成员函数不修改对象内容。volatile关键字放到非静态函数声明的尾部,表示该非静态成员函数是线程安全的。注意他们都只能放到非静态成员函数声明的尾部,否则会产生如下报错: +`error: non-member function 'xxx' cannot have cv-qualifier` +放到非成员函数声明的尾部 + +```CPP +#include + +using namespace std; + +double getSqureArea(int a) const +{ + return a * a; +} + + +int main(int arg, char *argv[]) +{ + cout << getSqureArea(2) << endl; + return 0; +} +``` + +编译上面的C++程序,报错如下: + +```shell +g++ -c const_volatile_test.cpp -o const_volatile_test.o +const_volatile_test.cpp:12:28: error: non-member function 'double getSqureArea(int)' cannot have cv-qualifier +``` + +放到静态成员函数声明的尾部 + +```CPP +#include + +using namespace std; + +class CStatic +{ + private: + static int static_value; + public: + static int get_static_value() const + { + return static_value; + } +}; + +int CStatic::static_value = 1; +int main(int argc,char *argv[]) +{ + cout << CStatic::get_static_value()< + +#include + +using namespace std; + +//关于cin cin.getline cin.get getline gets getchar 的用法实例 + +void main(int argc, char* argv[]) + +{ + +//1、cin>> + +//method one, 也就是最常用的方法 输入一个数字 + +cout << "Test cin>> 用法1:" << endl; + +int a,b; + +cout << "input two integer:" << endl; + +cin >> a >> b; + +cout << "SUM =" << a + b << "\n" << endl; + +//method two,输入一个字符串,遇到“空格 回车 Tab”都结束 + +cout << "Test cin>>用法2:" << endl; + +char array[10]; + +cout << "input a char array:" << endl; + +cin >> array; + +cout << array << "\n" << endl; + +//2、cin.get() + +//one cin.get(字符变量名) 可以用来接收字符 + +cout << "Test cin.get(字符变量名):" << endl; + +char ch; + +char cch; + +cout << "Input a char:" << endl; + +ch = cin.get(); //把之前输入的回车符号滤去 + +cch = cin.get(); //or cin.get(ch); + +cout << cch << "\n" << endl; + +//two cin.get(字符数组,接收的字符数) 用来接收一行字符串可以接收空格 + +cout << "Test cin.get(字符数组,接收的字符数):" << endl; + +char array1[20]; + +cout << "Input a char array:" << endl; + +ch = cin.get(); //把之前输入的回车符号滤去 + +cin.get(array1,10); + +cout << array1 << "\n" << endl; + +//注:cin.get(无参数)主要用来舍弃输入流中不需要的字符 或者舍弃回车 + +//从而弥补了cin.get(字符数组,接收的字符数)的不足 + +//3、cin.getline(cin,str) 接收一个字符串 可以接收空格 + +cout << "Test cin.getline() 的用法:" << endl; + +char array2[20]; + +cout << "Input a char array:" << endl; + +ch = cin.get(); //把之前输入的回车符号滤去 + +cin.getline(array2,20); + +cout << array2 << "\n" << endl; + +//实际上cin.get(字符数组,接收的字符数) 和cin.getline(字符数组,接收的字符数) + +//有三个参数cin.getline(字符数组,接收字符数,结束字符) 第三个参数默认是'\0' + +//多维数组中也经常用到cin.getline(字符数组,接收的字符数)的用法 + +cout << "cin.get(字符数组,接收的字符数) is used in multidimensional array:" << endl; + +char array3[3][10]; + +for (int i = 0;i < 3;i ++) + +{ + +cout << "请输入第" << i+1 << "行的字符串:" << endl; + +cin.getline(array3[i],10); + +} + +for (int j = 0;j < 3;j ++) + +{ + +cout << "第" << j+1 << "行:" << array3[j] << endl; + +} + +//4、getline(cin,str)的用法 接收一个可以包含空格的字符串(这儿是string类型的) 需要包含头文件#include + +//getline(cin,str)是string流不是i/o流 + +cout << "Test getline(cin,str):" << endl; + +string str; + +cout << "Input a string:" << endl; + +//ch = cin.get(); //把之前输入的回车符号滤去 + +getline(cin,str); + +cout << str << "\n" << endl; + +//5、gets(char *) 接收一个可以包含空格的字符串 需要包含头文件#include + +cout << "Test gets(char *)的用法" << endl; + +char array4[20]; + +cout << "input a char array:" << endl; + +ch = cin.get(); //把之前输入的回车符号滤去 + +gets(array4); + +//The gets function reads a line from the standard input stream stdin and stores it in buffer. + +//The line consists of all characters up to and including the first newline character ('\n'). + +//gets then replaces the newline character with a null character ('\0') before returning the line + +cout << array4 << "\n" << endl; + +//gets(char *)也可以用在多维数组里面 跟cin.getline()用法类似 + +//6、getchar(无参数) 接收一个字符 需要包含头文件#include + +cout << "Test getchar(无参数)的用法:" << endl; + +char ch1; + + + +cout << "input a char:" << endl; + +ch1 = getchar(); // 不能写成getchar(ch1); + +cout << ch1 << "\n" << endl; + +//getchar()是C的函数 C++是兼容C 所以也可以使用 但尽量不用或少用 + +} +``` \ No newline at end of file diff --git "a/_drafts/C_Cpp/C_C++\344\270\255\347\232\204abort\343\200\201atexit\343\200\201exit\345\222\214_Exit.md" "b/_drafts/C_Cpp/C_C++\344\270\255\347\232\204abort\343\200\201atexit\343\200\201exit\345\222\214_Exit.md" new file mode 100644 index 0000000..ac22d40 --- /dev/null +++ "b/_drafts/C_Cpp/C_C++\344\270\255\347\232\204abort\343\200\201atexit\343\200\201exit\345\222\214_Exit.md" @@ -0,0 +1,69 @@ +这几个函数都在头文件`stdlib.h`中声明。`exit`、`_Exit`与`abort`函数使程序终止,控制并不返回到这些函数的调用者。 + +## exit函数 + +```c +void exit(intstate); +``` + +`exit`函数用于在程序运行的过程中随时结束程序,`exit`的参数`state`是返回给操作系统,返回0表示程序正常结束,非0表示程序非正常结束。`main`函数结束时也会隐式地调用`exit`函数。`exit`函数运行时首先会执行由`atexit`函数登记的函数,然后会做一些自身的清理工作,同时刷新所有输出流、关闭所有打开的流、删除标准I/O函数`tmpfile`创建的临时文件、控制返回宿主环境,提供状态值。 + +按照许多系统中的习惯,`state`值为0表示终止程序成功,用非0值表示异常终止。标准C语言中数值0和宏EXIT_SCCESS的值表示终止成功,宏EXIT_FAILURE的值表示终止不成功,其他值的含义由实现定义。从函数`main`返回一个整数值相当于用这个值调用`exit`函数。 + +## _Exit函数 + +```c +void _Exit(int status); //C99 +``` + +函数`_Exit`与`exit`函数不同之处在于既不调用`atexit`注册的退出处理器,也不调用`singal`注册的信号处理器。是否进行其他清理操作由实现定义,如关闭所有打开的数据流。`_Exit`是C99增加的,传统上有些实现用名为`_exit`的函数提供类似功能。 + +## atexit函数 + +```c +int atexit(void(*func)(void)); +``` + +很多时候我们需要在程序退出的时候做一些诸如释放资源的操作,但程序退出的方式有很多种,比如`main`函数运行结束、在程序的某个地方用`exit`结束程序、用户通过`Ctrl+C`或`Ctrl+break`操作来终止程序等等,因此需要有一种与程序退出方式无关的方法来进行程序退出时的必要处理。方法就是用`atexit`函数来注册程序正常终止时要被调用的函数。 + +`atexit`函数的参数是一个函数指针,函数指针指向一个没有参数也没有返回值的函数。 + +在一个程序中最多可以用`atexit`注册32个处理函数,这些处理函数的调用顺序与其注册的顺序相反,也即最先注册的最后调用,最后注册的最先调用。注册函数不能引用任何不是自己定义的存储类为auto或 register的对象(例如通过指针引用)。函数注册几次就会在此时调用几次。 + +下面是一段代码示例: + +```c +#include +#include + +void terminateTest() +{ + cout<<"程序正在结束..."< + +## 环境概要 +* Code::Blocks 13.12 +* Windows 8.1 +* gtest-1.7.0 + +## 说明 +gtest是一个优秀的开源C++单元测试框架,详细介绍可以参考官方网站。由于某种原因需要在Windows下使用Code::Blocks进行C++开发,特将配置过程进行记录以备之后查阅。 + +下载安装Code::Blocks,官方网址是,我下载的是codeblocks-13.12mingw-setup.exe版本,由于Code::Blocks的安装比较简单,这里我就不多说了,相信大家看此文章的目的不是查阅Code::Blocks的安装说明。 + +下载gtest,官方网站是,我下的是gtest-1.7.0.zip版本,下载完成之后进行解压,我解压的目录是E:\ gtest-1.7.0\ + +下载安装CMake,CMake是跨平台的构建工具,官方网站是,我下载的是cmake-3.2.1-win32-x86.exe,安装之后运行cmake-gui,配置源代码目录(之前解压的gtest目录)和要build到的目标目录。可以参考如下截图,但目录设置要根据自己的环境进行配置。 + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/611264-20181223093156175-476261803.png) + +单击Configure,出现如下对话框,按照下图选择合适选项: + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/611264-20181223093209117-1699921407.png) + +单击Finish,会在目标目录产生MinGW Makefiles,之后会出现下图: + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/611264-20181223093226394-691530640.png) + +选中gtest_disable_pthreads一项,单击Generate,即会产生Code::Blocks工程文件(.cbp)。 +用Code::Blocks打开该文件,直接进行编译,会在目标目录产生两个库文件: +* libgtest.a +* libgtest_main.a + +此时gtest的编译工作就完成了,用Code::Blocks新建工程目录gtest,在maim.h中写入如下代码: + +```cpp +#include +#include +int add(int a, int b) +{ + return a+b; +} +TEST(addtest, HandleNoneZeroInput) +{ + EXPECT_EQ(14, add(4, 10)); + EXPECT_EQ(-2, add(-3, 1)); +} +int main(int argc, char *argv[]) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); + return 0; +} +``` + +将gtest源代码目录中的`include`中的文件拷贝到工程目录中的`include`文件夹中,将之前build生成的两个文件拷贝到工程目录中的`lib`文件夹中。右键`project`,选择`Build options...`,在`Linker settings > Other linker options`下填写`-lgtest`。点击选项卡`Search directories`,在`Compiler`子选项卡中`Add`一项,加入`include`目录,在`Linker`子选项卡中`Add`一项,加入`lib`目录。点击OK + +此时build工程如果成功会出现以下结果: + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/611264-20181223093246425-1302771199.png) + + +如果工程中想使用C++11的新特征,得用命令行参数`-std=gnu++11`,而不是`-std=c++11`。具体做法是右键`project`,选择`Build options...`,清除`Compiler settings > Compile Flags`下与-std=有关的复选框,点击选项卡`Compiler settings > Other options`,在对话框中填写`-std=gnu++11`。如果直接使用-std=c++11等选项会出现以下错误: + +```shell +include/gtest/internal/gtest-port.h: In function 'int testing::internal::posix::StrCaseCmp(const char*, const char*)': +include/gtest/internal/gtest-port.h:1719:25: error: '_stricmp' was not declared in this scope +include/gtest/internal/gtest-port.h: In function 'char* testing::internal::posix::StrDup(const char*)': +include/gtest/internal/gtest-port.h:1721:58: error: '_strdup' was not declared in this scope +include/gtest/internal/gtest-port.h: In function 'int testing::internal::posix::FileNo(FILE*)': +include/gtest/internal/gtest-port.h:1729:52: error: '_fileno' was not declared in this scope +include/gtest/internal/gtest-port.h: In function 'FILE* testing::internal::posix::FDOpen(int, const char*)': +include/gtest/internal/gtest-port.h:1779:71: error: 'fdopen' was not declared in this scope +``` + +另外,如果你的project的build option中已经清除Compiler settings > Compile Flags下与-std=有关的复选框,但还是出现这个问题,可能原因是你的global设置有问题,你可以查看Settings->Complier选项卡是否也设置正确,如果没有请正确设置。 + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/611264-20181223093303761-1014037495.png) \ No newline at end of file diff --git "a/_drafts/C_Cpp/C\350\257\255\350\250\200\344\270\255\351\232\217\346\234\272\346\225\260\347\233\270\345\205\263\351\227\256\351\242\230.md" "b/_drafts/C_Cpp/C\350\257\255\350\250\200\344\270\255\351\232\217\346\234\272\346\225\260\347\233\270\345\205\263\351\227\256\351\242\230.md" new file mode 100644 index 0000000..a8a874d --- /dev/null +++ "b/_drafts/C_Cpp/C\350\257\255\350\250\200\344\270\255\351\232\217\346\234\272\346\225\260\347\233\270\345\205\263\351\227\256\351\242\230.md" @@ -0,0 +1,75 @@ +用C语言产生随机数重要用到rand函数、srand函数、及宏RAND_MAX(32767),它们均在stdlib.h中进行了声明。 + +int rand(void);//生成一个随机数 + +voidsrand(unsigned int seed); //为rand设置“种子”的值 + +srand()就是给rand()提供种子seed,如果srand每次输入的数值是一样的,那么每次运行产生的随机数也是一样的。通常的做法是以这样一句代码: + +srand((unsigned)time(NULL)); + +来取代,这样将使得种子为一个不固定的数,这样产生的随机数就不会每次执行都一样了。先看一个例子: + +```C +#include + +#include + +#include + + + +int test_rand() + +{ + + int i; + + /* Seed therandom-number generator with current time so that + + * thenumbers will be different every time we run. + + */ + + srand((unsigned)time( NULL ) ); + + /* Display10 numbers. */ + + for( i = 0;i < 10; i++ ) + + printf("%6d\n", rand()); + + return 0; + +} +``` + +C的函数库之所以没有把使用系统时钟初始化随机种子这步重要的操作直接放进rand函数的实现中,可能有如下原因: + +1.可以高效产生连续的随机数,不用每次都初始化; + +2.给程序员以更高的灵活性,因为可能在要求较高的场合,应该使用更好的的数据做种子,而不是系统时钟; + +3.对于只是想产生大量伪随机数来尽兴某种验证或者统计,未必需要初始化,大不了程序每次运行都产生同样的一系列随机数而已——有些情况下,这是无所谓的。 + +4.作为伪随机序列产生器的rand()函数,必须具备的一个重要特性就是:产生的序列必须是可重现的。这不仅仅是一个算法,相当大的程度上,它关系到代码测试的准确性。如果算法中使用了和rand()的结果相关的数据,通过一个可控的可重现序列,我们就有机会再现每一次测试的过程,从而更有效的找到问题的所在。所以这里提出一个建议,代码中,如果rand()的函数结果关系到算法的结果,那么,必须保证你的rand()调用是可重现的。 + +另外使用rand还用几个问题: + +* 如何生成 0到 100之间的随机数? + +用"int x = rand() % 100;"这种方法是不或取的,会使产生的随机数不在随机。产生一个0到n之间的随机数的比较好的做法是: + +j=(int)(n*rand()/(RAND_MAX+1.0)); + +* 如何产生一个范围在(a,b)之间的随机数? + +先计算a与b的差值,设c=b-a;产生一个介于0和b-a的数值,设 + +d=(int)((b-a)*rand())/(RAND_MAX+1.0) + +让上面产生的值d加上a就可以了。 + +如果你使用C++11编程,请使用C++11自己的随机数生成方法! + +虽然前面介绍了那么多,但是我还是想说C语言的随机数生成方法有很多缺陷,很容易被引入非随机性,而且功能单一,如果可以的话去,你最好避免使用它。 \ No newline at end of file diff --git "a/_drafts/C_Cpp/const \344\270\215\345\206\215\350\277\267\350\214\253.md" "b/_drafts/C_Cpp/const \344\270\215\345\206\215\350\277\267\350\214\253.md" new file mode 100644 index 0000000..986d370 --- /dev/null +++ "b/_drafts/C_Cpp/const \344\270\215\345\206\215\350\277\267\350\214\253.md" @@ -0,0 +1,78 @@ +首先说明一下const在C和C++中的主要用法,被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。它可以修饰变量、函数的参数、返回值,甚至函数的定义体。 + +## const修饰变量 + +const修饰变量通常用于定义符号常量。我们过去一般使用宏定义的方式定义符号常量,比如: + +`#define PI3.1415926` + +其实我们也可以使用const方式定义符号常量,下面的语句与上面宏定义的方式达到的效果相似。 + +`const double PI3.1415926` + +那两者有什么区别呢?在使用宏定义的方式时,首先在预处理过程中将源程序中的常量名全部替换成对应的字面常量,然后对替换过的源程序进行编译。相比较之下const方式不涉及预处理过程,它只在编译过程中起作用。因为宏定义方式定义的符号常量没有类型,所以不能进行严格的类型检查,而const则可以,并且有些调试工具可以对const常量进行调试,而不能对宏常量进行调试。但这也不是说宏常量就比const常量差,比如你能够利用const常量实现下面的宏定义功能么? + +```CPP +#ifndefJIANGXIN_H +#defineJIANGXIN_H +#endef +``` + +而且由于历史原因,实际上使用宏常量的情况远远大于const常量,特别是在纯C编程环境中。不过在C++编程环境中,我还是推荐你使用const,因为这更加安全。同时时刻记住一句话,const常量作用与编译期,宏常量作用于预编译期,当你遇到一些令人迷惑的问题时想想这句话,或许能够有所收获。 + +现在举几个例子: + +const int m = 0; //其实我们通常把const符号常量叫做const常量,以便和宏常量区分 + +typedef char * pStr; //新的类型pStr,代表一个指向char的指针类型。 + +char string[4] = "abc"; + +const char *p1 = string;//p1是一个指针,指向一个const char类型 + +p1++; //正确,p1本身不是常量,它指向一个常量 + +const pStr p2 = string; //p2是一个常量指针,这个指针指向一个char类型变量 + +p2++; //错误,p2是一个常量指针 + +char *const p3 = string; //p3同p2相同,是一个常量指针 + +char const* p4 = string; //p4同p1相同,是一个指向const char的指针,只能用于C中,在C++中只能使用const char *p4 = string; + + +## const修饰函数形参 + +函数形参主要包括传值型参数,传指针型参数,传引用型参数。 + +对于传指针和传引用型参数,如果我们怕在该函数中错误的改变实参的值,一般都会加上const来修饰形参。 + +对于传值型参数,我们一般不会使用const修饰,因为完全没有必要,你是不是用const,都不能改变实参的值,因为传值型形参是在栈中分配的,函数调用之后一定会销毁。例如:void Fun(int n)和void Fun(const int n)没有任何区别,而且还是代码更加晦涩,所以不推荐。但是有一种情况需要考虑,如果你需要传递一个比较复杂的类类型,但是你又不需要改变该类的对象,这时你可以将传值改为传指针或者引用,同时用const修饰。这样的话就避免了构造临时对象的开销。比如加入A是一个很复杂的类类型,这是使用void Fun(const A &a)要比void Fun(A a)效率更高。 + +## 用const修饰函数的返回值 + +函数的返回值与形参类似,也包括返回值类型,返回指针类型,返回引用类型。 + +对于返回值类型为指针或者引用的情况,如果我们不希望其被修改,可以使用const对返回值进行限定。此时该返回值const修饰的同类型const指针(返回值为指针)或者同类型的const变量(返回值为引用,且该值为基本类型或者定义了拷贝构造函数、拷贝赋值运算符的类型)。如对于: + +const char * Fun(void); + +如下语句将出现编译错误: + +char *str = Fun();//cannotconvert from 'const char *' to 'char *'; + +正确的用法是: + +const char *str= Fun(); + +如果返回值类型为值类型,由于函数会把返回值复制到外部函数的存储单元中,加const修饰没有任何价值,所以不要把函数int Fun(void) 写成const int Fun(void)。但是在某些情况下将值类型改为引用类型或者指针类型可以提高效率,可用不用const就看你要不要改变它们了。 + +## const修饰成员函数 + +const关键字可以放在非静态成员函数声明的尾部,表示该函数不修改对象中的成员变量的值。注意const只能修饰非静态成员函数,不能修饰普通函数,无论是C还是C++。 + +有些人可能会疑惑为什么纯虚函数后面为什么一般都写const,其实如前所述,函数后面的const只是提示以后维护代码的人,这个函数里面没有改变变量的值。所以,只要是没有改变变量值的函数,就在后面写个const,纯虚函数当然没有改变某个变量的值,所以习惯上就加了const,成员函数用不用const关键在于这成员函数是不是要修改对象的数据成员。而与纯虚函数没什么关系。 + + + + \ No newline at end of file diff --git "a/_drafts/C_Cpp/\345\261\217\345\271\225\350\276\223\345\207\272VS\346\226\207\344\273\266\350\276\223\345\207\272.md" "b/_drafts/C_Cpp/\345\261\217\345\271\225\350\276\223\345\207\272VS\346\226\207\344\273\266\350\276\223\345\207\272.md" new file mode 100644 index 0000000..51cd015 --- /dev/null +++ "b/_drafts/C_Cpp/\345\261\217\345\271\225\350\276\223\345\207\272VS\346\226\207\344\273\266\350\276\223\345\207\272.md" @@ -0,0 +1,60 @@ +## 输出到屏幕快还是输出到文件快? + +我们在编写程序时经常需要数一些数据到屏幕,来查看我们的结果是否正确,虽然直接输出到屏幕,查看起来呢很方便,但当数据量很大时,需要耗费大量的时间。于是我们想到能不能通过输出到文件来减少时间呢。相同的数据是输出到屏幕更快还是输出到文件更快? + +这个地方变量有很多:磁盘速度、目的文件有没有其他IO请求、文字渲染的方式、API具体的操作流程、操作系统本身的设计等等都会影响输出到文件的速度。但一般来说还是会比直接输出到屏幕快(而且通常快几个数量级)。 + +比如我们可以用如下代码进行测试,如果测试输出到文件的时间就在开头加入#define ToFile,如果测试输出到屏幕的时间,就注释掉。 + +```c +//#define ToFile +#include +#include + +int main() +{ + clock_t start_test,end_test; + start_test = clock(); + FILE *output_fils; + output_fils = fopen("output_file.txt","w"); + if(output_fils == NULL) + { + perror("Error to create the file\n"); + } + long unsigned int i; + for(i=0;i<1000000;i++) + { + #ifdef ToFile + fprintf(output_fils,"item %ld\n",i); + #else + printf("item %ld\n",i); + #endif + } + fclose(output_fils); + end_test = clock(); + printf("The total time is: %lf",((double)(end_test-start_test)/CLOCKS_PER_SEC)); + return 0; +} +``` + +通过编译运行,我们会发现,如果输出到文件仅需要0.015s,但是直接输出到屏幕却需要12.906s,两者差距很大。 + +## 怎样编程使结果全部输出到文件? + +当然,你可以在每个需要输出的地方用fprintf来设置输出到文件。但是考虑到那样太麻烦了,而且我们已经系管理直接使用printf,所以我们可以用一个函数freopen来把标准输出流导出到我们设定的文件流中,这样我们以后用printf输出到东西全部到达我们设定的文件中。 + +但是有个问题是,那我们如何在某些特定的时候在屏幕上输出提示信息呢?考虑到标准错误流也是输出到屏幕,所以我们可以假借这个标准错误流。示例代码如下: + +```c +#include + +int main() +{ + int num; + freopen("output_file.txt","w",stdout); + fprintf (stderr,"Please input your num:"); // output to the screen + scanf("%d",&num); + printf("The num that you input is:%d",num); // output to the file + fclose (stdout); + return 0; +} diff --git "a/_drafts/C_Cpp/\347\224\261\345\207\275\346\225\260clock\346\203\263\345\210\260\347\232\204.md" "b/_drafts/C_Cpp/\347\224\261\345\207\275\346\225\260clock\346\203\263\345\210\260\347\232\204.md" new file mode 100644 index 0000000..fa4be99 --- /dev/null +++ "b/_drafts/C_Cpp/\347\224\261\345\207\275\346\225\260clock\346\203\263\345\210\260\347\232\204.md" @@ -0,0 +1,82 @@ + +今天介绍一下`clock`这个函数的使用,它是C标准库的一部分,声明在头文件``中,返回处理器使用的时间值,函数声明为: +`clock_t clock(void);` + +这个函数看起来很简单,但是当使用时还是有不少需要注意的地方,让我们先看看`clock_t`这个类型,它表示程序所占用的处理器时间,具体的实现可以是整形或者浮点型,例如我们如果查看`CodeBlock 12.11`中的`time.h`文件,可以看到如下定义: + +```c +/* + * A type for measuring processor time (in clock ticks). + */ +#ifndef _CLOCK_T_DEFINED +typedef long clock_t; +#define _CLOCK_T_DEFINED +#endif +``` + +在这里,`clock_t`被定义为long类型,`MS Visual Studio`中的time.h与此完全相同,但是,如果你将其定义修改为double也没有什么不好的,虽然它的本意是指”ticks”,也就是中文中的“滴答”。那么什么是“滴答”呢?简单的将就是系统每发生一次时钟中断就会产生一个“滴答”,如果详细介绍的话,这设计很多系统内核时钟中断的问题,不过詹荣开老师在它的一篇文章`Linux内核的时钟中断`中对这些概念有着很详细透彻的解读,虽然文章发表于2003年,但其中的骨架知识仍然适用。 + +介绍完`clock_t`的概念,还要介绍一下`CLOCKS_PER_SEC`这个宏定义,(也是在time.h中),从它的字面意思就可得知,它指的是每秒的时钟滴答数,通过用clock函数返回值除以该值可以得到程序运行是实际秒数。`CLOCKS_PER_SEC`的实际值也是随着操作系统和编译器的差异而不同,例如现在Windows平台上的编译器通常会将其定义为1000,也就是说每秒会产生1000个时钟滴答数。而在一些比较古老的编译器中,比如说TC2.0中,该值是18.2个(当然,在TC2.0中不叫`CLOCKS_PER_SEC`,而叫`CLK_TCK`,但它们的实质是一样的,VC6.0中为了兼容保留了`CLK_TCK`的名称,但建议使用`CLOCKS_PER_SEC`),为什么不同的编译器的默认值差异这么大,这主要是因为与当时硬件条件有关,这是个历史问题,在这里就不继续探讨了。 + +另外,经常看到一些文章中把`CLOCKS_PER_SEC`翻译成每秒的时钟周期数,其实这是错误的,混淆了时钟周期(clock cycle)和时钟滴答(clock tick)的概念,关于这两个词的区别詹荣开老师也做了介绍。但如果你不想深挖,可以简单的认为要经过若干时钟周期才是一个时钟滴答,具体是多少个决定于系统中对可编程间隔定时器(Programmable Interval Timer,PIT)值的初始定义。 + +好了,说了这么多,让我们回到clock函数,它主要的用处是衡量我们程序时间开销,例如: + +```c +#include +#include + +int main(int argc,char* argv[]) +{ + clock_t clock_time,start_time,end_time; + long int count = 1000000000; + start_time = clock(); + while(count--); + end_time = clock(); + clock_time = end_time - start_time; + printf("The program runs %lf clocks\n",(double)clock_time); + printf("The program runs %lf s\n",(double)(clock_time/CLOCKS_PER_SEC)); + return 0; +} +``` + +程序的运行结果为: + +```shell +The program runs 8430.000000 clocks +The program runs 8.000000 s +``` + +由于在我的机器上,`CLOCKS_PER_SEC`的值被定义为1000,所以从结果上看是没有问题的。但我们稍微修改一下程序,把初始的count值改为10000,看看结果有什么不同。 + +```c +#include +#include + +int main(int argc,char* argv[]) +{ +  clock_t clock_time,start_time,end_time; +  long int count = 10000; +  start_time = clock(); +  while(count--); +  end_time = clock(); +  clock_time = end_time - start_time; +  printf("The program runs %lf clocks\n",(double)clock_time); +  printf("The program runs %lf s\n",(double)(clock_time/CLOCKS_PER_SEC)); +  return 0; +} + +运行结果如下: + +```shell +The program runs 0.000000 clocks +The program runs 0.000000 s +``` + +咦,为什么变成了0,结合编译器为我们指出的运行时间并联系上面的程序,可以发现程序的问题出现在由于count值很小,计算机在不到一个滴答的时间内就完成了计算,又因为clock_t的默认类型是long型,所以会截断取整,所以结果会产生错误。问题根源找到了,那有没有什么解决办法呢?欢迎大家提出自己的见解。 + +参考文献:`The Standart C Library P.J.Plauger` + + + + \ No newline at end of file diff --git "a/_drafts/C_Cpp/\347\274\226\347\250\213\350\216\267\345\276\227CPU\347\232\204\344\270\273\351\242\221.md" "b/_drafts/C_Cpp/\347\274\226\347\250\213\350\216\267\345\276\227CPU\347\232\204\344\270\273\351\242\221.md" new file mode 100644 index 0000000..e0aaf69 --- /dev/null +++ "b/_drafts/C_Cpp/\347\274\226\347\250\213\350\216\267\345\276\227CPU\347\232\204\344\270\273\351\242\221.md" @@ -0,0 +1,63 @@ +CPU的主频,即CPU内核工作的时钟频率(CPU Clock Speed)。CPU的主频表示在CPU内数字脉冲信号震荡的速度。主频和实际的运算速度存在一定的关系,但目前还没有一个确定的公式能够定量两者的数值关系,因为CPU的运算速度还要看CPU的流水线的各方面的性能指标(缓存、指令集,CPU的位数等等)。由于主频并不直接代表运算速度,所以在一定情况下,很可能会出现主频较高的CPU实际运算速度较低的现象。 + +在windows操作系统中,可以使用右键点击“我的电脑”,查看属性来获取CPU的主频信息,然而该信息是存在于注册表之中的。也就是说可以通过修改注册表来伪造CPU主频信息。那能不能用其它方法获得该信息呢? + +检测CPU的速度,一般是测试在单位时间内运算的指令条数,但用这种方法有太大的局限性,由于受到很多因素的影响,准确度比较低,因为你不知道在你的程序外别的程序占用了多少的时间片。其实在586及之后处理器中,已经有了一条专用的指令来测试主频,那就是 RDTSC指令,意思是读取时间标记计数器(Read Time-Stamp Counter),Time-stamp counter 是处理器内部的一个64位的MSR (model specific register),处理器每时钟周期递增时间标签计数器 MSR 一次,在处理器复位时将它重设为 0。RDTSC 指令把 TSC的值低32位装入EAX中,高32位装入EDX中。如果CPU的主频是200MHz,那么在一秒钟内,TSC的值增加 200,000,000 次。所以在计算的时候,把两次的TSC差值除以两次的时间差值就是CPU的主频。 + +从上面的资料得到了一个思路:首先使用RDTSC指令获取1个TSC的值,将其存储起来,再延时1秒,使用RDTSC指令获取1个新的TSC值,并用其减去第一次获得的TSC值,即可得到该CPU的主频。用C和汇编混合的代码如下: + +```c +#include +#include + +int main(int argc,char* argv[]) +{ + static int time[2]; + int quotient = 0; //商 + int remainder = 0; //余数 + + __asm{ + rdtsc // read time-stamp count + mov ebx,offset time //将time的偏移地址存入ebx + mov [ebx+0],edx //把TSC的值的高32位存入[ebx+0]中 + mov [ebx+4],eax //把TSC的值的低32位存入[ebx+4]中 + } + Sleep(1000); + __asm{ + rdtsc // read time-stamp count + mov ecx,offset time //将time的偏移地址存入ecx + sub eax,[ecx+4] //把延时1秒后的TSC值的低32位减去1秒前的TSC值的低32位 + sbb edx,[ecx+0] //把延时1秒后的TSC值的高32位减去1秒前的TSC值的高32位 + + mov ecx,1000000000 //转换成GHz + div ecx + mov quotient,eax //将结果中的商赋值于quotiend + mov remainder,edx //将结果中的余数赋值于remainder + } + remainder = remainder / 10000000; //余数仅保留两位 + printf("该机主频为:%d.%d",quotient,remainder); + return 0; +} +``` + +注意:寄存器 CR4 中的时间标签禁用 (TSD) 标志限制 RDTSC 的使用。清除 TSD 标志时,RDTSC 指令可以在任何特权级别执行;设置此标志时,指令只能在特权级别 0 执行。在特权级别 0 执行时,时间标签计数器还可以使用 RDMSR 指令读取。 + +但是在多核时代,RDTSC 指令的准确度大大削弱了,原因有如下几点: + +1. 不能保证同一块主板上每个核的 CPU 时钟周期数(Time Stamp Counter)是同步的; + +2. CPU 的时钟频率可能变化,例如笔记本电脑的节能功能; + +3. 乱序执行导致 RDTSC 测得的周期数不准。 虽然 RDTSC 废掉了,高精度计时还是有办法的,在 Windows 上用 QueryPerformanceCounter 和 QueryPerformanceFrequency,Linux 上用 POSIX 的 clock_gettime 函数,以 CLOCK_MONOTONIC 参数调用。 + +在接下来的几篇文章章中,我会继续介绍相关内容。 + +参考文章: + +1、多核时代不宜再用 x86 的 RDTSC 指令测试指令周期和时间 + +http://blog.csdn.net/solstice/article/details/5196544 + +2、RDTSC命令详解 + +http://blog.csdn.net/tbwood/article/details/5536597 \ No newline at end of file diff --git "a/_drafts/Database/(\350\275\254)SQLServer\345\256\236\344\276\213\350\256\262\350\247\243.md" "b/_drafts/Database/(\350\275\254)SQLServer\345\256\236\344\276\213\350\256\262\350\247\243.md" new file mode 100644 index 0000000..66895c0 --- /dev/null +++ "b/_drafts/Database/(\350\275\254)SQLServer\345\256\236\344\276\213\350\256\262\350\247\243.md" @@ -0,0 +1,51 @@ + +实例就是一个在数据库中存在的,现实的数据库例子。数据库实例就是后台进程和数据库文件的集合。一个SQL Server服务器就是一个实例,2000支持在同一个NT/2000 Server操作系统装多个服务器,换言之,就是多个实例。 + +# 多个SQL Server实例 + +Microsoft® SQL Server™ 2000 支持在同一台计算机上同时运行多个 SQL Server 数据库引擎实例。每个 SQL Server 数据库引擎实例各有一套不为其它实例共享的系统及用户数据库。应用程序连接同一台计算机上的 SQL Server 数据库引擎实例的方式与连接其它计算机上运行的 SQL Server 数据库引擎的方式基本相同。实例主要应用于数据库引擎及其支持组件,而不应用于客户端工具。如果安装了多个实例,则每个实例都将获得各自唯一的一套: +系统和用户数据库。 +SQL Server 和 SQL Server 代理服务。对于默认实例,服务名仍为 MSSQLServer 和 SQLServerAgent。对于命名实例,服务名改为 MSSQL$instancename 和 SQLAgent$instancename,使得这些服务与服务器上的其它实例分开启动和停止。可使用相关联的 SQL Server 服务启动和停止不同实例的数据库引擎。SQL Server 代理服务管理相关联的数据库引擎实例的调度事件。 +与数据库引擎、SQL Server 和 SQL Server 代理服务相关联的注册表键。使应用程序能连接特定实例的网络连接地址。 +有两种类型的 SQL Server 实例: + +## 默认实例 + +SQL Server 2000 数据库引擎默认实例的运行方式与 SQL Server 早期版本的数据库引擎相同。默认实例仅由运行该实例的计算机的名称唯一标识,它没有单独的实例名。如果应用程序在请求连接 SQL Server 时只指定了计算机名,则 SQL Server 客户端组件将尝试连接这台计算机上的数据库引擎默认实例。这保留了与现有 SQL Server 应用程序的兼容性。 一台计算机上只能有一个默认实例,而默认实例可以是 SQL Server 的任何版本。 +计算机名是可以修改的,但修改后对默认实例无影响,即默认实例随计算机名的改变而改变,所以说,默认实例的名称是与计算机名相同,而不是称为"local",但一般情况下,如果要访问本机上的默认SQL服务器实例,使用计算机名、(local)、localhost、127.0.0.1、. 、本机IP地址,都可以达到相同的目的。但如果要访问非本机的SQL服务器,那就必须使用计算机/实例名的办法。 + +## 命名实例 + +除默认实例外,所有数据库引擎实例都由安装该实例的过程中指定的实例名标识。应用程序必须提供准备连接的计算机的名称和命名实例的实例名。计算机名和实例名以格式 computer_name\instance_name 指定。 一台计算机上可以运行多个命名实例,但只有 SQL Server 2000 数据库引擎才可作为命名实例运行。SQL Server 早期版本中的数据库引擎不能作为命名实例运行。 + +# 共享组件 + +以下组件可由运行于同一台计算机上的所有实例共享: 此计算机上只有一个 SQL Server 2000 程序组 (Microsoft SQL Server),以及由该程序组中的每个图标表示的唯一一个实用工具的复本、唯一一个 SQL Server 联机丛书的复本。 +程序组中的实用工具版本来自计算机上最先安装的 SQL Server 2000 版本。例如,如果将 SQL Server 2000 简体中文版作为默认实例安装,然后将 SQL Server 2000 美国英语版作为命名实例安装,则只有一个 SQL Server 2000 程序组。该程序组中的所有实用工具图标和 SQL Server 联机丛书图标都将启动简体中文版的工具。 +所有 SQL Server 2000 实用工具都能处理多个实例。可以从一个 SQL Server 2000 服务管理器的复本启动和停止每个实例。可使用一个 SQL Server 2000企业管理器复本控制计算机上所有实例中的对象,使用一个 SQL Server 2000 服务器网络管理器复本管理计算机上所有实例的网络通讯地址。 +仅有一个 MSSearchService 复本管理针对计算机上所有 SQL Server 实例的全文检索。 +English Query 和 Microsoft SQL Server 2000 Analysis Services 服务器都只有一个复本。 +与客户端软件相关联的注册表键在实例间不重复。 +只有一个 SQL Server 开发库(包括 *.lib 文件)和示例应用程序的复本。 + +# 查看机器上所装实例 + +开始---运行, 输入 services.msc, 回车。在服务列表里, 找到 MSSQLSERVER 开头的, 有几个,就是几个实例。 + +# 更改SQL实例名 + +下面是从网上找到的一个方法,稍微复杂些,以后安装一定要注意了,否则带来很多麻烦.但更名方法亦是十分重要,应掌握. + 重新命名服务器的过程非常简单。你所需要做的就是给服务器改名,就像平时为Windows NT或Windows 2000 Server改名那样。重新启动计算机,然后系统会给出一个出错信息:“安装文件被破坏,或者未知的包标识”。看起来很吓人。但实际上你只需要重新运行SQL Server的安装程序,会有提示“是否升级到当前版本”。点击Yes,安装程序很快就结束了。实际上并没有进行重新安装,只是重新设置了有关服务器名字方面的选项。然后我们需要运行一些存储过程重置服务器名字,以便让SQL Server中的一些函数,例如有关复制的函数,能够有效地工作。 + 我们切换到示范计算机,这将是我们的最后一个示范。先关掉一些刚才启动的应用程序。到“我的电脑”属性,转到“网络标示”的属性,改变这台计算机的名字。我们把这台计算机的名字改为“Win2kSQL”,点击OK。然后提示需要重新启动计算机。OK,重新启动。重新启动后,我们回到了登录界面。正如我们刚才已经提到的,有提示说“安装文件被破坏,或者未知的包标识”。我们现在需要登录到服务器上,转到SQL Server安装文件的目录,然后重新运行安装程序。 现在我们到SQL Server安装文件的目录,运行安装程序。NT有提示说有SQL Server服务不能正常运行。需要等一会儿安装程序才能继续。Windows NT提示说服务控制失败。现在安装程序可以继续了,接下来安装程序询问安装到哪里。我们选择本地安装。然后是搜索已安装组件。下面安装程序询问是否升级到标准版。这个提示看起来比较奇怪,但这正是我们现在想要的。点击Yes,SQL Server将更新一些服务器设置。现在运行完毕,我们返回并启动SQL Server服务。 打开Query Ananlyzer,我们将装入最后一个脚本。转到脚本目录,打开这个叫做“Rename SQL”的脚本。如果我们现在打开sysservers表的话,我们就会发现它仍然是指向原先的服务器名。我们需要把这个服务器先删除,然后再添加,以便让sysservers表能够反映新的服务器名。现在我们来运行这段脚本,删除原先的服务器,然后再添加。现在我们就已经成功改变了服务器的名字 + --打开修改系统表的开关 + EXEC sp_configure 'allow updates',1 RECONFIGURE WITH OVERRIDE + + update master..sysservers set srvname='新服务器名',datasource='新服务器名',srvnetname='新服务器名' where srvname='旧服务器名' + + --关闭修改系统表的开关 + EXEC sp_configure 'allow updates',0 RECONFIGURE WITH OVERRIDE + go + +# 删除 SQL Serve实例 + +已经安装SQLServer实例,想把原来的SQLExpress实例删除。 第一步:进“控制面板”,打开“添加\删除程序”; 第二步:打开 sql server 2008 卸载程序(三个选择:添加、修复、删除,选中删除就好了); 第三步:在配置窗口右侧选择你要删除的 数据引擎 ; 第四步:勾选所有选择(为了彻底清除); 第五步:下一步 到结束; 第六步:等待(大概15 分钟左右)。 \ No newline at end of file diff --git "a/_drafts/Database/DB2\345\270\270\347\224\250\350\257\255\345\217\245.md" "b/_drafts/Database/DB2\345\270\270\347\224\250\350\257\255\345\217\245.md" new file mode 100644 index 0000000..9831c0f --- /dev/null +++ "b/_drafts/Database/DB2\345\270\270\347\224\250\350\257\255\345\217\245.md" @@ -0,0 +1,75 @@ + +```sql +CREATE TABLE STAFF_BAK LIKE STAFF; +INSERT INTO STAFF_BAK SELECT * FROM STAFF; + +SELECT * FROM STAFF_BAK; + +SELECT * FROM STAFF_BAK FETCH FIRST 10 ROWS ONLY; + +DELETE FROM STAFF_BAK; +DROP TABLE STAFF_BAK; + +CREATE TABLE STAFF_BAK AS (SELECT * FROM STAFF) DEFINITION ONLY; + +-- 创建物化表 +CREATE TABLE STAFF_BAK AS (SELECT * FROM STAFF) +DATA INITIALLY DEFERRED REFRESH DEFERRED; +REFRESH TABLE STAFF_BAK; + +-- 系统表说明 + +-- SYSIBM 基本表,对于db2使用进行最优化 + +-- 存放系统中check约束的信息,系统为每个表的每一个check约束建立一条记录 +SELECT * FROM SYSIBM.SYSCHECKS; + +-- 存放系统中所有表的数据列的描述信息,系统为db2里定义的每个表的每一列建立一条记录 +SELECT * FROM SYSIBM.SYSCOLUMNS; + +-- 为每个索引建立一条记录 +SELECT * FROM SYSIBM.SYSINDEXES; + +-- 系统为每一个表,视图和别名在该表中创建一行记录 +SELECT * FROM SYSIBM.SYSTABLES; + +-- 每个plan有一条记录 +SELECT * FROM SYSIBM.SYSPLAN; + +SELECT * FROM SYSIBM.SYSNODEGROUPDEF; + +-- SYSCAT 基于SYSIBM表的视图,对平常轻负荷使用进行优化 + +-- 这两个视图显示被注册的包装器和它们特定的选项 +SELECT * FROM SYSCAT.WRAPPERS; +SELECT * FROM SYSCAT.WRAPOPTIONS; + + +-- 这两个视图显示被注册的远程数据源和它们的特定选项 +SELECT * FROM SYSCAT.SERVERS; +SELECT * FROM SYSCAT.SERVEROPTIONS; + + +-- 这个视图显示被注册的一个db2用户用于特定服务器的用户认证 +SELECT * FROM SYSCAT.USEROPTIONS; + +-- 这个视图显示分区表信息 +SELECT * FROM SYSCAT.DATAPARTITIONS; + +-- SYSSTAT 数据库分析 + + +CREATE DATABASE 'JXDB'; + +-- 创建分区表 +CREATE TABLE MUSIC(ID INTEGER,NAME VARCHAR(20),STYLE VARCHAR(20),STYLE_CODE INTEGER) +PARTITION BY RANGE(STYLE_CODE) +(PART P1 STARTING '1',PART P2 STARTING '2',PART P3 STARTING '3',PART P4 STARTING '4' ENDING MAXVALUE); + +INSERT INTO MUSIC VALUES(1,'HAVE A NICE DAY','POP',2); +INSERT INTO MUSIC VALUES(2,'WE WILL ROCK YOU','ROCK',1); + +DESCRIBE DATA PARTITIONS FOR TABLE MUSIC SHOW DETAIL; + +SELECT DATAPARTITIONNAME,TABNAME,AVGROWSIZE FROM SYSCAT.DATAPARTITIONS WHERE TABNAME = 'MUSIC'; +``` \ No newline at end of file diff --git "a/_drafts/Database/MYSQL\345\270\270\350\247\201\351\224\231\350\257\257\345\217\212\345\205\266\350\247\243\345\206\263\346\226\271\345\274\217.md" "b/_drafts/Database/MYSQL\345\270\270\350\247\201\351\224\231\350\257\257\345\217\212\345\205\266\350\247\243\345\206\263\346\226\271\345\274\217.md" new file mode 100644 index 0000000..89cd1ad --- /dev/null +++ "b/_drafts/Database/MYSQL\345\270\270\350\247\201\351\224\231\350\257\257\345\217\212\345\205\266\350\247\243\345\206\263\346\226\271\345\274\217.md" @@ -0,0 +1,74 @@ + +# ERROR 1130: Host 10.0.0.1 is not allowed to connect to this MySQL server + +在用远程连接MySQL服务器的数据库,不管怎么弄都是连接不到,错误代码是1130,ERROR 1130: Host 10.0.0.1 is not allowed to connect to this MySQL server +猜想是无法给远程连接的用户权限问题。结果这样子操作MySQL库,即可解决。在本机登入MySQL后,更改 “mysql” 数据库里的 “user” 表里的 “host” 项,从”localhost”改称'%'。 + +```SQL +mysql -u root -p +use mysql; +select `Host`, `User` from `user` where `User` = 'root'; +update user set host = '%' where user ='root'; +flush privileges; +select `Host`, `User` from `user` where `User` = 'root'; +``` +第一句是以权限用户root登录;第二句:选择mysql库;第三句:查看mysql库中的user表的host值;第四句:修改host值(以通配符%的内容增加主机/IP地址),当然也可以直接增加IP地址;第五句:刷新MySQL的系统权限相关表;第六句:再重新查看user表时。重启mysql服务即可完成。 + +# ERROR 1044 (42000):Access denied for user + +这个问题主要是因为授权用户本身的权限不足引起的。我们以root用户为例,需要注意到地方有以下几个方面: + +* MySQL的user表很重要。必须保证root用户在user表里面有两条记录,也就是 +root localhost …….. +root 127.0.0.1 ……. + +* 保证root用户拥有所有权限,也就是user表里面的所有字段里面对应的内容是Y +* 在my.ini后者my.cnf里面有这个配置项的时候 +bind-address=localhost +启用这个配置项可以保证安全 + +# Error: 1265 SQLSTATE: 01000 (WARN_DATA_TRUNCATED + +* 字符长度太短; +* 乱码,更改统一的字符类型,比如更改字符类型为utf8; +* 如果是 Enum,则可能是添加的字符不在enum类型范围内; +* 另一可能是在alter table更改列设置时,影响原来存入的值,这时可将原值update为需要的类型值或删除这些原值再alter table + +# error while loading shared libraries: libtinfo.so.5 + +ncurses包(ncurses-libs-5.6)已经安装,运行mysql时仍然提示: + +mysql: error while loading shared libraries: libtinfo.so.5: cannot open shared object file: No such file or directory + +此时只需要创建软连接即可,创建命令如下: + +``` +ln -s /usr/lib/libncurses.so.5 /lib/libtinfo.so.5 +``` + +其中`libncurses.so.5`到底在哪个目录,不同的OS可能有所不同(比如SUSE X64就是在`/lib64`目录下),可以尝试使用`ldd mysql`命令查看mysql依赖的其它库在哪个目录,然后在对应目录查找是否有`libncurses.so.5` + +# No curses/termcap library found + +源码安装MySQL 5.1.30,在./configure阶段报错如下: + +``` +checking for tgetent in -lncurses... no +checking for tgetent in -lcurses... no +checking for tgetent in -ltermcap... no +checking for tgetent in -ltinfo... no +checking for termcap functions library... configure: error: No curses/termcap library found +``` + +原因是缺少ncurses的相关库,按照下面方式安装ncurses即可: + +```sh +# RedHat系列 +yum list|grep ncurses +yum -y install ncurses-devel +yum install ncurses-devel + +# Debian系列 +apt-cache search ncurses +apt-get install libncurses5-dev +``` \ No newline at end of file diff --git "a/_drafts/Database/MySQL\351\273\230\350\256\244\346\225\260\346\215\256\345\272\223.md" "b/_drafts/Database/MySQL\351\273\230\350\256\244\346\225\260\346\215\256\345\272\223.md" new file mode 100644 index 0000000..6eff9dd --- /dev/null +++ "b/_drafts/Database/MySQL\351\273\230\350\256\244\346\225\260\346\215\256\345\272\223.md" @@ -0,0 +1,9 @@ + +INFORMATION_SCHEMA:提供了访问数据库元数据的方式。 +元数据是关于数据的数据,如数据库名、表名、列的数据类型或访问权限等。有些时候用于表述该信息的其他术语包括“数据词典”和“系统目录”。你可以讲INFORMATION_SCHEMA看成一个信息数据库,其中保存着关于MySQL服务器所维护的所有其他数据库的信息。在INFORMATION_SCHEMA中,有数个只读表。它们实际上是视图,而不是基本表,因此你将无法看到与之相关的任何文件。每位MySQL用户均有权访问这些表,但仅限于表中的特定行,在这类行中含有用户具有恰当访问权限的对象。 + +PERFORMANCE_SCHEMA:主要用于收集数据库服务器性能参数。MySQL用户是不能创建存储引擎为PERFORMANCE_SCHEMA的表。performance_schema提供以下功能:提供进程等待的详细信息,包括锁、互斥变量、文件信息;保存历史的事件汇总信息,为提供MySQL服务器性能做出详细的判断;对于新增和删除监控事件点都非常容易,并可以随意改变mysql服务器的监控周期,例如(CYCLE、MICROSECOND)。通过以上得到的信息,DBA能够较明细得了解性能降低可能是由于哪些瓶颈。 + +mysql:这个是mysql的核心数据库,类似于sql server中的master表,主要负责存储数据库的用户、权限设置、关键字等mysql自己需要使用的控制和管理信息。不可以删除,如果对mysql不是很了解,也不要轻易修改这个数据库里面的表信息。 + +test:这个是安装时候创建的一个测试数据库,和它的名字一样,是一个完全的空数据库,没有任何表,可以删除。 \ No newline at end of file diff --git "a/_drafts/Database/Oracle\344\270\255session\345\222\214processes\347\232\204\350\256\276\347\275\256.md" "b/_drafts/Database/Oracle\344\270\255session\345\222\214processes\347\232\204\350\256\276\347\275\256.md" new file mode 100644 index 0000000..3eb9fe0 --- /dev/null +++ "b/_drafts/Database/Oracle\344\270\255session\345\222\214processes\347\232\204\350\256\276\347\275\256.md" @@ -0,0 +1,45 @@ + +* PROCESSES: http://docs.oracle.com/cd/B28359_01/server.111/b28320/initparams188.htm#sthref560 +* SESSIONS: http://docs.oracle.com/cd/B28359_01/server.111/b28320/initparams220.htm#sthref647 +* TRANSACTIONS: http://docs.oracle.com/cd/B28359_01/server.111/b28320/initparams248.htm + +* Oracle 11gR2之前:sessions=(1.1*processes) + 5 +* Oracle 11gR2之后:sessions=(1.5*porcesses) + 22 + +当Oracle需要启动新的process而又已经达到processes参数时,就会报错: + +```shell + 00020, 00000, "maximum number of processes (%s) exceeded" + // *Cause: All process state objects are in use. + // *Action: Increase the value of the PROCESSES initialization parameter. +``` + +当数据库连接的并发用户已经达到sessions这个值时,又有新session连进来,就会报错 + +```shell + 00018, 00000, "maximum number of sessions exceeded" + // *Cause: All session state objects are in use. + // *Action: Increase the value of the SESSIONS initialization parameter. +``` + +如何使用sqlplus查看、修改processes呢?使用sys,以sysdba权限登录: + +```shell + show parameter processes; --显示:processes integer 150 + show parameter sessions; --显示:sessions integer 165 + select count(*) from v$process; --显示当前processes数目 + select count(*) from v$session; --显示当前sessions数目 + alter system set processes=400 scope = spfile; --显示系统已更改 + show parameter processes; --显示:processes integer 150 + create pfile from spfile; --显示:文件已创建。 + + --重启数据库 + shutdown immediate; + startup + + --重启监听 + lsnrctl stop/start/status + + show parameter processes; --显示:processes integer 400 + show parameter session; --显示:sessions integer 445 +``` \ No newline at end of file diff --git "a/_drafts/Database/Oracle\345\257\274\345\205\245\345\257\274\345\207\272\345\270\270\347\224\250\345\221\275\344\273\244.md" "b/_drafts/Database/Oracle\345\257\274\345\205\245\345\257\274\345\207\272\345\270\270\347\224\250\345\221\275\344\273\244.md" new file mode 100644 index 0000000..c688de5 --- /dev/null +++ "b/_drafts/Database/Oracle\345\257\274\345\205\245\345\257\274\345\207\272\345\270\270\347\224\250\345\221\275\344\273\244.md" @@ -0,0 +1,18 @@ +```sql +-- 全量导出 +exp system/manager@TEST file=d:\daochu.dmp full=y + +-- 将数据库中system用户与sys用户的表导出 +exp system/manager@TEST file=d:\daochu.dmp owner=(system,sys) + +-- 将数据库中的表table1中的字段filed1以"00"打头的数据导出 +exp system/manager@TEST file=d:\daochu.dmp tables=(table1) query=\" where filed1 like '00%'\" + +-- 将数据库中的表table1/table2导出 +exp system/manager@TEST file=d:\daochu.dmp tables=(table1,table2) + + +-- 将d:\daochu.dmp中的表table1/table2导入 +-- 如果在Linux下导入导出,需要使用 tables="(table1,table2)",否则报错"syntax error near unexpected token(" +imp system/manager@TEST file=d:\daochu.dmp tables=(table1,table2) +``` \ No newline at end of file diff --git "a/_drafts/Database/Oracle\347\263\273\347\273\237\350\241\250\346\225\264\347\220\206+\345\270\270\347\224\250SQL\350\257\255\345\217\245\346\224\266\351\233\206.md" "b/_drafts/Database/Oracle\347\263\273\347\273\237\350\241\250\346\225\264\347\220\206+\345\270\270\347\224\250SQL\350\257\255\345\217\245\346\224\266\351\233\206.md" new file mode 100644 index 0000000..8e39d61 --- /dev/null +++ "b/_drafts/Database/Oracle\347\263\273\347\273\237\350\241\250\346\225\264\347\220\206+\345\270\270\347\224\250SQL\350\257\255\345\217\245\346\224\266\351\233\206.md" @@ -0,0 +1,447 @@ + +``` +-- DBA/ALL/USER/V_$/GV_$/SESSION/INDEX开头的绝大部分都是视图 +-- DBA_TABLES意为DBA拥有的或可以访问的所有的关系表。 +-- ALL_TABLES意为某一用户拥有的或可以访问的所有的关系表。 +-- USER_TABLES意为某一用户所拥有的所有的关系表。 +-- 当某一用户本身就为数据库DBA时,DBA_TABLES与ALL_TABLES等价。 +-- DBA_TABLES >= ALL_TABLES >= USER_TABLES +-- 需要注意的是在ORACLE数据库中大小写是敏感的,而此三表中数据默认都是大写的,所以在进行查询的时候注意小写的数据可能会造成数据无法查到。 + +SELECT * FROM dba_views WHERE view_name LIKE 'DBA%'; +SELECT * FROM dba_views WHERE view_name LIKE 'ALL%'; +SELECT * FROM dba_views WHERE view_name LIKE 'USER%'; +SELECT * FROM dba_views WHERE view_name LIKE 'V_$%'; -- 针对某个实例的视图 +SELECT * FROM dba_views WHERE view_name LIKE 'GV_$%'; -- 全局视图,针对多个实例环境 +SELECT * FROM dba_views WHERE view_name LIKE 'SESSION%'; +SELECT * FROM dba_views WHERE view_name LIKE 'INDEX%'; + +SELECT count(1) FROM dba_tables; +SELECT count(1) FROM all_tables; +SELECT count(1) FROM user_tables; + +-- V$/GV$开头的绝大部分都是V_$/GV_$表的别名 +SELECT * FROM dba_synonyms WHERE synonym_name LIKE 'V$%'; +SELECT * FROM dba_synonyms WHERE synonym_name LIKE 'GV$%'; + +-- X$没有对应的X_$ +SELECT * FROM dba_synonyms WHERE synonym_name LIKE 'X$%'; + + +-- 比较常用的DBA开头的视图有 +select * from dba_users; --数据库用户信息 +select * from dba_roles; --角色信息 +select * from dba_segments; --表段信息 +select * from dba_extents; --数据区信息 +select * from dba_objects; --数据库对象信息 +select * from dba_lobs; --lob数据信息 +select * from dba_tablespaces; --数据库表空间信息 +select * from dba_data_files; --数据文件设置信息 +select * from dba_temp_files; --临时数据文件信息 +select * from dba_rollback_segs; --回滚段信息 +select * from dba_ts_quotas; --用户表空间配额信息 +select * from dba_free_space; --数据库空闲空间信息 +select * from dba_profiles; --数据库用户资源限制信息 +select * from dba_sys_privs; --用户的系统权限信息 +select * from dba_tab_privs; --用户具有的对象权限信息 +select * from dba_col_privs; --用户具有的列对象权限信息 +select * from dba_role_privs; --用户具有的角色信息 +select * from dba_audit_trail; --审计跟踪记录信息 +select * from dba_stmt_audit_opts; --审计设置信息 +select * from dba_audit_object; --对象审计结果信息 +select * from dba_audit_session; --会话审计结果信息 +select * from dba_indexes; --用户模式的索引信息 + + +-- 比较常用的ALL开头的视图有 +select * from all_users; --数据库所有用户的信息 +select * from all_objects; --数据库所有的对象的信息 +select * from all_def_audit_opts; --所有默认的审计设置信息 +select * from all_tables; --所有的表对象信息 +select * from all_indexes; --所有的数据库对象索引的信息 +select * from all_tab_comments; --查询所有用户的表,视图等 +select * from all_col_comments; --查询所有用户的表的列名和注释. +select * from all_tab_columns; --查询所有用户的表的列名等信息(详细但是没有备注) + + +-- 比较常用的ALL开头的视图有 +select * from user_objects; --用户对象信息 +select * from user_source; --数据库用户的所有资源对象信息 +select * from user_segments; --用户的表段信息 +select * from user_tables; --用户的表对象信息 +select * from user_tab_columns; --用户的表列信息 +select * from user_constraints; --用户的对象约束信息 +select * from user_sys_privs; --当前用户的系统权限信息 +select * from user_tab_privs; --当前用户的对象权限信息 +select * from user_col_privs; --当前用户的表列权限信息 +select * from user_col_comments; -- 查询本用户的表的列名和注释 +select * from user_role_privs; --当前用户的角色权限信息 +select * from user_indexes; --用户的索引信息 +select * from user_ind_columns; --用户的索引对应的表列信息 +select * from user_cons_columns; --用户的约束对应的表列信息 +select * from user_clusters; --用户的所有簇信息 +select * from user_clu_columns; --用户的簇所包含的内容信息 +select * from user_cluster_hash_expressions; --散列簇的信息 + + +-- 比较常用的V$开头的别名有 +select * from v$database; --数据库信息 +select * from v$datafile; --数据文件信息 +select * from v$controlfile; --控制文件信息 +select * from v$logfile; --重做日志信息 +select * from v$instance; --数据库实例信息 +select * from v$log; --日志组信息 +select * from v$loghist; --日志历史信息 +select * from v$sga; --数据库SGA信息 +select * from v$parameter; --初始化参数信息 +select * from v$process; --数据库服务器进程信息 +select * from v$bgprocess; --数据库后台进程信息 +select * from v$controlfile_record_section; --控制文件记载的各部分信息 +select * from v$thread; --线程信息 +select * from v$datafile_header; --数据文件头所记载的信息 +select * from v$archived_log; --归档日志信息 +select * from v$archive_dest; --归档日志的设置信息 +select * from v$logmnr_contents; --归档日志分析的DML DDL结果信息 +select * from v$logmnr_dictionary; --日志分析的字典文件信息 +select * from v$logmnr_logs; --日志分析的日志列表信息 +select * from v$tablespace; --表空间信息 +select * from v$tempfile; --临时文件信息 +select * from v$filestat; --数据文件的I/O统计信息 +select * from v$undostat; --Undo数据信息 +select * from v$rollname; --在线回滚段信息 +select * from v$session; --会话信息 +select * from v$transaction; --事务信息 +select * from v$rollstat; --回滚段统计信息 +select * from v$pwfile_users; --特权用户信息 +select * from v$sqlarea; --当前查询过的sql语句访问过的资源及相关的信息 +select * from v$sql; --与v$sqlarea基本相同的相关信息 +select * from v$sysstat; --数据库系统状态信息 + +-- 比较常用的SESSION开头的视图有 +select * from session_roles; --会话的角色信息 +select * from session_privs; --会话的权限信息 + +-- 比较常用的INDEX开头的视图有 +select * from index_stats; --索引的设置和存储信息 + +-- 伪表,参考oracle 中 dual 详解:http://blog.csdn.net/ozhouhui/article/details/7935196 +select * from dual; --系统伪列表信息 +select sysdate from dual; --可将Sysdate视为一个其结果为当前日期和时间的函数,在任何可以使用Oracle函数的地方都可以使用Sysdate。也可以将它视为每个表的一个隐藏的列或伪列。 +select current_date from dual; --报告会话的时区中的系统日期。注:可以设置自己的时区,以区别于数据库的时区。 +select SYSTIMESTAMP from dual; --报告TIMESTAMP数据类型格式的系统日期。 + + +-- 系统权限 +-- GRANTEE 接受该权限的用户名 +-- OWNER 对象的拥有者 +-- GRANTOR 赋予权限的用户 +SELECT * FROM dba_sys_privs WHERE grantee = 'SYS'; +SELECT * FROM dba_sys_privs WHERE grantee = 'CONNECT'; +SELECT * FROM dba_sys_privs WHERE grantee = 'RESOURCE'; + +-- 角色权限 +-- 查看某个用户有哪些角色 +select * from dba_role_privs where grantee='SYS'; +-- 查看某个角色被赋予了哪些用户 +SELECT * FROM dba_role_privs WHERE granted_role = 'DBA'; + +-- 对象权限 +SELECT * FROM dba_tab_privs; + +-- 授予某个用户某些角色 +GRANT connect,resource TO 'USER'; +GRANT dba to 'USER'; --给普通用户授予dba角色时,要重新连接才能生效 +REVOKE dba to 'USER'; +-- 直接授予某个用户某些权限 +GRANT CREATE VIEW TO 'USER'; + +-- 查看某个系统用户是否有SYSDBA或者SYSOPER权限 +-- oracle:DBA,SYSDBA,SYSOPER三者的区别:http://blog.chinaunix.net/uid-22457844-id-3045741.html +select * from V$PWFILE_USERS; + +-- 锁定、解锁用户 +SELECT * FROM dba_users WHERE username = 'SCOTT'; +ALTER USER SCOTT account LOCK; --锁定用户 +ALTER USER SCOTT account UNLOCK; --解锁用户 +COMMIT; + +-- oracle10g 修改用户密码: http://blog.163.com/benbenfafa_88/blog/static/64930162200972594612972/ +-- User Default Password Check in Oracle 11g: http://www.dbform.com/html/2009/673.html +SELECT password FROM dba_users WHERE username = 'SCOTT'; +alter user SCOTT identified by new_password; --修改用户密码 + + +-- SERVICE_NAMES: http://docs.oracle.com/database/121/REFRN/GUID-AC956707-D568-4F8A-BF2E-99BA41E0A64F.htm#REFRN10194 +SELECT * FROM global_name; -- 查看oracle的全局数据库名 +SELECT * FROM v$database; -- 查看数据库名 show parameter db_name; + +-- 数据库实例名对应着SID +-- SID: http://docs.oracle.com/database/121/LADBI/glossary.htm#LADBI8021 +-- linux下在配置oracle环境变量的情况可以使用 echo $ORACLE_SID,如果没有可以使用ps -ef |grep oracle 来查询,结果中的xxxx就是对应的SID。 +-- oracle 2548 1 0 Aug17 ? 00:00:00 ora_pmon_xxxx +-- 在windows环境下,oracle是以后台服务的方式被管理的,所以看"控制面板->管理工具->服务 里面的名称:"OracleServiceORCL",则ORCL就是sid; +SELECT * FROM v$instance; --查看数据库实例名 show parameter instance_name; +select instance from v$thread; + +-- show parameter是oracle的命令,不是标准SQL语句 +-- 可以在sqlplus或者pl/sql dev的命令窗口执行 +-- show parameter aaaa;等价于SELECT * FROM v$parameter WHERE name like '%aaaa%'; +SELECT * FROM v$parameter WHERE name like '%name%'; --等价于show parameter name; +select * from v$parameter where name like '%db_domain%'; --查询数据库域名 + + +select username from all_users where username like '%SCOTT%'; +drop user SCOTT cascade; +commit; + +-- ERROR at line 1: +-- ORA-01940: cannot drop a user that is currently connected + +select 'ALTER SYSTEM KILL SESSION '||''''||SID||','||SERIAL#||''''||';' as KILLER from v$session where username='SCOTT'; +-- KILLER +-- ALTER SYSTEM KILL SESSION '363,35'; +-- ALTER SYSTEM KILL SESSION '364,51'; +commit; + +select * from dba_roles where role like '%CONNECT%'; +drop role CONNECT; +commit; + +select * from dba_tablespaces where tablespace_name like 'EXAMPLE'; +drop tablespace EXAMPLE including contents and datafiles cascade constraints ; +-- including contents 删除表空间中的内容,如果删除表空间之前表空间中有内容,而未加此参数,表空间删不掉,所以习惯性的加此参数。 +-- including datafiles 删除表空间中的数据文件。 +-- cascade constraints 同时删除 tablespace 中表的外键参照。 + + +-- 如何创建dblink和视图 +-- http://docs.oracle.com/database/121/SQLRF/statements_5006.htm#i2061505 +-- 如果需要创建全局 DBLink,则需要先确定用户有创建 dblink 的权限: +select * from user_sys_privs where privilege like upper('%DATABASE LINK%'); + +-- 如果没有,则需要使用 sysdba 角色给用户赋权: +grant create public database link to dbusername; + +-- 如果创建全局 dblink,必须使用 systm 或 sys 用户,在 database 前加 public。 +create /* public */ database link dblink1 +connect to dbusername identified by dbpassword +using '(DESCRIPTION =(ADDRESS_LIST =(ADDRESS =(PROTOCOL = TCP)(HOST = 192.168.0.1)(PORT = 1521)))(CONNECT_DATA =(SERVICE_NAME = orcl)))'; + +-- 创建dblink后,就可以直接在dblink上创建视图 +create or replace view cptp as (select SJDH from dbusername.cptp@dblink1); drop view cptp; + + +-- 锁表查询SQL +SELECT object_name, machine, s.sid, s.serial# +FROM gv$locked_object l, dba_objects o, gv$session s +WHERE l.object_id = o.object_id +AND l.session_id = s.sid; + +-- 解除锁表 +alter system kill session 'sid, serial#'; + + +-- 备份某个表 +create table new_table as select * from old_table; + + +-- 查看数据库是否在rac环境的集群中的 +show parameter cluster_database; +select * from v$parameter where name = 'cluster_database'; + + +-- 列操作 +-- 增加和修改列不需要加关键字COLUMN +-- 删除单列的话,一定要加COLUMN,删除多列的时候,不能加COLUMN关键字 + +-- 增加一列 +alter table emp4 add test varchar2(10); +-- 修改一列 +alter table emp4 modify test varchar2(20); +-- 删除一列 +alter table emp4 drop column test; +-- 增加多列 +alter table emp4 add (test varchar2(10),test2 number); +-- 修改多列 +alter table emp4 modify (test varchar2(20),test2 varchar2(20)); +-- 删除多列 +alter table emp4 drop (test,test2); + + +-- Windows下以管理员身份启动数据库 +net start oracleserviceorcl -- 后面的orcl是你安装的数据库实例名 +net start oracleoradb11g_home1tnslistener --非必须 + +-- linux下以sysdba用户登录,然后启动数据库 +sqlplus / as sysdba +startup + +-- sqlplus登陆方式 +sqlplus / as sysdba --以操作系统权限认证的oracle sys管理员登陆 + +sqlplus /nolog +conn / as sysdba --以操作系统权限认证的oracle sys管理员登陆 + + +sqlplus sys/password@orcl as sysdba --以sys用户登陆必须使用as sysdba + +sqlplus /nolog --不在cmd或者teminal当中暴露密码的登陆方式 +conn sys/password as sysdba + + +sqlplus --不显露密码的方式登陆 +Enter user-name:sys +Enter password:password as sysdba --以sys用户登陆的话 必须要加上as sysdba子句 + +sqlplus scott/tiger@orcl --非管理员用户登陆 + + +desc v$database; --查询v$database数据库的表结构 + + + +--在sqlplus中执行sql脚本,下面两种方式都可以 +START file_name +@file_name + + +--判断表是否存在,如果存在则删除 +declare + num number; +begin + select count(1) into num from all_tables where TABLE_NAME = 'EMP' and OWNER='SCOTT'; + if num=1 then + execute immediate 'drop table EMP'; + end if; +end; +/ +--创建表 +CREATE TABLE EMP + (EMPNO NUMBER(4) NOT NULL, + ENAME VARCHAR2(10), + JOB VARCHAR2(9), + MGR NUMBER(4), + HIREDATE DATE, + SAL NUMBER(7, 2), + COMM NUMBER(7, 2), + DEPTNO NUMBER(2)); +可以将上述存储过程加载到每一个create table前面。 + +--ORACLE 判断序列是否存在,如果存在就删除 + +declare + V_NUM number; + +BEGIN + ----多次删除时,每次都将v_num设置成为0 + V_NUM := 0; + ----判断序列 seq_name_1 是否存在(区分大小写) + select count(0) into V_NUM from user_sequences where sequence_name = 'SEQ_BUSINESS_PROCESS_INDEX_ID'; + ----如果存在立即删除 + if V_NUM > 0 then + execute immediate 'DROP SEQUENCE SEQ_BUSINESS_PROCESS_INDEX_ID'; + end if; +END; + + +-- 设置sqlplus模式显示总行数 +show pagesize; --查看当前的pagesize +set pagesize 300; + +-- 设置sqlplus模式显示行宽度 +show linesize; --查看当前的linesize +set linesize 300; + +-- 修改安装目录glogin.sql文件才能保证之前的设置永久生效 +set pagesize 300; +set linesize 300; + + + + +-- 删除表对象 +select 'drop table '||segment_name from dba_segments where owner='VPMUSER' and segment_type='TABLE'; +-- 创建表对象 +select +'create table '||segment_name || ' as select * from '||segment_name ||'@DBLINK' +from dba_segments where owner='VPMUSER' and segment_type='TABLE'; + +-- 检查表是否完整导入 +select segment_name from dba_segments@aaa where owner='VPMUSER' and segment_type='TABLE' +and (segment_name not like 'BIN$%' +and segment_name not like '%201%') +minus +select segment_name from dba_segments where owner='VPMUSER' and segment_type='TABLE' and segment_name not like 'BIN$%' + + +--查询用户所有表的语句1 +select t.table_name,t.comments from user_tab_comments t + +--查询用户所有表的语句2: +select r1, r2, r3, r5 +from (select a.table_name r1, a.column_name r2, a.comments r3 + from user_col_comments a), + (select t.table_name r4, t.comments r5 from user_tab_comments t) +where r4 = r1 + + +-- 查找表的所有索引(包括索引名,类型,构成列): +select t.*,i.index_type from user_ind_columns t,user_indexes i where t.index_name = i.index_name and t.table_name = i.table_name and t.table_name = 要查询的表 +-- 查找表的主键(包括名称,构成列): +select cu.* from user_cons_columns cu, user_constraints au where cu.constraint_name = au.constraint_name and au.constraint_type = 'P' and au.table_name = 要查询的表 + +-- 查找表的唯一性约束(包括名称,构成列): +select column_name from user_cons_columns cu, user_constraints au where cu.constraint_name = au.constraint_name and au.constraint_type = 'U' and au.table_name = 要查询的表 + +-- 查找表的外键(包括名称,引用表的表名和对应的键名,下面是分成多步查询): +select * from user_constraints c where c.constraint_type = 'R' and c.table_name = 要查询的表 + +-- 查询外键约束的列名: +select * from user_cons_columns cl where cl.constraint_name = 外键名称 + +-- 查询引用表的键的列名: +select * from user_cons_columns cl where cl.constraint_name = 外键引用表的键名 + +-- 查询表的所有列及其属性 +select t.*,c.COMMENTS from user_tab_columns t,user_col_comments c where t.table_name = c.table_name and t.column_name = c.column_name and t.table_name = 要查询的表 + + +--备份表数据 +create table emp as select * from scott.emp + +--还原表数据 +insert into emp select * from scott.emp + +--查看已经执行过的sql这些是存在共享池中的,用户名需要大写,必须具有DBA 的权限 +select * from v$sqlarea t where t.PARSING_SCHEMA_NAME in ('用户名') order by t.LAST_ACTIVE_TIME desc + + +--ORACLE11G 字符集更改(这里更改为AL32UTF8) +sqlplus sys as sysdba + +--执行下面命令,有可能造成数据库中已有数据混乱的情况,所以在进行操作前,要进行数据库的备份操作 +shutdown immediate; +STARTUP MOUNT; +ALTER SESSION SET SQL_TRACE=TRUE; +ALTER SYSTEM ENABLE RESTRICTED SESSION; +ALTER SYSTEM SET JOB_QUEUE_PROCESSES=0; +ALTER SYSTEM SET AQ_TM_PROCESSES=0; +ALTER DATABASE OPEN; +ALTER DATABASE character set INTERNAL_USE AL32UTF8; +ALTER SESSION SET SQL_TRACE=FALSE; +shutdown immediate; +startup; + +--察看 NLS_LANG 信息: +SELECT parameter, value FROM v$nls_parameters WHERE parameter LIKE '%CHARACTERSET'; + + +UPDATE STAFF +SET MODIFY_TIME = TO_DATE('2016/04/22 00:01:00', 'yyyy/MM/dd hh24:mi:ss') +WHERE MODIFY_TIME < TO_DATE('2016/04/22 00:01:00', 'yyyy/MM/dd hh24:mi:ss'); + +UPDATE STAFF +SET MODIFY_TIME = TO_TIMESTAMP('19-03-2008 02:36:00.360000', 'dd-MM-yyyy hh24:mi:ss.ff') +WHERE STAFF_ID = '01'; + +``` \ No newline at end of file diff --git "a/_drafts/Database/SQL Server\345\270\270\347\224\250\350\257\255\345\217\245.md" "b/_drafts/Database/SQL Server\345\270\270\347\224\250\350\257\255\345\217\245.md" new file mode 100644 index 0000000..cefed39 --- /dev/null +++ "b/_drafts/Database/SQL Server\345\270\270\347\224\250\350\257\255\345\217\245.md" @@ -0,0 +1,196 @@ + +```SQL +-- 身份证验证(SQLServer) +-- 主要验证SQL数据库中已输入的15位 及18位 身份证号码的位数、出生年月日是否正确,可以过滤出大部分的输入错误。 +select 身份证号 +from 身份表 +where (len(身份证号)<>15 and len(身份证号)<>18) +or (len(身份证号)=18 and (Substring(身份证号,7,2)<'19' or Substring(身份证号,7,2)>'20' +or (Substring(身份证号,11,2)>12) +or (Substring(身份证号,11,2) in (01,03,05,07,08,10,12) and Substring(身份证号,13,2)>31) +or (Substring(身份证号,11,2) in (04,06,09,11) and Substring(身份证号,13,2)>30) +or (Substring(身份证号,11,2)=02 and Substring(身份证号,13,2)>29))) +---------------------- 下面是针对 15位 及18位 身份证号码性别的验证语句 -------------------- Access 不支持 Substring 查询,可以替换为 mid 查询。 +select 序号,姓名,身份证号,性别 +from 身份表 +where (((len(身份证号)=15) and (Substring(身份证号,15,1) in (1,3,5,7,9)) and 性别<>'男') +or ((len(身份证号)=15) and (Substring(身份证号,15,1) in (2,4,6,8,0)) and 性别<>'女')) +or (((len(身份证号)=18) and (Substring(身份证号,17,1) in (1,3,5,7,9)) and 性别<>'男') +or ((len(身份证号)=18) and (Substring(身份证号,17,1) in (2,4,6,8,0)) and 性别<>'女')) +----------------- 下面是针对 15位 及18位 身份证号码位数与出生年月日的验证 ------------- Access 不支持 Substring 查询,可以替换为 mid 查询 +select 序号,姓名,身份证号,性别 +from 身份表 +where (len(身份证号)<>15 and len(身份证号)<>18) +or (len(身份证号)=15 and ((Substring(身份证号,9,2)>12) +or (Substring(身份证号,11,2) > 31) +or (Substring(身份证号,9,2) in (01,03,05,07,08,10,12) and Substring(身份证号,11,2)>31) +or (Substring(身份证号,9,2) in (04,06,09,11) and Substring(身份证号,11,2)>30) +or (Substring(身份证号,9,2)=02 and Substring(身份证号,11,2)>29))) + + + + +-- 更改列名: +exec sp_rename '表名.原列名','新列名','column'; +exec sp_rename 'student.Ssex','Sex','column'; + + + +-- 成绩统计语句(SQLServer) +CREATE TABLE stuscore +( + id int NOT NULL PRIMARY KEY identity(0000,1), + name varchar(20), + subject varchar(20), + score int, + stuid varchar(10) + ) + +insert into stuscore(name,subject,score,stuid) +select '张三', '数学', 89, '1' UNION ALL +select '张三', '语文', 80, '1' UNION ALL +select '张三', '英语', 70, '1' UNION ALL +select '李四', '数学', 90, '2' UNION ALL +select '李四', '语文', 70, '2' UNION ALL +select '李四', '英语', 80, '2' UNION ALL +select '王五', '数学', 49, '3' UNION ALL +select '王五', '语文', 87, '3' UNION ALL +select '王五', '英语', 90, '3' + +--计算每个人的总成绩并排名 +select name,sum(score) as allscore +from stuscore +group by name +order by allscore desc + +--计算每个人的总成绩并排名 +select distinct t1.name,t1.stuid,t2.allscore +from stuscore t1,(select stuid,sum(score) as allscore from stuscore group by stuid) t2 +where t1.stuid=t2.stuid +order by t2.allscore desc + +--计算每个人单科的最高成绩 +select t1.stuid,t1.name,t1.subject,t1.score +from stuscore t1,(select stuid,max(score) as maxscore from stuscore group by stuid) t2 +where t1.stuid=t2.stuid and t1.score=t2.maxscore + +--计算每个人的平均成绩 +select distinct t1.stuid,t1.name,t2.avgscore +from stuscore t1,(select stuid,avg(score) as avgscore +from stuscore +group by stuid) t2 +where t1.stuid=t2.stuid + +--列出各门课程成绩最好的学生 +select t1.stuid,t1.name,t1.subject,t2.maxscore +from stuscore t1,(select subject,max(score) as maxscore from stuscore group by subject) t2 +where t1.subject=t2.subject and t1.score=t2.maxscore + +--列出各门课程成绩最好的两位学生 +select distinct t1.* +from stuscore t1 +where t1.id in (select top 2 stuscore.id from stuscore where subject = t1.subject order by score desc) +order by t1.subject + +--学号 姓名 语文 数学 英语 总分 平均分 +select + stuid as 学号, + name as 姓名, + sum(case when subject='语文' then score else 0 end) as 语文, + sum(case when subject='数学' then score else 0 end) as 数学, + sum(case when subject='英语' then score else 0 end) as 英语, + sum(score) as 总分, + (sum(score)/count(*)) as 平均分 +from stuscore +group by stuid,name +order by 总分 desc + +--列出各门课程的平均成绩 +select subject,avg(score) as avgscore +from stuscore +group by subject + +--声明变量以便后续调用 +declare @tmp table(pm int,name varchar(50),score int,stuid int) +declare @id int + +--列出数学成绩的排名 +insert into @tmp +select null,name,score,stuid +from stuscore +where subject='数学' +order by score desc +set @id=0 +update @tmp set @id=@id+1,pm=@id +select * from @tmp +select DENSE_RANK () OVER(order by score desc) as row,name,subject,score,stuid +from stuscore +where subject='数学' +order by score desc + +--列出数学成绩在2-3名的学生 +select t3.* +from +( + select top 2 t2.* from(select top 3 name,subject,score,stuid from stuscore where subject='数学'order by score desc) t2 + order by t2.score +) t3 +order by t3.score desc + +--求出李四的数学成绩的排名 +insert into @tmp +select null,name,score,stuid +from stuscore where subject='数学' +order by score desc +set @id=0 +update @tmp +set @id=@id+1,pm=@id +select * from @tmp where name='李四' + +--课程 不及格(-59) 良(-80) 优(-100) +select subject, + (select count(*) from stuscore where score<60 and subject=t1.subject) as 不及格, + (select count(*) from stuscore where score between 60 and 80 and subject=t1.subject) as 良, + (select count(*) from stuscore where score >80 and subject=t1.subject) as 优 +from stuscore t1 +group by subject + +--数学:张三(50分),李四(90分),王五(90分),赵六(76分) +declare @s varchar(1000) +set @s='' +select @s =@s+','+name+'('+convert(varchar(10),score)+'分)' +from stuscore +where subject='数学' +set @s=stuff(@s,1,1,'') +print '数学:'+@s + + + + +-- 怎样删除外键约束(SQLServer) +--测试环境 +--主表 +create table test1(id int primary key not null,value int) +insert test1 select 1,2 +go +--从表 +create table test2(id int references test1(id),value int) +go + +--第一步:找出test2表上的外键约束名字 +--Microsoft SQLServer 2000(及以上) +exec sp_helpconstraint 'test2' +--Microsoft SQLServer 2005(及以上) +select name +from sys.foreign_key_columns f join sys.objects o +on f.constraint_object_id=o.object_id +where f.parent_object_id=object_id('test2') + +--第二步:删除外键约束 +alter table test2 +drop constraint FK__test2__id__08EA5793 + + + + +``` \ No newline at end of file diff --git "a/_drafts/Database/oracle\350\241\250\347\251\272\351\227\264\344\270\215\350\266\263\347\233\270\345\205\263\351\227\256\351\242\230\350\247\243\345\206\263\345\212\236\346\263\225.md" "b/_drafts/Database/oracle\350\241\250\347\251\272\351\227\264\344\270\215\350\266\263\347\233\270\345\205\263\351\227\256\351\242\230\350\247\243\345\206\263\345\212\236\346\263\225.md" new file mode 100644 index 0000000..7a23ccf --- /dev/null +++ "b/_drafts/Database/oracle\350\241\250\347\251\272\351\227\264\344\270\215\350\266\263\347\233\270\345\205\263\351\227\256\351\242\230\350\247\243\345\206\263\345\212\236\346\263\225.md" @@ -0,0 +1,135 @@ + +## oracle 临时表空间和数据表空间 + +Oracle临时表空间主要用来做查询和存放一些缓冲区数据。临时表空间消耗的主要原因是需要对查询的中间结果进行排序。重启数据库可以释放临时表空间,如果不能重启实例,而一直保持问题sql语句的执行,temp表空间会一直增长。直到耗尽硬盘空间。网上有人猜测在磁盘空间的分配上,oracle使用的是贪心算法,如果上次磁盘空间消耗达到1GB,那么临时表空间就是1GB。也就是说当前临时表空间文件的大小是历史上使用临时表空间最大的大小。临时表空间的主要作用: + +* 索引create或rebuild +* Order by 或 group by +* Distinct 操作 +* Union 或 intersect 或 minus +* Sort-merge joins +* analyze + +数据表空间:表空间的作用能帮助DBA用户完成以下工作: + +* 决定数据库实体的空间分配; +* 设置数据库用户的空间份额; +* 控制数据库部分数据的可用性; +* 分布数据于不同的设备之间以改善性能; +* 备份和恢复数据。 + +用户创建其数据库实体时其必须于给定的表空间中具有相应的权力,所以对一个用户来说,其要操纵一个ORACLE数据库中的数据,应该: + +* 被授予关于一个或多个表空间中的RESOURCE特权; +* 被指定缺省表空间; +* 被分配指定表空间的存储空间使用份额; +* 被指定缺省临时段表空间。 + +表空间的维护是由ORACLE数据库系统管理员DBA通过SQL*PLUS语句实现的,其中表空间创建与修改中的文件名是不能带路径的,因此DBA必须在ORACLE/DBS目录中操作。 + + +``` +-- 表空间相关问题 + +--创建永久表空间 +create tablespace tablespace_name +datafile 'PATH_OF_NEW_DATAFILE' +size 50M +AUTOEXTEND ON NEXT 50M; + +--删除表空间 +DROP TABLESPACE TABLESPACE_NAME; + +-- 表空间不足的一般报错信息为: +-- sqlexception:ora-01654: unable to extend index index_name by 1024 in tablespace tablespace_name + +-- 表空间是逻辑概念,数据文件和临时数据文件都是物理感念。 +-- 一个表空间可以有多个数据文件或者临时数据文件。 +SELECT * FROM dba_tablespaces; +SELECT * FROM dba_data_files; + +-- 该表中只有数据文件的使用率,不包含临时数据文件 +SELECT * FROM dba_free_space; + +-- dba_data_files表中同一个tablespace_name可能会有多个数据文件 +-- 使用sum聚集函数,并且添加group by子句可以统计相同的tablesapce_name的多个数据文件的总大小 +select t.tablespace_name, round(sum(d.bytes/(1024*1024)),0) "tablespace_size(M)" +from dba_tablespaces t, dba_data_files d +where t.tablespace_name = d.tablespace_name +group by t.tablespace_name; + +select sum(bytes)/(1024*1024) as free_space,tablespace_name +from dba_free_space +group by tablespace_name; + +-- 查询数据文件实际使用情况(不包含临时数据文件,执行速度比较慢) +select file_name, sum(e.bytes)/1024/1024 as mb +from dba_extents e join dba_data_files f on e.file_id=f.file_id +group by file_name; + + +-- 为某个表空间添加数据文件,文件路径格式参考dba_data_files表中的格式 +alter tablespace tablespace_name add datafile 'PATH_OF_NEW_DATAFILE' size 10m +autoextend ON next 5m maxsize 100m; + +-- 修改已有的数据文件自动增长 +alter database datafile 'PATH_OF_OLD_DATAFILE' +autoextend on next 5m maxsize 100m; + +-- 重新设置已有的数据文件大小,如果是缩小的话不能小于数据文件的实际使用情况 +alter database datafile 'PATH_OF_OLD_DATAFILE' +resize 100m; + + +-- 如何解决临时表空间不足 + +SELECT * FROM dba_temp_files; + +-- 查询临时表空间的使用情况,D.TABLESPACE_NAME = F.TABLESPACE_NAME(+)代表左联接 +SELECT D.TABLESPACE_NAME,SPACE "SUM_SPACE(M)",BLOCKS SUM_BLOCKS, +USED_SPACE "USED_SPACE(M)",ROUND(NVL(USED_SPACE,0)/SPACE*100,2) "USED_RATE(%)", +NVL(FREE_SPACE,0) "FREE_SPACE(M)" +FROM +(SELECT TABLESPACE_NAME,ROUND(SUM(BYTES)/(1024*1024),2) SPACE,SUM(BLOCKS) BLOCKS +FROM DBA_TEMP_FILES +GROUP BY TABLESPACE_NAME) D, +(SELECT TABLESPACE_NAME,ROUND(SUM(BYTES_USED)/(1024*1024),2) USED_SPACE, +ROUND(SUM(BYTES_FREE)/(1024*1024),2) FREE_SPACE +FROM V$TEMP_SPACE_HEADER +GROUP BY TABLESPACE_NAME) F +WHERE D.TABLESPACE_NAME = F.TABLESPACE_NAME(+); + +-- 建立一个中转临时表空间: +create temporary tablespace temp2 +tempfile 'PATH_OF_NEW_TEMPFILE' size 512M +reuse autoextend on next 100M maxsize 2048M; +alter database default temporary tablespace temp2; +drop tablespace temp including contents and datafiles; + +create temporary tablespace temp +tempfile 'PATH_OF_NEW_TEMPFILE' size 512M +reuse autoextend on next 100M maxsize 1024M; +alter database default temporary tablespace temp; +drop tablespace temp2 including contents and datafiles; + + +-- 查看分配给某个表的空间,不管实际是否使用 +SELECT TABLESPACE_NAME,TO_CHAR(SUM(BYTES)/(1024*1024),'999G999D999') CNT_MB +FROM DBA_EXTENTS +WHERE OWNER='SCOTT' AND SEGMENT_NAME='BONUS' AND SEGMENT_TYPE LIKE '%TABLE%' +GROUP BY TABLESPACE_NAME; + +-- 实际使用的空间 +analyze table SCOTT.BONUS compute statistics; +select num_rows * avg_row_len +from dba_tables +where owner = 'SCOTT' and table_name = 'BONUS'; + + +-- 数据文件,表空间与状态对应关系 +SELECT FILE_NAME, TABLESPACE_NAME, ONLINE_STATUS FROM DBA_DATA_FILES; + +-- 用户与表空间对应关系 +SELECT USERNAME, DEFAULT_TABLESPACE FROM DBA_USERS; + +``` \ No newline at end of file diff --git "a/_drafts/Database/oracle\350\241\250\347\251\272\351\227\264\350\241\250\345\210\206\345\214\272\350\257\246\350\247\243\345\217\212oracle\350\241\250\345\210\206\345\214\272\346\237\245\350\257\242\344\275\277\347\224\250\346\226\271\346\263\225(\350\275\254+\346\225\264\347\220\206).md" "b/_drafts/Database/oracle\350\241\250\347\251\272\351\227\264\350\241\250\345\210\206\345\214\272\350\257\246\350\247\243\345\217\212oracle\350\241\250\345\210\206\345\214\272\346\237\245\350\257\242\344\275\277\347\224\250\346\226\271\346\263\225(\350\275\254+\346\225\264\347\220\206).md" new file mode 100644 index 0000000..f763da8 --- /dev/null +++ "b/_drafts/Database/oracle\350\241\250\347\251\272\351\227\264\350\241\250\345\210\206\345\214\272\350\257\246\350\247\243\345\217\212oracle\350\241\250\345\210\206\345\214\272\346\237\245\350\257\242\344\275\277\347\224\250\346\226\271\346\263\225(\350\275\254+\346\225\264\347\220\206).md" @@ -0,0 +1,367 @@ + +此文从以下几个方面来整理关于分区表的概念及操作: + +1.表空间及分区表的概念 +2.表分区的具体作用 +3.表分区的优缺点 +4.表分区的几种类型及操作方法 +5.对表分区的维护性操作. + +# 表空间及分区表的概念 + +表空间:是一个或多个数据文件的集合,所有的数据对象都存放在指定的表空间中,但主要存放的是表, 所以称作表空间。 +分区表:当表中的数据量不断增大,查询数据的速度就会变慢,应用程序的性能就会下降,这时就应该考虑对表进行分区。表进行分区后,逻辑上表仍然是一张完整的表,只是将表中的数据在物理上存放到多个表空间(物理文件上),这样查询数据时,不至于每次都扫描整张表。 + +# 表分区的具体作用 + +Oracle的表分区功能通过改善可管理性、性能和可用性,从而为各式应用程序带来了极大的好处。通常,分区可以使某些查询以及维护操作的性能大大提高。此外,分区还可以极大简化常见的管理任务,分区是构建千兆字节数据系统或超高可用性系统的关键工具。 + +分区功能能够将表、索引或索引组织表进一步细分为段,这些数据库对象的段叫做分区。每个分区有自己的名称,还可以选择自己的存储特性。从数据库管理员的角度来看,一个分区后的对象具有多个段,这些段既可进行集体管理,也可单独管理,这就使数据库管理员在管理分区后的对象时有相当大的灵活性。但是,从应用程序的角度来看,分区后的表与非分区表完全相同,使用 SQL DML 命令访问分区后的表时,无需任何修改。 + +什么时候使用分区表: + +* 表的大小超过2GB。 +* 表中包含历史数据,新的数据被增加都新的分区中。 + +# 表分区的优缺点 + +表分区有以下优点: + +* 改善查询性能:对分区对象的查询可以仅搜索自己关心的分区,提高检索速度。 +* 增强可用性:如果表的某个分区出现故障,表在其他分区的数据仍然可用; +* 维护方便:如果表的某个分区出现故障,需要修复数据,只修复该分区即可; +* 均衡I/O:可以把不同的分区映射到磁盘以平衡I/O,改善整个系统性能。 + +缺点: + +* 已经存在的表没有方法可以直接转化为分区表。但是有几种方式可以间接完成这个操作,大家可以参考:oracle分区表的建立方法(包含已经存在的表要分区):http://blog.csdn.net/wanglilin/article/details/7177338 + +# 表分区的几种类型及操作方法 + +## 范围分区: + +范围分区将数据基于范围映射到每一个分区,这个范围是你在创建分区时指定的分区键决定的。这种分区方式是最为常用的,并且分区键经常采用日期。举个例子:你可能会将销售数据按照月份进行分区。 +当使用范围分区时,请考虑以下几个规则: + +1、每一个分区都必须有一个VALUES LESS THEN子句,它指定了一个不包括在该分区中的上限值。分区键的任何值等于或者大于这个上限值的记录都会被加入到下一个高一些的分区中。 +2、所有分区,除了第一个,都会有一个隐式的下限值,这个值就是此分区的前一个分区的上限值。 +3、在最高的分区中,MAXVALUE被定义。MAXVALUE代表了一个不确定的值。这个值高于其它分区中的任何分区键的值,也可以理解为高于任何分区中指定的VALUE LESS THEN的值,同时包括空值。 + +例一:假设有一个CUSTOMER表,表中有数据200000行,我们将此表通过CUSTOMER_ID进行分区,每个分区存储100000行,我们将每个分区保存到单独的表空间中,这样数据文件就可以跨越多个物理磁盘。下面是创建表和分区的代码,如下: + +```sql +CREATE TABLE CUSTOMER +( + CUSTOMER_ID NUMBER NOT NULL PRIMARY KEY, + FIRST_NAME VARCHAR2(30) NOT NULL, + LAST_NAME VARCHAR2(30) NOT NULL, + PHONEVARCHAR2(15) NOT NULL, + EMAILVARCHAR2(80), + STATUS CHAR(1) +) +PARTITION BY RANGE (CUSTOMER_ID) +( + PARTITION CUS_PART1 VALUES LESS THAN (100000) TABLESPACE CUS_TS01, + PARTITION CUS_PART2 VALUES LESS THAN (200000) TABLESPACE CUS_TS02 +) +``` + +例二:按时间划分 + +```sql +CREATE TABLE ORDER_ACTIVITIES +( + ORDER_ID NUMBER(7) NOT NULL, + ORDER_DATE DATE, + TOTAL_AMOUNT NUMBER, + CUSTOTMER_ID NUMBER(7), + PAID CHAR(1) +) +PARTITION BY RANGE (ORDER_DATE) +( + PARTITION ORD_ACT_PART01 VALUES LESS THAN (TO_DATE('01- MAY -2003','DD-MON-YYYY')) TABLESPACEORD_TS01, + PARTITION ORD_ACT_PART02 VALUES LESS THAN (TO_DATE('01-JUN-2003','DD-MON-YYYY')) TABLESPACE ORD_TS02, + PARTITION ORD_ACT_PART02 VALUES LESS THAN (TO_DATE('01-JUL-2003','DD-MON-YYYY')) TABLESPACE ORD_TS03 +) +``` + +例三:MAXVALUE + +```sql +CREATE TABLE RangeTable +( + idd INT PRIMARY KEY , + iNAME VARCHAR(10), + grade INT +) +PARTITION BY RANGE (grade) +( + PARTITION part1 VALUES LESS THEN (1000) TABLESPACE Part1_tb, + PARTITION part2 VALUES LESS THEN (MAXVALUE) TABLESPACE Part2_tb +); +``` + +## 列表分区: + +该分区的特点是某列的值只有几个,基于这样的特点我们可以采用列表分区。 + +例一 + +```sql +CREATE TABLE PROBLEM_TICKETS +( + PROBLEM_ID NUMBER(7) NOT NULL PRIMARY KEY, + DESCRIPTION VARCHAR2(2000), + CUSTOMER_ID NUMBER(7) NOT NULL, + DATE_ENTERED DATE NOT NULL, + STATUS VARCHAR2(20) +) +PARTITION BY LIST (STATUS) +( + PARTITION PROB_ACTIVE VALUES ('ACTIVE') TABLESPACE PROB_TS01, + PARTITION PROB_INACTIVE VALUES ('INACTIVE') TABLESPACE PROB_TS02 +) +``` + +例二 + +```sql +CREATE TABLE ListTable +( + id INT PRIMARY KEY , + name VARCHAR (20), + area VARCHAR (10) +) +PARTITION BY LIST (area) +( + PARTITION part1 VALUES ('guangdong','beijing') TABLESPACE Part1_tb, + PARTITION part2 VALUES ('shanghai','nanjing') TABLESPACE Part2_tb +); +) +``` + +## 散列分区: + +这类分区是在列值上使用散列算法,以确定将行放入哪个分区中。当列的值没有合适的条件时,建议使用散列分区。 +散列分区为通过指定分区编号来均匀分布数据的一种分区类型,因为通过在I/O设备上进行散列分区,使得这些分区大小一致。 +例一: + +```sql +CREATE TABLE HASH_TABLE +( + COL NUMBER(8), + INF VARCHAR2(100) +) +PARTITION BY HASH (COL) +( + PARTITION PART01 TABLESPACE HASH_TS01, + PARTITION PART02 TABLESPACE HASH_TS02, + PARTITION PART03 TABLESPACE HASH_TS03 +) +``` + +简写: + +```sql +CREATE TABLE emp +( + empno NUMBER (4), + ename VARCHAR2 (30), + sal NUMBER +) +PARTITION BY HASH (empno) PARTITIONS 8 +STORE IN (emp1,emp2,emp3,emp4,emp5,emp6,emp7,emp8); +``` + +hash分区最主要的机制是根据hash算法来计算具体某条纪录应该插入到哪个分区中,hash算法中最重要的是hash函数,Oracle中如果你要使用hash分区,只需指定分区的数量即可。建议分区的数量采用2的n次方,这样可以使得各个分区间数据分布更加均匀。 + +## 组合范围散列分区 + +这种分区是基于范围分区和列表分区,表首先按某列进行范围分区,然后再按某列进行列表分区,分区之中的分区被称为子分区。 + +```sql +CREATE TABLE SALES +( +PRODUCT_ID VARCHAR2(5), +SALES_DATE DATE, +SALES_COST NUMBER(10), +STATUS VARCHAR2(20) +) +PARTITION BY RANGE(SALES_DATE) SUBPARTITION BY LIST (STATUS) +( + PARTITION P1 VALUES LESS THAN(TO_DATE('2003-01-01','YYYY-MM-DD'))TABLESPACE rptfact2009 + ( + SUBPARTITION P1SUB1 VALUES ('ACTIVE') TABLESPACE rptfact2009, + SUBPARTITION P1SUB2 VALUES ('INACTIVE') TABLESPACE rptfact2009 + ), + PARTITION P2 VALUES LESS THAN (TO_DATE('2003-03-01','YYYY-MM-DD')) TABLESPACE rptfact2009 + ( + SUBPARTITION P2SUB1 VALUES ('ACTIVE') TABLESPACE rptfact2009, + SUBPARTITION P2SUB2 VALUES ('INACTIVE') TABLESPACE rptfact2009 + ) +) +``` + +## 复合范围散列分区: + +这种分区是基于范围分区和散列分区,表首先按某列进行范围分区,然后再按某列进行散列分区。 + +```sql +create table dinya_test + ( + transaction_id number primary key, + item_id number(8) not null, + item_description varchar2(300), + transaction_date date + ) + partition by range(transaction_date)subpartition by hash(transaction_id) subpartitions 3 store in (dinya_space01,dinya_space02,dinya_space03) + ( + partition part_01 values less than(to_date(‘2006-01-01','yyyy-mm-dd')), + partition part_02 values less than(to_date(‘2010-01-01','yyyy-mm-dd')), + partition part_03 values less than(maxvalue) + ); +``` + +# 有关表分区的一些维护性操作: + +## 添加分区 + +以下代码给SALES表添加了一个P3分区 +```sql +ALTER TABLE SALES ADD PARTITION P3 VALUES LESS THAN(TO_DATE('2003-06-01','YYYY-MM-DD')); +``` +注意:以上添加的分区界限应该高于最后一个分区界限。 + +以下代码给SALES表的P3分区添加了一个P3SUB1子分区 +```sql +ALTER TABLE SALES MODIFY PARTITION P3 ADD SUBPARTITION P3SUB1 VALUES('COMPLETE'); +``` + +## 删除分区 + +以下代码删除了P3表分区: +```sql +ALTER TABLE SALES DROP PARTITION P3; +``` + +在以下代码删除了P4SUB1子分区: +```sql +ALTER TABLE SALES DROP SUBPARTITION P4SUB1; +``` + +注意:如果删除的分区是表中唯一的分区,那么此分区将不能被删除,要想删除此分区,必须删除表。 + +## 截断分区 + +截断某个分区是指删除某个分区中的数据,并不会删除分区,也不会删除其它分区中的数据。当表中即使只有一个分区时,也可以截断该分区。通过以下代码截断分区: +```sql +ALTER TABLE SALES TRUNCATE PARTITION P2; +``` + +通过以下代码截断子分区: +```sql +ALTER TABLE SALES TRUNCATE SUBPARTITION P2SUB2; +``` + +## 合并分区 + +合并分区是将相邻的分区合并成一个分区,结果分区将采用较高分区的界限,值得注意的是,不能将分区合并到界限较低的分区。以下代码实现了P1 P2分区的合并: +```sql +ALTER TABLE SALES MERGE PARTITIONS P1,P2 INTO PARTITION P2; +``` + +## 拆分分区 + +拆分分区将一个分区拆分两个新分区,拆分后原来分区不再存在。注意不能对HASH类型的分区进行拆分。 +```sql +ALTER TABLE SALES SBLIT PARTITION P2 AT(TO_DATE('2003-02-01','YYYY-MM-DD')) INTO (PARTITION P21,PARTITION P22); +``` + +## 接合分区(coalesca) + +结合分区是将散列分区中的数据接合到其它分区中,当散列分区中的数据比较大时,可以增加散列分区,然后进行接合,值得注意的是,接合分区只能用于散列分区中。通过以下代码进行接合分区: +```sql +ALTER TABLE SALES COALESCA PARTITION; +``` + +## 重命名表分区 + +以下代码将P21更改为P2 +```sql +ALTER TABLE SALES RENAME PARTITION P21 TO P2; +``` + +## 相关查询 + +```sql +-- 跨分区查询 +select sum( *) from +(select count(*) cn from t_table_SS PARTITION (P200709_1) +union all +select count(*) cn from t_table_SS PARTITION (P200709_2) +); + +--查询表上有多少分区 +SELECT * FROM useR_TAB_PARTITIONS WHERE TABLE_NAME='tableName' + +--查询索引信息 +select object_name,object_type,tablespace_name,sum(value) +from v$segment_statistics +where statistic_name IN ('physical reads','physical write','logical reads')and object_type='INDEX' +group by object_name,object_type,tablespace_name +order by 4 desc + +--显示数据库所有分区表的信息: +select * from DBA_PART_TABLES + +--显示当前用户可访问的所有分区表信息: +select * from ALL_PART_TABLES + +--显示当前用户所有分区表的信息: +select * from USER_PART_TABLES + +--显示表分区信息 显示数据库所有分区表的详细分区信息: +select * from DBA_TAB_PARTITIONS + +--显示当前用户可访问的所有分区表的详细分区信息: +select * from ALL_TAB_PARTITIONS + +--显示当前用户所有分区表的详细分区信息: +select * from USER_TAB_PARTITIONS + +--显示子分区信息 显示数据库所有组合分区表的子分区信息: +select * from DBA_TAB_SUBPARTITIONS + +--显示当前用户可访问的所有组合分区表的子分区信息: +select * from ALL_TAB_SUBPARTITIONS + +--显示当前用户所有组合分区表的子分区信息: +select * from USER_TAB_SUBPARTITIONS + +--显示分区列 显示数据库所有分区表的分区列信息: +select * from DBA_PART_KEY_COLUMNS + +--显示当前用户可访问的所有分区表的分区列信息: +select * from ALL_PART_KEY_COLUMNS + +--显示当前用户所有分区表的分区列信息: +select * from USER_PART_KEY_COLUMNS + +--显示子分区列 显示数据库所有分区表的子分区列信息: +select * from DBA_SUBPART_KEY_COLUMNS + +--显示当前用户可访问的所有分区表的子分区列信息: +select * from ALL_SUBPART_KEY_COLUMNS + +--显示当前用户所有分区表的子分区列信息: +select * from USER_SUBPART_KEY_COLUMNS + +--怎样查询出oracle数据库中所有的的分区表 +select * from user_tables a where a.partitioned='YES' + +--删除一个表的数据是 +truncate table table_name; + +--删除分区表一个分区的数据是 +alter table table_name truncate partition p5; +``` \ No newline at end of file diff --git "a/_drafts/Database/\345\246\202\344\275\225\345\273\272\347\253\213DB2\345\210\206\345\214\272\346\225\260\346\215\256\345\272\223\357\274\237\357\274\210\350\275\254\357\274\211.md" "b/_drafts/Database/\345\246\202\344\275\225\345\273\272\347\253\213DB2\345\210\206\345\214\272\346\225\260\346\215\256\345\272\223\357\274\237\357\274\210\350\275\254\357\274\211.md" new file mode 100644 index 0000000..f60e55d --- /dev/null +++ "b/_drafts/Database/\345\246\202\344\275\225\345\273\272\347\253\213DB2\345\210\206\345\214\272\346\225\260\346\215\256\345\272\223\357\274\237\357\274\210\350\275\254\357\274\211.md" @@ -0,0 +1,158 @@ + +原作:陈敏 2008-05-21 + +熟悉IBM DB2 UDB的都知道,构筑DB2数据库对象的层次关系,既每台物理机器可以配置多个实例,而每个实例是一个独立的运行环境,在每个实例下可以创建多个数据库,每个数据库可以有多个表空间,而数据库中的表会存放在这些表空间中。那分区数据库中他们的关系又如何,是如何分区的呢?本文就分区数据库的基本概念做简单介绍。 + +有了数据库分区后,在原来构筑DB2数据库对象的层次关系里发生了一些变化,实例增加了一个物理特性,就是实例所拥有的数据库分区,为了使使用者能够充分利用分区数据库的特性,在数据库和表空间之间增加了一层,——数据库分区组。与之相关的名词包括数据库分区,数据库分区组,分区映射,分区键,下面就详细解释一下: + +## 数据库分区 + +首先说一下什么是数据库分区,数据库分区是DB2数据库的一部分,由它自己的数据、索引、配置文件和事务日志组成。分区数据库就是具有两个或多个分区的数据库。这样,表就可以位于一个或多个数据库分区中。与每个数据库分区相关联的处理器都用来满足表请求。数据检索和更新请求将自动分解为子请求,并在适当的数据库分区中并行执行。 + +## 数据库分区组 + +数据库分区组是一个或多个数据库分区的集合。想要为数据库创建表时,首先创建用来存储表空间的数据库分区组,然后创建用来存储表的表空间。 + +可以在数据库中定义一个或多个数据库分区组成的命名子集。您定义的每个子集称为 数据库分区组 。包含多个数据库分区的每个子集称为 多分区数据库分区组 。多分区数据库分区组只能使用属于相同实例的数据库分区定义。 + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/200852110411.jpg) + +图 1 给出了一个含五个分区的数据库示例,在这个示例中: + +* 数据库分区组横跨除一个数据库分区外的所有其它分区(数据库分区组 1)。 +* 数据库分区组包含一个数据库分区(数据库分区组 2)。 +* 数据库分区组包含两个数据库分区(数据库分区组 3)。 +* 数据库分区组 2 中的数据库分区与数据库分区组 1 共享并与之相交。 +* 数据库分区组 3 中存在单个数据库分区,该分区与数据库分区组 1 共享并与之相交。 + +可使用 CREATE DATABASE PARTITION GROUP 语句创建数据库分区组。此语句指定表空间容器和表数据将驻留其上的一组数据库分区。此语句还可以: + +* 为数据库分区组创建分区映射。 +* 生成分区映射标识。 +* 将记录插入下列目录表: + * SYSCAT.DBPARTITIONGROUPS + * SYSCAT.PARTITIONMAPS + * SYSCAT.DBPARTITIONGROUPDEF + +创建数据库时创建的缺省数据库分区组由数据库管理器使用。IBMCATGROUP 是包含系统目录的表空间的缺省数据库分区组,只在主节点上(主数据库分区)。IBMTEMPGROUP 是系统临时表空间的缺省数据库分区组,包含所有数据库分区。 +IBMDEFAULTGROUP 是包含用户定义的表的表空间的缺省数据库分区组,包含所有数据库分区。 + +通过将表空间放置在多分区数据库分区组中,将该表空间内的所有表划分或分区到该数据库分区组的每个分区中。由此该表空间被创建到了一个数据库分区组中。一旦位于某个数据库分区组中,该表空间就必须保留在该处;而不能更改至另一数据库分区组。CREATE TABLESPACE 语句用于将表空间与数据库分区组关联。 + +建数据库分区组示例: + +```sql +CREATE DATABASE PARTITION GROUP MAXGROUP ON ALL DBPARTITIONNUMS +CREATE DATABASE PARTITION GROUP MEDGROUP ON DBPARTITIONNUMS( 0 TO 2, 5, 8) +``` + +## 分区映射 + +在分区数据库环境中,数据库管理器必须具有弄清一个表的哪些行存储在哪些数据库分区上的方法。数据库管理器必须知道到哪里去查找所需的数据,并使用一个称为 分区映射 的映射来查找数据。 + +分区映射是一个内部生成的数组,对于多分区数据库分区组,它包含 4 096 个条目,对于单一分区数据库分区组,只包含一个条目。对于单一分区数据库分区组,分区映射只有一个条目,该条目包含存储数据库表的所有行的数据库分区的分区号。对于多分区数据库分区组,以循环方式指定数据库分区组的分区号。正如使用网格将城市地图划分为区一样,数据库管理器使用 分区键 来确定存储数据的位置(数据库分区)。 + +例如,假定您将一个数据库创建在四个数据库分区(编号为 0-3)上。此数据库的 IBMDEFAULTGROUP 数据库分区组的分区映射将是: +0 1 2 3 0 1 2 ... + +若已使用数据库分区 1 和 2 在该数据库中创建了一个数据库分区组,则该数据库分区组的分区映射将是: +1 2 1 2 1 2 1 ... + +若要装入数据库的一个表的分区键是一个可能在范围 1 至 500 000 之间的整数,则会将分区键散列至 0 至 4 095 之间的一个 分区号。将该编号用作分区映射中的索引,以选择用于该行的数据库分区。 + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/2008521104134.jpg) + +图 2 显示如何将具有分区键值 (c1, c2, c3) 的行映射至分区 2,然后引用数据库分区 n5。 + +分区映射可以灵活地控制将数据存储在分区数据库中的哪个位置。若将来需要更改数据库中各数据库分区上的数据分发,可以使用数据再分发实用程序。此实用程序允许重新平衡或调整数据分发的偏差。 + +## 分区键 + +分区键 是一列(或一组列),用于确定将某行数据存储在什么分区。分区键是使用 CREATE TABLE 语句在表上定义的。如果没有为分布在数据库分区组中的多个数据库分区中的表空间中的表定义分区键,在缺省情况下将会根据主键的第一列创建分区键。若未指定主键,则缺省分区键是在该表中定义的第一个非长型字段列。( 长型 包括所有长型数据类型和所有大对象(LOB)数据类型)。若没有列满足缺省分区键的要求,则不会不带键创建该表。 + +好的表分区键就是将数据均匀分布在数据库分区组中的所有数据库分区上的分区键。不适当的分区键会导致数据分发不均匀。数据分发不均匀的列和含有少数特异值的列不应选作分区键。特异值的数目必须足够大,才能确保将行均匀分布在数据库分区组中的所有数据库分区上。应用分区散列算法的成本与分区键的大小是成正比的。分区键不能超过 16 列,而且列越少,性能越好。不应将不需要的列包括在分区键中。 + +当定义分区键时,应该考虑下列几点: + +* 不支持创建只包含长型数据类型(LONG VARCHAR、LONG VARGRAPHIC、BLOB、CLOB 或 DBCLOB)的多分区表。 +* 不能改变分区键定义。 +* 分区键应该包括最频繁连接的列。 +* 分区键应该由经常参与 GROUP BY 子句的列组成。 +* 任何唯一键或主键必须包含所有分区键列。 +* 在联机事务处理(OLTP)环境中,分区键中的所有列都应该使用带常量或主机变量的等于(=)谓词来参与该事务。例如,假 +定有一个在事务中经常使用的职员号 emp_no,如: + +```sql +UPDATE emp_table SET ... WHERE +emp_no = host-variable +``` + +散列分区 是确定分区表中每一行的位置的方法。该方法的原理如下: + +1. 将散列算法应用于分区键的值,并生成介于 0 与 4095 之间的分区号。 +2. 创建数据库分区组时将创建分区映射。每个分区号依次以循环方式重复,以填写该分区映射。 +3. 该分区号用作分区映射中的一个索引。分区映射中该位置处的编号是存储该行的数据库分区的编号。 + +在分区数据库中跨越几个分区创建一个表在性能上有几个优点。与检索数据相关联的工作可分成几部分在各个数据库分区中进行。 + +必须小心地选择适当的分区键,因为 以后再也不能更改它 。再者,必须将任何唯一索引(因此也是唯一键或主键)定义为分区键的一个超集。即,若定义了分区键,则唯一键和主键必须包括所有与分区键相同的列(它们可能有多列)。 + +表的一个分区大小不能超过 64 GB 和可用的磁盘空间中较小的那一个。(假设表空间具有 4 KB 的页大小。)该表的最大大小可以是 64 GB(或可用磁盘空间)乘以数据库分区数之积。若该表空间的页大小为 8 KB,则该表最大的大小可以为 128 GB(或可用的磁盘空间)乘以数据库分区数之积。若该表空间的页大小为 16 KB,则该表的最大大小可为 256 GB(或可用的磁盘空间)乘以数据库分区数之积。若该表空间的页大小为 32 KB,则该表的最大大小可为 512 GB(或可用的磁盘空间)乘以数据库分区数之积。 + +以下是一个示例: + +```sql +CREATE TABLE MIXREC (MIX_CNTL INTEGER NOT NULL, +MIX_DESC CHAR(20) NOT NULL, +MIX_CHR CHAR(9) NOT NULL, +MIX_INT INTEGER NOT NULL, +MIX_TMSTMP TIMESTAMP NOT NULL) +IN MIXTS12 +PARTITIONING KEY (MIX_INT) USING HASHING +``` + +在上一个示例中,表空间是 MIXTS12,而分区键是 MIX_INT。若未显式指定分区键,则它是 MIX_CNTL。(若未指定主键且未定义分区键,则分区键是该列表中的第一个非长型字段的列。) + +## 建立分区数据库 + +有了上述了解,就可以建一个分区数据库了,示例如下: + +### 1) 了解分区定义 + +分区定义可以从节点配置文件(db2nodes.cfg)得到,其位于实例所有者的主目录中,它包含一些配置信息,告诉 DB2 有哪些服务器参与分区数据库环境的实例。分区数据库环境中的每个实例都有一个 db2nodes.cfg 文件。对于每个参与实例的服务器,db2nodes.cfg 文件必须包含一个条目。当创建实例时,会自动创建 db2nodes.cfg 文件并对拥有实例的服务器添加条目。这里我们假设有4个分区。 + +### 2) 创建数据库 + +`create db dpfdb;` +默认会创建3个分区组IBMCATGROUP(只在0号分区上) +IBMTEMPGROUP ,IBMDEFAULTGROUP(在所有分区上),如果用户没有创建其他分区组,所创建的表空间会默认放在IBMDEFAULTGROUP上 + +### 3) 创建分区组 + +我们在 1到3号分区建立一个分区组 +`CREATE DATABASE PARTITION GROUP USERGROUP ON DBPARTITIONNUMS(1,2,3);` + +### 4) 创建表空间 + +`CREATE TABLESPACE TS IN USERGROUP MANAGED BY DATABASE USING (file '/DB2containers/TScontainer $N' 10000)` + +有4个containers被创建 + +``` +/DB2containers/TScontainer0 - on DATABASE PARTITION 0 +/DB2containers/TScontainer1 - on DATABASE PARTITION 1 +/DB2containers/TScontainer2 - on DATABASE PARTITION 2 +/DB2containers/TScontainer3 - on DATABASE PARTITION 3 +``` + +### 5) 创建表 + +```sql +CREATE TABLE DPFTABLE (ID INTEGER NOT NULL, +NAME CHAR(20) NOT NULL) +IN TS +PARTITIONING KEY (ID) USING HASHING; +``` + +如果想了解如何配置分区数据库环境,请参考 +http://www.ibm.com/developerworks/cn/aix/library/au-db2-dpf/index.html#N100A7 \ No newline at end of file diff --git "a/_drafts/Database/\345\277\230\350\256\260oracle\347\232\204sys\347\224\250\346\210\267\345\257\206\347\240\201\346\200\216\344\271\210\344\277\256\346\224\271\344\273\245\345\217\212Oracle 11g \351\273\230\350\256\244\347\224\250\346\210\267\345\220\215\345\222\214\345\257\206\347\240\201.md" "b/_drafts/Database/\345\277\230\350\256\260oracle\347\232\204sys\347\224\250\346\210\267\345\257\206\347\240\201\346\200\216\344\271\210\344\277\256\346\224\271\344\273\245\345\217\212Oracle 11g \351\273\230\350\256\244\347\224\250\346\210\267\345\220\215\345\222\214\345\257\206\347\240\201.md" new file mode 100644 index 0000000..ad7db48 --- /dev/null +++ "b/_drafts/Database/\345\277\230\350\256\260oracle\347\232\204sys\347\224\250\346\210\267\345\257\206\347\240\201\346\200\216\344\271\210\344\277\256\346\224\271\344\273\245\345\217\212Oracle 11g \351\273\230\350\256\244\347\224\250\346\210\267\345\220\215\345\222\214\345\257\206\347\240\201.md" @@ -0,0 +1,47 @@ + +## 忘记除SYS、SYSTEM用户之外的用户的登录密码 + + CONN SYS/PASS_WORD AS SYSDBA; --用SYS (或SYSTEM)用户登录 + ALTER USER user_name IDENTIFIED BY "newpassword"; --修改用户的密码,密码不能是数字开头,否则会出现:ORA-00988: 口令缺失或无效 + +## 忘记SYS用户,或者是SYSTEM用户的密码 + + CONN SYS/PASS_WORD AS SYSDBA; --如果是忘记SYSTEM用户的密码,可以用SYS用户登录。 + ALTER USER SYSTEM IDENTIFIED BY "newpassword"; + + CONN SYSTEM/PASS_WORD AS SYSDBA; --如果是忘记SYS用户的密码,可以用SYSTEM用户登录。 + ALTER USER SYS IDENTIFIED BY "newpassword"; + +## SYS,SYSTEM用户的密码都忘记 + +Oracle提供了两种验证方式,一种是OS验证,另一种密码文件验证方式,如果是第一种方式用以下方法修改密码: + +```sql +  sqlplus /nolog; +  connect / as sysdba +  alter user sys identified by newpassword; +  alter user system identified by newpassword; +``` + +如果是第二种方法可以使用ORAPWD.EXE 工具修改密码。打开命令提示符窗口,输入如下命令: + + orapwd file=D:\oracle10g\database\pwdctcsys.ora password=newpassword + +这个命令重新生成了数据库的密码文件。密码文件的位置在ORACLE_HOME目录下的\database目录下。这个密码是修改sys用户的密码。除sys其他用户的密码不会改变。也可以下方法修改密码,设定完后,重新启动服务,再次登陆就可以了。 + + orapwd file=pwdxxx.ora password=newpassword entries=10 + + +## Oracle 11g 默认用户名和密码 + +安装ORACLE时,若没有为下列用户重设密码,则其默认密码如下: + +用户名/密码 登录身份 说明 +sys/change_on_install SYSDBA 或 SYSOPER 不能以 NORMAL 登录,可作为默认的系统管理员 +system/manager SYSDBA 或 NORMAL 不能以 SYSOPER 登录,可作为默认的系统管理员 +sysman/oem_temp sysman 为 oms 的用户名 +scott/tiger NORMAL 普通用户 +aqadm/aqadm SYSDBA 或 NORMAL 高级队列管理员 +Dbsnmp/dbsnmp SYSDBA 或 NORMAL 复制管理员 + +登录身份:指登录时的Role指定,oracle11g中分SYSDBA和default两种。在安装Oracle 10g的时候,提示创建数据库,在创建的同时提示你输入口令,若此时你输入了密码,在登录数据库的时候用户名sys 对应的密码就应该是你创建数据库时候输入的口令。而非默认的change_on_install. \ No newline at end of file diff --git "a/_drafts/Database/\346\225\260\346\215\256\345\272\223\347\233\270\345\205\263\346\200\273\347\273\223.md" "b/_drafts/Database/\346\225\260\346\215\256\345\272\223\347\233\270\345\205\263\346\200\273\347\273\223.md" new file mode 100644 index 0000000..04e0b98 --- /dev/null +++ "b/_drafts/Database/\346\225\260\346\215\256\345\272\223\347\233\270\345\205\263\346\200\273\347\273\223.md" @@ -0,0 +1,60 @@ + +# 通用: + +* http://db-engines.com/en/ranking + + +# MySQL + +* MySQL: http://www.mysql.com/ +* MySQL参考:http://dev.mysql.com/doc/#manual +* windows下忘记mysql超级管理员root密码的解决办法:http://superman7020.blog.163.com/blog/static/1374465920085210119253/ +* 5款常用mysql slow log分析工具的比较:http://blog.chinaunix.net/uid-8504518-id-2030594.html +* Autocomplete in MySQL under Windows: http://stackoverflow.com/questions/269653/autocomplete-in-mysql-under-windows + + +# SQL Server + +* SQL Server: https://msdn.microsoft.com/library/bb545450.aspx +* Transact-SQL 参考: https://msdn.microsoft.com/zh-cn/library/bb510741(v=sql.105).aspx +* TSQLT:http://tsqlt.org/ +* sqlcmd 实用工具:http://msdn.microsoft.com/zh-cn/library/ms162773.aspx +* SQL SERVER 2005中的Schema(架构)概念详解:http://blog.sina.com.cn/s/blog_5b2c0dcc0100alj9.html +* Sql Server 2005中的架构(Schema)、用户(User)、角色(Role)和登录(Login)(一):http://www.cnblogs.com/end/archive/2009/08/07/1541373.html +* Sql Server 2005中的架构(Schema)、用户(User)、角色(Role)和登录(Login)(二):http://www.cnblogs.com/end/archive/2009/08/07/1541374.html +* Sql Server 2005中的架构(Schema)、用户(User)、角色(Role)和登录(Login)(三):http://www.cnblogs.com/end/archive/2009/08/07/1541377.html +* 谓词和运算符:http://www.cnblogs.com/cuiyh/archive/2010/12/18/1910090.html +* Tempdb数据库详细介绍:http://www.cnblogs.com/qanholas/archive/2012/01/05/2313006.html + + +# Oracle + +* Oracle Database DownLoad: http://www.oracle.com/technetwork/cn/database/enterprise-edition/downloads/index.html +* Oracle SQL Developer: http://www.oracle.com/technetwork/developer-tools/sql-developer/overview/index.html +* Instant Client Downloads for Microsoft Windows (32-bit): http://www.oracle.com/technetwork/topics/winsoft-085727.html + +* Oracle SQL Handler:http://www.heartblue.cn/ +* SI Object Browser:http://www.presoft.com.cn/ob/ + +* Oracle Berkeley DB:http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/overview/index.html + +* Navicat: http://www.navicat.com.cn/ +* Navicat Premium: http://www.navicat.com.cn/products/navicat-premium + + Navicat Premium 是一套数据库管理工具,让你以单一程序同時连接到 MySQL、MariaDB、SQL Server、SQLite、Oracle 和 PostgreSQL 数据库。 + +* ToadWorld: http://www.toadworld.com/ + +* Oracle 11g安装图文攻略: http://jingyan.baidu.com/article/9f7e7ec04c14c76f29155465.html +* win7_oracle11g_64位连接32位PLSQL_Developer: http://jingyan.baidu.com/article/fb48e8be4c7c206e622e1491.html +* Oracle 11g 如何创建数据库:http://jingyan.baidu.com/article/cbcede07cf42ef02f40b4dc2.html +* 数据库使用详解:[3]SQL Developer如何配置:http://jingyan.baidu.com/article/e4511cf33f289e2b845eafb6.html +* oracle的各版本发行时间及特点: http://blog.csdn.net/dream19881003/article/details/7178357 +* oracle客户端软件的说明:http://blog.csdn.net/haiross/article/details/17917637 +* 怎么判断oracle客户端、服务器端的位数:http://blog.csdn.net/linghe301/article/details/8471945 + + +# DB2 + +* 官网:http://www-01.ibm.com/software/data/db2/ +* DB2China:http://www.db2china.net/ \ No newline at end of file diff --git "a/_drafts/Java/202110171056\345\246\202\344\275\225\345\260\206cpdetector\345\217\221\345\270\203Maven\344\276\235\350\265\226\345\210\260\344\270\255\345\244\256\344\273\223\345\272\223.md" "b/_drafts/Java/202110171056\345\246\202\344\275\225\345\260\206cpdetector\345\217\221\345\270\203Maven\344\276\235\350\265\226\345\210\260\344\270\255\345\244\256\344\273\223\345\272\223.md" new file mode 100644 index 0000000..5e92178 --- /dev/null +++ "b/_drafts/Java/202110171056\345\246\202\344\275\225\345\260\206cpdetector\345\217\221\345\270\203Maven\344\276\235\350\265\226\345\210\260\344\270\255\345\244\256\344\273\223\345\272\223.md" @@ -0,0 +1,75 @@ +参考: +https://www.cnblogs.com/jiangxinnju/p/9903517.html + +从官网下载最新版本的发布包 +https://sourceforge.net/projects/cpdetector/ + +1、生成cpdetector_1.0.10_bundle.jar + +解压后把几个依赖的包放到一起,为了简单把cpdetector*.jar和几个扩展依赖打包到一起,解压各个jar包 +jar -xvf .\cpdetector_1.0.10.jar +jar -xvf .\chardet-1.0.jar +jar -xvf .\antlr-2.7.4.jar +jar -xvf .\jargs-1.0.jar +把jar包移除,然后 +jar -cvfM cpdetector-1.0.10.jar . + +2、生成cpdetector-1.0.10-javadoc.jar和cpdetector-1.0.10-sources.jar + +jar -cvf cpdetector-1.0.10-javadoc.jar .\binary-release.txt .\MPL-1.1.txt +jar -cvf cpdetector-1.0.10-sources.jar .\binary-release.txt .\MPL-1.1.txt + +3、生成cpdetector-1.0.10.pom + +然后创建pom.xml,填写对应信息。 + +```xml + + 4.0.0 + com.github.jiangxincode + cpdetector + 1.0.10 + jar + cpdetector + cpDetector is a proxy for codepage detection of documents. It delegates to multiple instances that try to detect the codepage by different techinques. A command line executeable is shipped that allows to sort documents by codepage. + https://sourceforge.net/projects/cpdetector/ + + + MPL-1.1 + https://www.mozilla.org/en-US/MPL/1.1/ + + + + git://git.code.sf.net/p/cpdetector/sourcecode cpdetector-sourcecode + git://git.code.sf.net/p/cpdetector/sourcecode cpdetector-sourcecode + git://git.code.sf.net/p/cpdetector/sourcecode cpdetector-sourcecode + + + + achimwestermann + + + +``` + +4、生成对应的asc签名文件 + +gpg --gen-key创建密钥(密码12345678) + +pub rsa3072 2021-10-17 [SC] [expires: 2023-10-17] + 6F52975E26BFE145B5A23C55A11AA01F0E9838A9 +uid Aloys +sub rsa3072 2021-10-17 [E] [expires: 2023-10-17] + +分发公钥到某个公钥服务器 +gpg --keyserver hkp://keyserver.ubuntu.com --send-keys 6F52975E26BFE145B5A23C55A11AA01F0E9838A9 + +gpg -ab .\cpdetector-1.0.10.jar +gpg -ab .\cpdetector-1.0.10-javadoc.jar +gpg -ab .\cpdetector-1.0.10-sources.jar +gpg -ab .\cpdetector-1.0.10.pom + +5、打包到一起 +jar -cvf bundle.jar cpdetector-1.0.10* \ No newline at end of file diff --git "a/_drafts/Java/202209041204\347\203\255\345\210\207\346\215\242Log4j\347\272\247\345\210\253\351\205\215\347\275\256.md" "b/_drafts/Java/202209041204\347\203\255\345\210\207\346\215\242Log4j\347\272\247\345\210\253\351\205\215\347\275\256.md" new file mode 100644 index 0000000..78f2c1f --- /dev/null +++ "b/_drafts/Java/202209041204\347\203\255\345\210\207\346\215\242Log4j\347\272\247\345\210\253\351\205\215\347\275\256.md" @@ -0,0 +1,230 @@ +# 热切换Log4j级别配置 + +原创不易,转载请注明原始地址: + +做一个产品或者项目,在测试时一般要打印详细的log,发布以后,因为打印日志会损失性能,所以通常在生产机上将log4j级别设置为最高,以提高效率,一旦客户那里出了问题,需要查看详细的日志信息来跟踪问题,此时打印日志就是很重要的事情。这就需要在应用开发不重启的情况下,动态切换log4j日志策略了。 + +目前有两种方式可以实现热切换Log4j级别配置,一是定时刷新log4j配置文件,二是调用setlevel()动态设置。 + +## 定时刷新log4j配置文件 + +### 使用log4j原生动态更新配置文件的方法 + +使用log4j自带的动态更新配置很简单,只要调用 PropertyConfigurator 或者 DOMConfigurator类的 configureAndWatch(String configFileName)或者 configureAndWatch(String configFileName, long delay)方法就可以了。其中configFileName值配置文件的路径加文件名,delay指扫描配置文件是否改变的间隔时间,默认值是 60 秒。在调用时log4j会创建一个线程,定时的去检查配置文件是否改变,如果改变的话就重新加载配置文件。需要注意的是在log4j中每调用一次configureAndWatch方法都会启动一个新的扫描线程: + +Log4j configureAndWatch() spawning thousands of threads: + +实例代码参考: + +```java +package edu.jiangxin.log4j.hotchange; + +import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; + +public class Log4jHotChangeWatchdog { + public static Logger logger = Logger.getLogger(Log4jHotChangeWatchdog.class); + static { + PropertyConfigurator.configureAndWatch("log4j.properties", 60000); + } + + public static void main(String[] args) { + while (!Thread.interrupted()) { + if (logger.isDebugEnabled()) { + logger.debug("debug!!"); + } + if (logger.isInfoEnabled()) { + logger.info("info!!"); + } + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + } + } + } +} +``` + +### 使用触发方式更新配置文件 + +我们可以通过configureAndWatch方法来进行动态的改变log4j的配置,但是他采用了轮询方式来实现的,现在我们需要某种触发机制自己调用PropertyConfigurator对象的configure(String configFilename)方法重新加载log4j的配置。触发机制需要结合实际业务情况,比如提供JSP页面触发,提供Rest接口触发。 + +实例代码参考: + +```JSP + +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<%@ page import="java.util.*, java.io.*" %> +<%@ page import="org.apache.log4j.PropertyConfigurator" %> +<% + String state = request.getParameter("state"); + if (state != null && state.trim().equals("start")) { + PropertyConfigurator.configure("./log4j.properties"); + } +%> + + + Codestin Search App + + +开始 + + +``` + +### 使用Spring定时更新配置文件 + +spring通过org.springframework.util. Log4jConfigListener实现运行时切换需求,Log4jConfigListener对log4j原生方法进行封装。默认情况是1分钟重新加载一次。 + +在web.xml文件中 配置 加载 log4j.properties的属性 + +```xml + + log4jConfigLocation + /WEB-INF/log4j.properties + + + log4jRefreshInterval + 10000 + + + org.springframework.web.util.Log4jConfigListener + + + + webAppRootKey + scheduleProject + +``` + +1. log4jConfigLocation 指定Spring从哪个目录下加载 log4j.properties 配置文件 +2. log4jRefreshInterval 当修改了配置文件时,不需要重启就能加载变化了的log4j.properties 配置文件 +3. webAppRootKey 项目的标识,一个窗口中可能部署了多个项目,用它进行区分。当配置日志文件的输出目录时,可能会用到它。 + +## 调用setlevel()动态设置 + +### 通过界面 + +通过自己做的web界面,客户在前台设置日志级别,后台调用logger.setlevel()来完成日志级别切换,但是这有个缺点,下次服务重启后,本次的日志级别调整持久保存下来。 + +实例代码参考: + +```JSP + +<%@ page import="org.apache.log4j.*" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> +<%@ page contentType="text/html;charset=GBK" language="java" %> + + +Codestin Search App + + +

Log4J级别控制

+<% String logName = request.getParameter("log"); + if (null != logName) { + Logger log = ("".equals(logName) ? + Logger.getRootLogger() : Logger.getLogger(logName)); + log.setLevel(Level.toLevel(request.getParameter("level"), Level.DEBUG)); + } +%> + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
LevelLoggerSet New Level
${rootLogger.level}${rootLogger.name} + + ${level} + +
${logger.level}${logger.name} + + ${level} + +
+
+
+Show 所有已知 loggers + + +``` + +效果如图所示: + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/2022090412041.png) + +调用setlevel()方法与配置文件动态加载一样,除了可以通过JSP触发,还可以通过Rest接口触发,暴露Rest接口可以参考如下文章,本文不再展开。 +Build a RESTful Web service using Jersey and Apache Tomcat: + +提供一个暴露Rest服务的代码样例: + +```Java +package edu.jiangxin.jersey.resources; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.apache.log4j.Level; +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; + +@Path("/Log4JHotChange") +public class Log4jHotChangeWS { + @GET + @Produces(MediaType.APPLICATION_XML) + @Path("/{package}/{level}") + public Response index(@PathParam("package") String p, @PathParam("level") String l) { + Level level = Level.toLevel(l); + Logger logger = LogManager.getLogger(p); + logger.setLevel(level); + return Response.ok().build(); + } + + @GET + @Produces(MediaType.APPLICATION_XML) + @Path("/root/{level}") + public Response index(@PathParam("level") String l) { + Level level = Level.toLevel(l); + LogManager.getRootLogger().setLevel(level); + return Response.ok().build(); + } + +} +``` + +### 通过Spring+JMX方式 + +如果使用spring和jmx会很简单 + +实例代码参考:Log4jHotChangeMBean + +本文所有实例代码见: \ No newline at end of file diff --git "a/_drafts/Java/202301072303Java Swing TreeTable\346\240\267\344\276\213\346\214\207\345\257\274.md" "b/_drafts/Java/202301072303Java Swing TreeTable\346\240\267\344\276\213\346\214\207\345\257\274.md" new file mode 100644 index 0000000..d33b36b --- /dev/null +++ "b/_drafts/Java/202301072303Java Swing TreeTable\346\240\267\344\276\213\346\214\207\345\257\274.md" @@ -0,0 +1,663 @@ +# Java Swing TreeTable样例指导 + +如何在Java中使用TreeTable本身就是一个主题。在各种GUI库中,我们一般都是假设这种组件是现成的。但是很不幸,在Java Swing这个GUI框架中并没有一个现成的TreeTable控件。但是我们仍然可以创建一个自定义控件,来模拟TreeTable。在接下来的教程中,我们使用一个样例来说明如何做到这一点。 + +在Oracle的网站上(之前是Sun的网站),Philip Milne曾经写过的一个教我们如何创建一个TreeTable控件的样例教程。很可能是因为这些代码太过于老旧的缘故,我们会感觉到源码中包含了一些现在来看并不需要的"hacks"。除此之外,这个样例中内置了很多功能,这些功能掩盖了TreeTable的本质特征。为了让样例代码更容易理解,我删除了一些非必要代码,方便大家理解,同时尽量展现出TreeTable组件的本质。 + +下面两张图是样例完成后的效果截图,其中一个在Windows平台,另一个在Windows平台。 + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/2023010723031.png) + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/2023010723032.png) + +下面的示意图展示了TreeTable的类结构。基于这个示意图,我们会用后面的一些图例详细解释TreeTable是如何进行组织的。所有来自于JDK的类都用渐变色背景,新建的类则使用纯色背景,同时这些新建的类都是以'My'开头的。 + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/2023010723033.png) + +首先,我们创建一个`MyTreeTableModel`接口,该接口继承于`TreeModel`接口。通过扩展我们可以让一个节点拥有多个列元素。使用的时候我们一般这样声明:`TreeModel MyTreeTableModel` + +```java +package de.hameister.treetable; +  +import javax.swing.tree.TreeModel; +  +public interface MyTreeTableModel extends TreeModel { +  +  +    /** +     * Returns the number of available columns. +     * @return Number of Columns +     */ +    public int getColumnCount(); +  +    /** +     * Returns the column name. +     * @param column Column number +     * @return Column name +     */ +    public String getColumnName(int column); +  +  +    /** +     * Returns the type (class) of a column. +     * @param column Column number +     * @return Class +     */ +    public Class getColumnClass(int column); +  +    /** +     * Returns the value of a node in a column. +     * @param node Node +     * @param column Column number +     * @return Value of the node in the column +     */ +    public Object getValueAt(Object node, int column); +  +  +    /** +     * Check if a cell of a node in one column is editable. +     * @param node Node +     * @param column Column number +     * @return true/false +     */ +    public boolean isCellEditable(Object node, int column); +  +    /** +     * Sets a value for a node in one column. +     * @param aValue New value +     * @param node Node +     * @param column Column number +     */ +    public void setValueAt(Object aValue, Object node, int column); +} +``` + +接下来,我们创建一个抽象类`MyAbstractTreeTableModel`来继承`MyTreeTableModel`。在该类中,我们保存根节点,同时提供一个方法来检查是否存在子节点,并且该类还负责管理所有的`EventListener`。`EventListener`可以确保当数据模型结构发生变化时会传递到树状结构,并被正确的显示。使用的时候我们一般这样声明:`MyTreeTableModel MyAbstractTreeTableModel` + +```java +package de.hameister.treetable; +  +import javax.swing.event.EventListenerList; +import javax.swing.event.TreeModelEvent; +import javax.swing.event.TreeModelListener; +import javax.swing.tree.TreePath; +  +public abstract class MyAbstractTreeTableModel implements MyTreeTableModel { +    protected Object root; +    protected EventListenerList listenerList = new EventListenerList(); +  +    private static final int CHANGED = 0; +    private static final int INSERTED = 1; +    private static final int REMOVED = 2; +    private static final int STRUCTURE_CHANGED = 3; +  +    public MyAbstractTreeTableModel(Object root) { +        this.root = root; +    } +  +    public Object getRoot() { +        return root; +    } +  +    public boolean isLeaf(Object node) { +        return getChildCount(node) == 0; +    } +  +    public void valueForPathChanged(TreePath path, Object newValue) { +    } +  +    /** +     * Die Methode wird normalerweise nicht aufgerufen. +     */ +    public int getIndexOfChild(Object parent, Object child) { +        return 0; +    } +  +    public void addTreeModelListener(TreeModelListener l) { +        listenerList.add(TreeModelListener.class, l); +    } +  +    public void removeTreeModelListener(TreeModelListener l) { +        listenerList.remove(TreeModelListener.class, l); +    } +  +    private void fireTreeNode(int changeType, Object source, Object[] path, int[] childIndices, Object[] children) { +        Object[] listeners = listenerList.getListenerList(); +        TreeModelEvent e = new TreeModelEvent(source, path, childIndices, children); +        for (int i = listeners.length - 2; i >= 0; i -= 2) { +            if (listeners[i] == TreeModelListener.class) { +  +                switch (changeType) { +                case CHANGED: +                    ((TreeModelListener) listeners[i + 1]).treeNodesChanged(e); +                    break; +                case INSERTED: +                    ((TreeModelListener) listeners[i + 1]).treeNodesInserted(e); +                    break; +                case REMOVED: +                    ((TreeModelListener) listeners[i + 1]).treeNodesRemoved(e); +                    break; +                case STRUCTURE_CHANGED: +                    ((TreeModelListener) listeners[i + 1]).treeStructureChanged(e); +                    break; +                default: +                    break; +                } +  +            } +        } +    } +  +    protected void fireTreeNodesChanged(Object source, Object[] path, int[] childIndices, Object[] children) { +        fireTreeNode(CHANGED, source, path, childIndices, children); +    } +  +    protected void fireTreeNodesInserted(Object source, Object[] path, int[] childIndices, Object[] children) { +        fireTreeNode(INSERTED, source, path, childIndices, children); +    } +  +    protected void fireTreeNodesRemoved(Object source, Object[] path, int[] childIndices, Object[] children) { +        fireTreeNode(REMOVED, source, path, childIndices, children); +    } +  +    protected void fireTreeStructureChanged(Object source, Object[] path, int[] childIndices, Object[] children) { +        fireTreeNode(STRUCTURE_CHANGED, source, path, childIndices, children); +    } +  +} +``` + +下面的类定义了视图的具体数据模型。意味着该类定义了每个数据列,以及它们对应的数据类型。同时该类还实现了接口中尚未被实现的方法。 + +```java +package de.hameister.treetable; +  +import java.util.Date; +  +public class MyDataModel extends MyAbstractTreeTableModel { +    // Spalten Name. +    static protected String[] columnNames = { "Knotentext", "String", "Datum", "Integer" }; +  +    // Spalten Typen. +    static protected Class[] columnTypes = { MyTreeTableModel.class, String.class, Date.class, Integer.class }; +  +    public MyDataModel(MyDataNode rootNode) { +        super(rootNode); +        root = rootNode; +    } +  +    public Object getChild(Object parent, int index) { +        return ((MyDataNode) parent).getChildren().get(index); +    } +  +  +    public int getChildCount(Object parent) { +        return ((MyDataNode) parent).getChildren().size(); +    } +  +  +    public int getColumnCount() { +        return columnNames.length; +    } +  +  +    public String getColumnName(int column) { +        return columnNames[column]; +    } +  +  +    public Class getColumnClass(int column) { +        return columnTypes[column]; +    } +  +    public Object getValueAt(Object node, int column) { +        switch (column) { +        case 0: +            return ((MyDataNode) node).getName(); +        case 1: +            return ((MyDataNode) node).getCapital(); +        case 2: +            return ((MyDataNode) node).getDeclared(); +        case 3: +            return ((MyDataNode) node).getArea(); +        default: +            break; +        } +        return null; +    } +  +    public boolean isCellEditable(Object node, int column) { +        return true; // Important to activate TreeExpandListener +    } +  +    public void setValueAt(Object aValue, Object node, int column) { +    } +  +} +``` + +下面的类是一个简单的值对象,通过一些get/set方法保存数据节点。 + +```java +package de.hameister.treetable; +  +import java.util.Collections; +import java.util.Date; +import java.util.List; +  +public class MyDataNode { +  +    private String name; +    private String capital; +    private Date declared; +    private Integer area; +  +    private List children; +  +    public MyDataNode(String name, String capital, Date declared, Integer area, List children) { +        this.name = name; +        this.capital = capital; +        this.declared = declared; +        this.area = area; +        this.children = children; +  +        if (this.children == null) { +            this.children = Collections.emptyList(); +        } +    } +  +    public String getName() { +        return name; +    } +  +    public String getCapital() { +        return capital; +    } +  +    public Date getDeclared() { +        return declared; +    } +  +    public Integer getArea() { +        return area; +    } +  +    public List getChildren() { +        return children; +    } +  +    /** +     * Knotentext vom JTree. +     */ +    public String toString() { +        return name; +    } +} +``` + +到此为止,数据模型部分已经准备完毕。在main方法所在的类中,我们可以创建包含节点的数据模型了。负责呈现界面的类将会在后面进行描述。在实际是使用过程中,数据结构不会在开始就通过一个方法就完整的创建出来,大部分场景下是需要在运行过程中通过数据库获取这些数据。 + +```java +package de.hameister.treetable; +  +import java.awt.Container; +import java.awt.GridLayout; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +  +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +  +public class TreeTableMain extends JFrame { +  +  +    public TreeTableMain() { +        super("Tree Table Demo"); +  +        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); +  +        setLayout(new GridLayout(0, 1)); +  +        MyAbstractTreeTableModel treeTableModel = new MyDataModel(createDataStructure()); +  +        MyTreeTable myTreeTable = new MyTreeTable(treeTableModel); +  +        Container cPane = getContentPane(); +  +        cPane.add(new JScrollPane(myTreeTable)); +  +        setSize(1000, 800); +        setLocationRelativeTo(null); +  +  +    } +  +  +    private static MyDataNode createDataStructure() { +        List children1 = new ArrayList(); +        children1.add(new MyDataNode("N12", "C12", new Date(), Integer.valueOf(50), null)); +        children1.add(new MyDataNode("N13", "C13", new Date(), Integer.valueOf(60), null)); +        children1.add(new MyDataNode("N14", "C14", new Date(), Integer.valueOf(70), null)); +        children1.add(new MyDataNode("N15", "C15", new Date(), Integer.valueOf(80), null)); +  +        List children2 = new ArrayList(); +        children2.add(new MyDataNode("N12", "C12", new Date(), Integer.valueOf(10), null)); +        children2.add(new MyDataNode("N13", "C13", new Date(), Integer.valueOf(20), children1)); +        children2.add(new MyDataNode("N14", "C14", new Date(), Integer.valueOf(30), null)); +        children2.add(new MyDataNode("N15", "C15", new Date(), Integer.valueOf(40), null)); +  +        List rootNodes = new ArrayList(); +        rootNodes.add(new MyDataNode("N1", "C1", new Date(), Integer.valueOf(10), children2)); +        rootNodes.add(new MyDataNode("N2", "C2", new Date(), Integer.valueOf(10), children1)); +        rootNodes.add(new MyDataNode("N3", "C3", new Date(), Integer.valueOf(10), children2)); +        rootNodes.add(new MyDataNode("N4", "C4", new Date(), Integer.valueOf(10), children1)); +        rootNodes.add(new MyDataNode("N5", "C5", new Date(), Integer.valueOf(10), children1)); +        rootNodes.add(new MyDataNode("N6", "C6", new Date(), Integer.valueOf(10), children1)); +        rootNodes.add(new MyDataNode("N7", "C7", new Date(), Integer.valueOf(10), children1)); +        rootNodes.add(new MyDataNode("N8", "C8", new Date(), Integer.valueOf(10), children1)); +        rootNodes.add(new MyDataNode("N9", "C9", new Date(), Integer.valueOf(10), children1)); +        rootNodes.add(new MyDataNode("N10", "C10", new Date(), Integer.valueOf(10), children1)); +        rootNodes.add(new MyDataNode("N11", "C11", new Date(), Integer.valueOf(10), children1)); +        rootNodes.add(new MyDataNode("N12", "C7", new Date(), Integer.valueOf(10), children1)); +        rootNodes.add(new MyDataNode("N13", "C8", new Date(), Integer.valueOf(10), children1)); +        rootNodes.add(new MyDataNode("N14", "C9", new Date(), Integer.valueOf(10), children1)); +        rootNodes.add(new MyDataNode("N15", "C10", new Date(), Integer.valueOf(10), children1)); +        rootNodes.add(new MyDataNode("N16", "C11", new Date(), Integer.valueOf(10), children1)); +        MyDataNode root = new MyDataNode("R1", "R1", new Date(), Integer.valueOf(10), rootNodes); +  +        return root; +    } +  +    public static void main(final String[] args) { +        Runnable gui = new Runnable() { +  +            public void run() { +                try { +                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); +                } catch (Exception e) { +                    e.printStackTrace(); +                } +                new TreeTableMain().setVisible(true); +            } +        }; +        SwingUtilities.invokeLater(gui); +    } +} +``` + +因为TreeTable组件是由JTree组件和JTable组件组合而成, +Since the TreeTable is composed of a JTree component and a JTable component, it must be ensured that a continuous row is always marked when selecting the tree or table. To ensure this, create a class that extends the DefaultTreeSelectionModel. This SelectionModel is later assigned to the JTree and the JTable. MyTreeTableSelectionModel + +```java +package de.hameister.treetable; +  +import javax.swing.ListSelectionModel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.tree.DefaultTreeSelectionModel; +  +public class MyTreeTableSelectionModel extends DefaultTreeSelectionModel { +  +    public MyTreeTableSelectionModel() { +        super(); +  +        getListSelectionModel().addListSelectionListener(new ListSelectionListener() { +            @Override +            public void valueChanged(ListSelectionEvent e) { +  +            } +        }); +    } +  +    ListSelectionModel getListSelectionModel() { +        return listSelectionModel; +    } +} +``` + +To enable the opening of the tree, you need a . That's why you create a class that extends and implements the interface. The only function of the class is to forward a double click to the tree. The method checks whether the first column () has been clicked. If this is the case, a double click is forwarded to the so that they can react. AbstractCellEditorMyTreeTableCellEditorAbstractCellEditorTableCellEditorMyTreeTableCellEditorisCellEditablecolumn1treeExpansionListener + +```java +package de.hameister.treetable; +  +import java.awt.Component; +import java.awt.event.MouseEvent; +import java.util.EventObject; +  +import javax.swing.AbstractCellEditor; +import javax.swing.JTable; +import javax.swing.JTree; +import javax.swing.table.TableCellEditor; +  +public class MyTreeTableCellEditor extends AbstractCellEditor implements TableCellEditor { +  +    private JTree tree; +    private JTable table; +  +    public MyTreeTableCellEditor(JTree tree, JTable table) { +        this.tree = tree; +        this.table = table; +    } +  +    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int r, int c) { +        return tree; +    } +  +    public boolean isCellEditable(EventObject e) { +        if (e instanceof MouseEvent) { +            int colunm1 = 0; +            MouseEvent me = (MouseEvent) e; +            int doubleClick = 2; +            MouseEvent newME = new MouseEvent(tree, me.getID(), me.getWhen(), me.getModifiers(), me.getX() - table.getCellRect(0, colunm1, true).x, me.getY(), doubleClick, me.isPopupTrigger()); +            tree.dispatchEvent(newME); +        } +        return false; +    } +  +    @Override +    public Object getCellEditorValue() { +        return null; +    } +  +} +``` + +Since in Java Swing the GUI components still required a Model, which is unlike the actual data model, a class , which inherits from , is now created. This class is later used in the class as a model for the . If the TreeTable later asks for values to be displayed, it must be distinguished whether the requested values can be delivered by the tree or directly by the data model. In addition, the class is still generated and registered. This reacts to clicks in the tree and ensures that the tree is opened and closed. MyTreeTableModelAdapterAbstractTableModelMyTreeTableJTableMyAbstractTreeTableModelTreeExpansionListener + +```java +package de.hameister.treetable; +  +import java.awt.Rectangle; +  +import javax.swing.JTree; +import javax.swing.event.TreeExpansionEvent; +import javax.swing.event.TreeExpansionListener; +import javax.swing.table.AbstractTableModel; +import javax.swing.tree.TreePath; +  +public class MyTreeTableModelAdapter extends AbstractTableModel { +  +     JTree tree; +    MyAbstractTreeTableModel treeTableModel; +  +    public MyTreeTableModelAdapter(MyAbstractTreeTableModel treeTableModel, JTree tree) { +        this.tree = tree; +        this.treeTableModel = treeTableModel; +  +        tree.addTreeExpansionListener(new TreeExpansionListener() { +            public void treeExpanded(TreeExpansionEvent event) { +                fireTableDataChanged(); +            } +  +            public void treeCollapsed(TreeExpansionEvent event) { +                fireTableDataChanged(); +            } +        }); +    } +  +  +  +    public int getColumnCount() { +        return treeTableModel.getColumnCount(); +    } +  +    public String getColumnName(int column) { +        return treeTableModel.getColumnName(column); +    } +  +    public Class getColumnClass(int column) { +        return treeTableModel.getColumnClass(column); +    } +  +    public int getRowCount() { +        return tree.getRowCount(); +    } +  +    protected Object nodeForRow(int row) { +        TreePath treePath = tree.getPathForRow(row); +        return treePath.getLastPathComponent(); +    } +  +    public Object getValueAt(int row, int column) { +        return treeTableModel.getValueAt(nodeForRow(row), column); +    } +  +    public boolean isCellEditable(int row, int column) { +        return treeTableModel.isCellEditable(nodeForRow(row), column); +    } +  +    public void setValueAt(Object value, int row, int column) { +        treeTableModel.setValueAt(value, nodeForRow(row), column); +    } +} +``` + +Finally, the JTree and the JTable have to be created. The Tree component inherits from and implements the interface. This class ensures that the row heights of Tree and Table are the same and that the background colors are set correctly during selection. In addition, it is ensured that the elements of the tree are correctly indented depending on the level. MyTreeTableCellRendererJTreeTableCellRenderer + +```java +package de.hameister.treetable; +  +import java.awt.Component; +import java.awt.Graphics; +  +import javax.swing.JTable; +import javax.swing.JTree; +import javax.swing.table.TableCellRenderer; +import javax.swing.tree.TreeModel; +  +  +public class MyTreeTableCellRenderer extends JTree implements TableCellRenderer { +    /** Die letzte Zeile, die gerendert wurde. */ +    protected int visibleRow; +  +    private MyTreeTable treeTable; +  +    public MyTreeTableCellRenderer(MyTreeTable treeTable, TreeModel model) { +        super(model); +        this.treeTable = treeTable; +  +        // Setzen der Zeilenhoehe fuer die JTable +        // Muss explizit aufgerufen werden, weil treeTable noch +        // null ist, wenn super(model) setRowHeight aufruft! +        setRowHeight(getRowHeight()); +    } +  +    /** +     * Tree und Table muessen die gleiche Hoehe haben. +     */ +    public void setRowHeight(int rowHeight) { +        if (rowHeight > 0) { +            super.setRowHeight(rowHeight); +            if (treeTable != null && treeTable.getRowHeight() != rowHeight) { +                treeTable.setRowHeight(getRowHeight()); +            } +        } +    } +  +    /** +     * Tree muss die gleiche Hoehe haben wie Table. +     */ +    public void setBounds(int x, int y, int w, int h) { +        super.setBounds(x, 0, w, treeTable.getHeight()); +    } +  +    /** +     * Sorgt fuer die Einrueckung der Ordner. +     */ +    public void paint(Graphics g) { +        g.translate(0, -visibleRow * getRowHeight()); +  +        super.paint(g); +    } +  +    /** +     * Liefert den Renderer mit der passenden Hintergrundfarbe zurueck. +     */ +    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { +        if (isSelected) +            setBackground(table.getSelectionBackground()); +        else +            setBackground(table.getBackground()); +  +        visibleRow = row; +        return this; +    } +} +``` + +Now that the data model, the auxiliary components and the Main class are in place, the actual TreeTable is still missing. For this purpose, the class is created. This inherits from . Since multiple inheritance is not possible with Java, the Tree component is included in the class via an association. The data model is passed to both the (Tree) and the Object (Table). The class is set as a model. For the simultaneous selection of Tree and Table, this is used for Tree and Table. Then you have to set a default renderer for the tree and set a default editor for the table. MyTreeTableJTableMyTreeTableCellRendererMyAbstractTreeTableModelMyTreeTableCellRendererMyTreeTableModelAdapterMyTreeTableModelAdapterMyTreeTableSelectionModel + +```java +package de.hameister.treetable; +  +import java.awt.Dimension; +  +import javax.swing.JTable; +  +public class MyTreeTable extends JTable { +  +    private MyTreeTableCellRenderer tree; +  +  +    public MyTreeTable(MyAbstractTreeTableModel treeTableModel) { +        super(); +  +        // JTree erstellen. +        tree = new MyTreeTableCellRenderer(this, treeTableModel); +  +        // Modell setzen. +        super.setModel(new MyTreeTableModelAdapter(treeTableModel, tree)); +  +        // Gleichzeitiges Selektieren fuer Tree und Table. +        MyTreeTableSelectionModel selectionModel = new MyTreeTableSelectionModel(); +        tree.setSelectionModel(selectionModel); //For the tree +        setSelectionModel(selectionModel.getListSelectionModel()); //For the table +  +  +        // Renderer fuer den Tree. +        setDefaultRenderer(MyTreeTableModel.class, tree); +        // Editor fuer die TreeTable +        setDefaultEditor(MyTreeTableModel.class, new MyTreeTableCellEditor(tree, this)); +  +        // Kein Grid anzeigen. +        setShowGrid(false); +  +        // Keine Abstaende. +        setIntercellSpacing(new Dimension(0, 0)); +  +    } +} +``` + +If the individual classes are now compiled with , then one should have a working TreeTable component. But as already indicated above, it is not to be understood that such a component is not part of Java. I find it incredible that you have to implement 8 classes to get a really simple TreeTable. It should be noted that functionalities such as javac + +edit +Connected rows and columns +Colored rows and columns +SwingWorker for long-running expansion events +Icons in the tree +Swing components in the Table (ComboBox, Images, ...) +have not yet been taken into account at all. It's easy to imagine how much source code and time would have to go into these features in order to have a working component. diff --git "a/_drafts/Java/20\345\210\206\351\222\237\347\220\206\346\270\205Maven\346\236\204\345\273\272\344\270\255\347\232\204\346\265\213\350\257\225\347\233\270\345\205\263\345\267\245\345\205\267\347\232\204\345\205\263\347\263\273.md" "b/_drafts/Java/20\345\210\206\351\222\237\347\220\206\346\270\205Maven\346\236\204\345\273\272\344\270\255\347\232\204\346\265\213\350\257\225\347\233\270\345\205\263\345\267\245\345\205\267\347\232\204\345\205\263\347\263\273.md" new file mode 100644 index 0000000..619f241 --- /dev/null +++ "b/_drafts/Java/20\345\210\206\351\222\237\347\220\206\346\270\205Maven\346\236\204\345\273\272\344\270\255\347\232\204\346\265\213\350\257\225\347\233\270\345\205\263\345\267\245\345\205\267\347\232\204\345\205\263\347\263\273.md" @@ -0,0 +1,105 @@ +如果你用Maven进行系统构建,同时还要同步编写测试用例,获取用例成功与否以及用例覆盖率的相关报告,那么这些工具你肯定接触过不少: + +* JUnit +* TestNG +* maven-surefire-plugin +* maven-surefire-report-plugin +* emma-maven-plugin +* jacoco-maven-plugin +* cobertura-maven-plugin +* maven-project-info-reports-plugin +* maven-site-plugin + +是不是已经有些头晕了?没关系,我之前担任过很长时间的CI构建系统维护者,同时还是这里面几个Maven插件的Maintainer,我将尽最大努力帮你在最短的时间里理清他们的关系,帮助大家在实际项目中充分发挥这些工具的作用,提升产品质量。首先我们先大体分几个类别,让大家有个大概的印象。 + +* JUnit/TestNG是单元测试工具,可以帮你更方便的编写测试用例。 +* maven-surefire-plugin是一个Maven插件,帮你在通过Maven构建项目的时候自动执行之前编写的测试用例。它的输出文件是txt或者xml格式的测试报告。 +* maven-surefire-report-plugin是一个Maven插件,帮你将maven-surefire-plugin生成的测试报告转换为html格式,方便查看。 +* emma-maven-plugin/jacoco-maven-plugin/cobertura-maven-plugin这些是帮助生成测试覆盖率报告的Maven插件,他们的输出文件也是html格式。 +* maven-project-info-reports-plugin是一个Maven插件,他可以把maven-surefire-report-plugin/emma-maven-plugin/jacoco-maven-plugin/cobertura-maven-plugin这些插件生成的html格式文件进行汇总,帮助大家以更好的方式呈现。 +* maven-site-plugin是一个Maven插件,帮你自动生成、部署、启动你的站点,站点里面可以有很多东西,其中最重要的部分就是你使用maven-project-info-reports-plugin生成的项目报告。 + +接下来我们根据之前的分类详细了解下他们: + +## JUnit/TestNG + +* JUnit: +* TestNG: + +JUnit是老牌单元测试工具了,从3.X,4.X到最新的5.X版本,一直有广泛的群众基础。TestNG和JUnit一样,也是为了方便编写单元测试用例的Java类库,诞生之初就宣称是JUnit的下一代产品,事实也确实如此,在功能和易用性方面,TestNG都要强过于4.X及其更早版本的JUnit,网上有很多两者的对比文章,其中比较有名的一篇如下,感兴趣的话大家可以仔细阅读下: + +* JUnit 4 Vs TestNG – Comparison: + +JUnit面对市场份额被TestNG不断蚕食的情况,研发了更加强大的JUnit 5版本,补齐了能力差距,易用性方面也有了更大的提升。因此大家如果可以选择的话,我个人推荐大家直接使用JUnit 5,功能和易用性丝毫不逊色,还有广大的群众基础,出现问题也方便更快的搜索到解决方案。那你用JUnit或者TestNG工具写完了单元测试用例,怎么执行呢?你可以在各种IDE(比如Eclipse或者IntelliJ IDEA)使用对应的插件来帮助运行这些测试用例,当然你甚至都可以不用IDE,配置好依赖,直接命令行运行这些测试用例。 + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/611264-20200823170134052-10990627.png) + +但是如何在Maven项目中自动执行这些步骤呢?这就是maven-surefire-plugin所做的事情了。 + +## maven-surefire-plugin + +* maven-surefire-plugin: + +maven-surefire-plugin是Maven内嵌的一个插件,会帮助你在执行mvn test的时候自动执行用JUnit/TestNG编写的测试用例。 + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/611264-20200823170247317-1745411381.png) + +除了将用例执行结果打印到屏幕上之外,还会在`target\surefire-reports`目录下生成两种形式的用例执行结果: + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/611264-20200823170314097-523339706.png) + +但是xml和txt格式的报告文件不太方便查看,要是能以html格式展示就好,所以你需要maven-surefire-report-plugin + +## maven-surefire-report-plugin + +* maven-surefire-report-plugin: + +maven-surefire-report-plugin的输入就是maven-surefire-plugin所产生的的xml后者txt格式的用例执行报告,输出就是html格式的用例执行报告。当你执行`surefire-report:report`时,你就会获得一个html格式的文件: + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/611264-20200823170358803-2126291467.png) + +内容如下: + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/611264-20200823171109951-322803827.png) + +是不是感觉很丑?没有关系,我们可以利用maven-project-info-reports-plugin让他变得漂亮些。 + +## maven-site-plugin/maven-project-info-reports-plugin + +* maven-site-plugin: +* maven-project-info-reports-plugin: + +maven-site-plugin主要作用是帮你迅速生成(site:site)一个站点,并完成站点的部署(site:deploy)和启动(site:run)。当你执行site:site时,maven会同时调用maven-project-info-reports-plugin(所以如非定制要求,你可以不在pom.xml中配置maven-project-info-reports-plugin的依赖),生成一个项目框架结构,类似这种: + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/611264-20200823170514421-278511798.png) + +其实maven-project-info-reports-plugin还能根据你在pom.xml中配置的``配置的report插件,生成各种各样的项目报告,比如刚才说的maven-surefire-plugin生成的用例执行报告,和马上要说的用例覆盖报告。是不是感觉这里面展示的报告样式比直接打开surefire-report.html好多了? + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/611264-20200823170537517-1970699000.png) + +## [Emma/emma-maven-plugin][Jacoco/jacoco-maven-plugin][Cobertura/cobertura-maven-plugin] + +* Emma: +* emma-maven-plugin: +* JaCoCo/jacoco-maven-plugin: +* Cobertura: +* cobertura-maven-plugin: + +这坨东西可以分为两类,一类就是检查Java用例覆盖情况的工具,另一类就是为了方便maven项目使用,开发出来的对应maven插件。其中Emma和emma-maven-plugin都很长时间没人维护了,所以我fork了这两个项目,并打算继续维护,目前为止已经解决了一些常见问题: + +* Emma: +* emma-maven-plugin: + +JaCoCo/jacoco-maven-plugin是同一个团队(EclEmma)在维护,EclEmma原本是一个Emma在Eclipse上的插件,但是在插件开发过程中越来越发现Emma在框架结构上的缺陷无法彻底解决,于是另起炉灶开发了JaCoCo,也就是说EclEmma现在除了名字外已经和Emma没有关系了。 + +三类用例覆盖工具中JaCoCo是最新的,也是对Java 8+新标准支持最好的,但是另外两个工具也有其特长,大家工作中根据实际情况选择即可。 + +## 远不止这些 + +前面介绍了测试用例执行情况和用例覆盖情况的报告,其实为了提升项目质量,可以提供很多种报告供大家使用,比如静态检查的findbugs、PMD等,我之前写过一个更加全面的工具汇总文章,大家可以参考: + + +理论说了一大堆,大家有没有一种冲动,把这些工具引用到自己的项目中去?这边有个例子供大家参考: + +* 代码: +* 报告: diff --git "a/_drafts/Java/Calendar\347\261\273\344\270\255add_set_roll\346\226\271\346\263\225\347\232\204\345\214\272\345\210\253.md" "b/_drafts/Java/Calendar\347\261\273\344\270\255add_set_roll\346\226\271\346\263\225\347\232\204\345\214\272\345\210\253.md" new file mode 100644 index 0000000..79cfa55 --- /dev/null +++ "b/_drafts/Java/Calendar\347\261\273\344\270\255add_set_roll\346\226\271\346\263\225\347\232\204\345\214\272\345\210\253.md" @@ -0,0 +1,60 @@ + +Calendar类中有三个方法更改日期的某个字段:set()、add() 和 roll()。 + +set(f, value) 将日历字段 f 更改为 value。此外,它设置了一个内部成员变量,以指示日历字段 f 已经被更改。尽管日历字段 f 是立即更改的,但是直到下次调用 get()、getTime()、getTimeInMillis()、add() 或 roll()时才会重新计算日历的时间值(以毫秒为单位)。因此,多次调用 set() 不会触发多次不必要的计算。使用 set()更改日历字段的结果是,其他日历字段也可能发生更改,这取决于日历字段、日历字段值和日历系统。此外,在重新计算日历字段之后,get(f) 没必要通过调用 set 方法返回 value 集合。具体细节是通过具体的日历类确定的。 + +示例:假定 GregorianCalendar 最初被设置为 1999 年 8 月 31 日。调用 set(Calendar.MONTH, Calendar.SEPTEMBER) 将该日期设置为 1999 年 9 月 31 日。如果随后调用 getTime(),那么这是解析 1999 年 10 月 1 日的一个暂时内部表示。但是,在调用 getTime() 之前调用 set(Calendar.DAY_OF_MONTH, 30) 会将该日期设置为 1999 年 9 月 30 日,因为在调用 set() 之后没有发生重新计算。 + +add(f, delta) 将 delta 添加到 f 字段中。这等同于调用 set(f, get(f) + delta),但要带以下两个调整: + +* Add 规则 1。调用后 f 字段的值减去调用前 f 字段的值等于 delta,以字段 f 中发生的任何溢出为模。溢出发生在字段值超出其范围时,结果,下一个更大的字段会递增或递减,并将字段值调整回其范围内。 + +* Add 规则 2。如果期望某一个更小的字段是不变的,但让它等于以前的值是不可能的,因为在字段 f发生更改之后,或者在出现其他约束之后,比如时区偏移量发生更改,它的最大值和最小值也在发生更改,然后它的值被调整为尽量接近于所期望的值。更小的字段表示一个更小的时间单元。HOUR 是一个比 DAY_OF_MONTH 小的字段。对于不期望是不变字段的更小字段,无需进行任何调整。日历系统会确定期望不变的那些字段。 + +此外,与 set() 不同,add() 强迫日历系统立即重新计算日历的毫秒数和所有字段。 + +示例:假定 GregorianCalendar 最初被设置为 1999 年 8 月 31 日。调用 add(Calendar.MONTH, 13) 将日历设置为 2000 年 9 月 30 日。Add 规则 1 将 MONTH 字段设置为 September,因为向 August 添加 13 个月得出的就是下一年的 September。因为在 GregorianCalendar 中,DAY_OF_MONTH 不可能是 9 月 31 日,所以 add 规则 2 将DAY_OF_MONTH 设置为 30,即最可能的值。尽管它是一个更小的字段,但不能根据规则 2 调整 DAY_OF_WEEK,因为在 GregorianCalendar 中的月份发生变化时,该值也需要发生变化。 + +roll(f, delta) 将 delta 添加到 f 字段中,但不更改更大的字段。这等同于调用 add(f, delta),但要带以下调整: + +* Roll 规则。在完成调用后,更大的字段无变化。更大的字段表示一个更大的时间单元。DAY_OF_MONTH是一个比 HOUR 大的字段。 + +示例:请参阅 GregorianCalendar.roll(int, int)。 + +使用模型。为了帮助理解 add() 和 roll() 的行为,假定有一个用户界面组件,它带有用于月、日、年和底层GregorianCalendar 的递增或递减按钮。如果从界面上读取的日期为 1999 年 1 月 31 日,并且用户按下月份的递增按钮,那么应该得到什么?如果底层实现使用 set(),那么可以将该日期读为 1999 年 3 月 3 日。更好的结果是 1999 年 2 月 28 日。此外,如果用户再次按下月份的递增按钮,那么该日期应该读为 1999 年 3 月 31 日,而不是 1999 年 3 月 28 日。通过保存原始日期并使用 add() 或 roll(),根据是否会影响更大的字段,用户界面可以像大多数用户所期望的那样运行。 + +假设:f= 2001-1-30 +f.add(Calendar.MONTH, 13) = 2002.2.28 +f.set(Calendar.MONTH,1) = 2002.3.2 +f.roll(Calendar.MONTH, 13) = 2001.2.28 + +Add:修改后如果符合实际,会调整,但不会改变调整的值,如例子中的MONTH。 +Set: 会改变如把2月改为3月。 +roll:于Add类似,不同在于不会改变更大的日期单位,如还是2001 不会为2002。 + +下面为代码演示: + +```java + Calendar c=Calendar.getInstance(); + //c.setTimeInMillis(System.currentTimeMillis()); + + c.set(2001,0,30); + c.add(Calendar.MONTH, 13); + System.out.println(c.getTime().toString()); + c.set(2001,0,30); + c.set(Calendar.MONTH,1); + System.out.println(c.getTime().toString()); + c.set(2001,0,30); + c.roll(Calendar.MONTH, 13); + System.out.println(c.getTime().toString()); +``` + +结果: + +``` + Thu Feb 28 10:22:37 CST 2002 + Fri Mar 02 10:22:37 CST 2001 + Wed Feb 28 10:22:37 CST 2001 +``` + +注意,Calendar.MONTH是从0开始的,也就是说一月用0表示 \ No newline at end of file diff --git "a/_drafts/Java/Eclipse+Spark\346\220\255\345\273\272\346\272\220\347\240\201\345\210\206\346\236\220\347\216\257\345\242\203\351\227\256\351\242\230\345\210\206\346\236\220.md" "b/_drafts/Java/Eclipse+Spark\346\220\255\345\273\272\346\272\220\347\240\201\345\210\206\346\236\220\347\216\257\345\242\203\351\227\256\351\242\230\345\210\206\346\236\220.md" new file mode 100644 index 0000000..9fa6e1f --- /dev/null +++ "b/_drafts/Java/Eclipse+Spark\346\220\255\345\273\272\346\272\220\347\240\201\345\210\206\346\236\220\347\216\257\345\242\203\351\227\256\351\242\230\345\210\206\346\236\220.md" @@ -0,0 +1,25 @@ + +# Scala IDE complains about ‘... is cross-compiled with an incompatible version of Scala ...’ + +* http://scala-ide.org/docs/current-user-doc/faq/index.html + + +# "Cannot run program "bash" ...: CreateProcess error=2" + +[ERROR] Failed to execute goal org.apache.maven.plugins:maven-antrun-plugin:1.8:run (default) on project spark-core_2.11: An Ant BuildException has occured: Execute failed: java.io.IOException: Cannot run program "bash" (in directory "D:\temp\Scala\spark\core"): CreateProcess error=2, 系统找不到指定的文件。 + +* http://blog.csdn.net/xubo245/article/details/52073805 + + +# "Failed to execute goal org.apache.maven.plugins:maven-clean-plugin:3.0.0:clean..." + +[ERROR] Failed to execute goal org.apache.maven.plugins:maven-clean-plugin:3.0.0:clean (default-clean) on project spark-parent_2.10: Failed to clean project: Failed to delete /usr/spark/spark-2.1.0/target/tmp -> [Help 1] + +* http://www.cnblogs.com/o-din/p/6292153.html + + +# "Could not transfer artifact ... from/to central ...: GET request of: ... from central failed: Tag mismatch!" + +[ERROR] Failed to execute goal on project spark-sql_2.11: Could not resolve dependencies for project org.apache.spark:spark-sql_2.11:jar:2.2.0-SNAPSHOT: Could not transfer artifact it.unimi.dsi:fastutil:jar:6.5.7 from/to central (https://repo1.maven.org/maven2): GET request of: it/unimi/dsi/fastutil/6.5.7/fastutil-6.5.7.jar from central failed: Tag mismatch! -> [Help 1] + +删除fastutil-6.5.7.jar重新下载 \ No newline at end of file diff --git "a/_drafts/Java/Eclipse\344\270\255Ant\347\232\204\351\205\215\347\275\256\344\270\216\346\265\213\350\257\225 \350\275\254.md" "b/_drafts/Java/Eclipse\344\270\255Ant\347\232\204\351\205\215\347\275\256\344\270\216\346\265\213\350\257\225 \350\275\254.md" new file mode 100644 index 0000000..1306fc1 --- /dev/null +++ "b/_drafts/Java/Eclipse\344\270\255Ant\347\232\204\351\205\215\347\275\256\344\270\216\346\265\213\350\257\225 \350\275\254.md" @@ -0,0 +1,125 @@ + +Ant是Java平台下非常棒的批处理命令执行程序,能非常方便地自动完成编译,测试,打包,部署等等一系列任务,大大提高开发效率。如果你现在还没有开始使用Ant,那就要赶快开始学习使用,使自己的开发水平上一个新台阶。Eclipse中已经集成了Ant,我们可以直接在Eclipse中运行Ant。以前面建立的Hello工程为例,创建以下目录结构: + +![](http://images2015.cnblogs.com/blog/611264/201512/611264-20151211222840887-2057018532.jpg) + +新建一个build.xml,放在工程根目录下。build.xml定义了Ant要执行的批处理命令。虽然Ant也可以使用其它文件名,但是遵循标准能更使开发更规范,同时易于与别人交流。 通常,src存放Java源文件,classes存放编译后的class文件,lib存放编译和运行用到的所有jar文件,web存放JSP等web文件,dist存放打包后的jar文件,doc存放API文档。然后在根目录下创建build.xml文件,输入以下内容: + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + *********************************************************** + **** One or more tests failed! Check the output ... **** + *********************************************************** + + + + + + + + + + + + + + + + + + Hello, test]]> + All Rights Reserved.]]> + + + + + +``` + +选中Hello工程,然后选择“Project”,“Properties”,“Builders”,“New…”,选择“Ant Build”: + +![](http://images2015.cnblogs.com/blog/611264/201512/611264-20151211222905012-466425527.jpg) + +填入Name:Ant_Builder;Buildfile:build.xml;Base Directory:${workspace_loc:/Hello}(按“Browse Workspace”选择工程根目录),由于用到了junit.jar包,搜索Eclipse目录,找到junit.jar,把它复制到Hello/lib目录下,并添加到Ant的Classpath中: + +![](http://images2015.cnblogs.com/blog/611264/201512/611264-20151211222917434-787575864.jpg) + +然后在Builder面板中钩上Ant_Build,去掉Java Builder: + +![](http://images2015.cnblogs.com/blog/611264/201512/611264-20151211222930747-1268707877.jpg) + +再次编译,即可在控制台看到Ant的输出: + +```shell + +Buildfile: F:\eclipse-projects\Hello\build.xml +init: +compile: +[mkdir] Created dir: F:\eclipse-projects\Hello\classes +[javac] Compiling 2 source files to F:\eclipse-projects\Hello\classes +test: +[mkdir] Created dir: F:\eclipse-projects\Hello\report +[junit] Running example.HelloTest +[junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.02 sec +pack: +[mkdir] Created dir: F:\eclipse-projects\Hello\dist +[jar] Building jar: F:\eclipse-projects\Hello\dist\hello.jar +doc: +[mkdir] Created dir: F:\eclipse-projects\Hello\doc +[javadoc] Generating Javadoc +[javadoc] Javadoc execution +[javadoc] Loading source files for package example... +[javadoc] Constructing Javadoc information... +[javadoc] Standard Doclet version 1.4.2_04 +[javadoc] Building tree for all the packages and classes... +[javadoc] Building index for all the packages and classes... +[javadoc] Building index for all classes... +[javadoc] Generating F:\eclipse-projects\Hello\doc\stylesheet.css... +[javadoc] Note: Custom tags that could override future standard tags: +@todo. To avoid potential overrides, use at least one period character (.) in custom tag names. +[javadoc] Note: Custom tags that were not seen: @todo +BUILD SUCCESSFUL +Total time: 11 seconds + +``` + +Ant依次执行初始化,编译,测试,打包,生成API文档一系列任务,极大地提高了开发效率。将来开发J2EE项目时,还可加入部署等任务。并且,即使脱离了Eclipse环境,只要正确安装了Ant,配置好环境变量ANT_HOME=,Path=…;%ANT_HOME%\bin,在命令行提示符下切换到Hello目录,简单地键入ant即可。 \ No newline at end of file diff --git "a/_drafts/Java/Eclipse\345\205\250\351\235\242\346\217\220\351\200\237\345\260\217\346\212\200\345\267\247.md" "b/_drafts/Java/Eclipse\345\205\250\351\235\242\346\217\220\351\200\237\345\260\217\346\212\200\345\267\247.md" new file mode 100644 index 0000000..d0efb2d --- /dev/null +++ "b/_drafts/Java/Eclipse\345\205\250\351\235\242\346\217\220\351\200\237\345\260\217\346\212\200\345\267\247.md" @@ -0,0 +1,147 @@ +转自:http://rongmayisheng.com/post/eclipse%E5%85%A8%E9%9D%A2%E6%8F%90%E9%80%9F + +你是否经常在等待eclipse的一些操作完成? + +如果你看到这里,说明答案是yes。如果你苦于eclipse中响应很慢的功能,并且想给eclipse提速让开发更舒服些,就请看看下面的内容。 + +注意:可能一般人都建议加大内存。如果可以,你可以买个cpu好点的机器。弄个SSD让你的文件操作更快。 + +我们假设你买不起这些,你所能做的就是启动eclipse实例,所有ubuntu的设置都是基于eclipse 4.3.0版本,build id:I20121031-2000,当然其他平台的版本的设置都差不多。 + + +# Eclipse优化 + +## 插件 + +当我第一次找到强大的插件时,我非常高兴。我安装的越来越多后,eclipse就用起来不舒服了。所以你可以从众多的插件中禁用一些不常用的插件,禁用不代表删除,你仍然可以启用他们。 + +Window -> Preferences -> General -> Startup and Shutdown + +![](http://images2015.cnblogs.com/blog/611264/201604/611264-20160406215313750-912033735.jpg) +禁用不常用的eclipse启动插件 + +一些插件可能在尝试体验时用一用,但是后来可能在也不用了,这种情况可以把它删掉。 + +Help -> About Eclipse SDK -> Instalation Details -> Select plugin -> Uninstall + +![](http://images2015.cnblogs.com/blog/611264/201604/611264-20160406215333453-931954978.jpg) +卸载eclipse插件 + +## eclipse.ini + +下面的优化都需要修改eclipse所在目录下的eclipse.ini文件。 + +给eclipse执行jvm。它可以让你使用自己的jdk,而不是系统环境变量所指定的jdk + +-vm +/path/to/your/java +使用最新的jdk来运行eclipse。使用最新的jdk要好很多。 + +使用sun的jdk来运行ecipse。原因同上。 + +配置jvm虚拟机的启动参数。你可以自定义虚拟机参数,如果你觉得他们更合适(虚拟机参数介绍)。我使用下面的启动参数来增加堆的大小至768Mb,perm区设置为256Mb(内存总大小为3Gb) + +-vmargs +-Xms768m +-Xmx768m +-XX:PermSize=256m +-XX:MaxPermSize=256m +你可以添加-Xverify:none参数来跳过jvm对class文件的校验,以此提升eclipse的启动速度,但这是很不安全的。 + +你还可以通过测试不同的垃圾回收器策略、server参数来测试eclipse的性能差异。以下为实验过程中使用的部分参数: + +-server +-XX:+UnlockExperimentalVMOptions +-XX:+UseG1GC +-XX:+UseParallelGC +-XX:+UseFastAccessorMethods +-Xss2m +可以在这里查看所有的eclipse运行时参数,选择适合你的参数。 + + +## 禁用动画 + +动画很酷,但如果可以的话,我总是在所有的工具中禁用动画。所以classic主题是我最常用的主题。 + +Window -> Preferences -> General -> Appearance -> Uncheck 'Enable animations' + +![](http://images2015.cnblogs.com/blog/611264/201604/611264-20160406215358672-345703309.jpg) +设置eclipse主题 + +## 禁用label decoration + +label decoration是项目、文件、类层级上的小图标,它可以有益于显性化文件的状态。比如:文件是否已经提交到git。很多插件都提供了这个功能,但很少有用。你可以仅留下你想要的,其他的禁用。 + +Window -> Preferences -> General -> Appearance -> Label Decorations + +![](http://images2015.cnblogs.com/blog/611264/201604/611264-20160406215416437-851189716.jpg) +设置label decoration + +## 自动补全 + +有时在性能较差的机器上,或者当你有很多类的时,自动补全功能性能就会很差。一个很小的优化是减少自动补全的proposal。我仅保留了Java Proposals和Template Proposals: + +Window -> Preferences -> Java -> Editor -> Content Assist -> Advanced + +![](http://images2015.cnblogs.com/blog/611264/201604/611264-20160406215432031-265406027.jpg) +eclipse Content Assist,eclipse自动补全设置 + +## 取消验证器 + +如果你对自己的技术很自信,就可以暂停所有的校验器。就算出现问题,你也可以靠自己的能力定位问题,节省了你的开发时间。 + +Window -> Preferences -> Validation -> Suspend All Validators + +![](http://images2015.cnblogs.com/blog/611264/201604/611264-20160406215447500-1811569106.jpg) +取消eclipse校验器 + +## 关闭不相关的工程 + +如果你仅开发部分eclipse中的工程,那你最好把其他功能关闭掉。他们不会出现在eclipse索引中。 + +你可以在workspace中手动关闭不相关的工程(Close unrelated projects)。但我推荐使用Working Set,你可以添加多个工程到一个Working Set中,这样就可以快速的在Working Set件切换。 + +Right Click on Project -> Assign Working Sets.. +## 关闭编辑器中不用的tab** + +编辑中太多的tab会导致eclipse性能下降,可以这样控制下tab的个数: + +Window -> Preferences -> General -> Editors +勾选Close editors automatically并设置Number of opened tabs为10。 + + +![](http://images2015.cnblogs.com/blog/611264/201604/611264-20160406215502234-3237224.jpg) +控制eclipse编辑器中tab的个数 + +## 禁用拼写检查 + +你还是个程序员吗?我觉得没有任何理由需要拼写检查功能。取消这个功能吧: + +Window -> Preferences -> General -> Editors -> Text Editors -> Spelling -> Uncheck 'Enable spell checking' +## 禁用auto build + +如果你在意什么时候build你的工程,可以这样设置: + +Project -> Uncheck 'Build Automatically' +Window -> Preferences -> Java -> Compiler -> Building -> Uncheck 'Scrub output folders when cleaning' +Window -> Preferences -> Java -> Compiler -> Building -> Uncheck 'Rebuild class files modified by others' +## 快捷键 + +仁者见仁,智者见智。就算你用超快的IDE功能,但如果你要花10个动作才能实现一个操作,那你的开发过程就不算快。 + +把你最常用的动作配置成快捷键,并记住他们,几周的使用后,你的开发效率将由显著提升。 + +Windows -> Preferences -> General -> Keys +为了逼着自己使用所有的快捷键,我直接把工具栏给禁用了。 + +Window -> Hide Toolbar + +# 参考链接 + +* http://wiki.eclipse.org/Eclipse.ini +* http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html +* http://www.beyondlinux.com/2011/06/25/speed-up-your-eclipse-as-a-super-fast-ide/ +* http://blog.normation.com/2010/05/24/optimizing-eclipse-performances/ +* http://stackoverflow.com/questions/142357/what-are-the-best-jvm-settings-for-eclipse/1409590#1409590 + +英文原文:http://mishadoff.com/blog/eclipse-speedup/ \ No newline at end of file diff --git "a/_drafts/Java/Eclipse\347\233\270\345\205\263\351\227\256\351\242\230.md" "b/_drafts/Java/Eclipse\347\233\270\345\205\263\351\227\256\351\242\230.md" new file mode 100644 index 0000000..61eb505 --- /dev/null +++ "b/_drafts/Java/Eclipse\347\233\270\345\205\263\351\227\256\351\242\230.md" @@ -0,0 +1,328 @@ + +## MANIFEST.MF文件 + +在Eclipse的.classpath和runableX.jar中的MANIFEST.MF文件中都指定了可依赖jar包的顺序,所以只要保证需要的jar包排在前面,被classloader首先加载即可。对一普通的jar包,由于MANIFEST.MF没有指定加载顺序,所以必须`java -cp A.jar;C-2.jar;C-1.jar com.jiangxin.classloader.A` + + +## Eclipse 一直提示 loading descriptor for 解决 + +Eclipse左侧的Project Explorer 最右上角有一个小钮,鼠标移上去时提示"View Menu".点一下,在弹出的上下文菜单中选择"Customize View..." 弹出一个对话框.选择: Content 选项卡,在里面把没用的去掉就行了 J2EE WEB loading descriptor . + +## 关于Eclipse配置文件导出问题 + +Eclipse的默认配置一般不能满足我们的要求,我们一般会修改一些配置,如字体、背景颜色、快捷键及一些template等等,这样方便我们的开发。可是当我们新建一个工作空间的时候,Eclipse又会使用默认配置,怎样将我们习惯的配置导出然后导入新工作空间呢? + +方法一:使用eclipse的导出功能。工作目录中右键选择Export->General->Preference,这样可以导出epf文件,新的工作空间中可以用Import导入该配置文件,这个方法的确可以导入绝大多数的配置,但是并不全,导入后会丢失很多配置。 + +方法二:将workspace/.metadata/.plugins/org.eclipse.core.runtime中的.settings文件夹拷贝出来,里面就是所有的配置文件,新建工作空间的时候将该.settings文件夹替换掉新工作空间中的.settings文件夹即可。(有网友是将.plugings文件夹替换,但是.plugings文件夹太大了,实际上就是替换.settings文件夹,.settings只有几百k。)另外导出界面上的工具栏对话框布局等:.metadata\.plugins\org.eclipse.e4.workbench 将该文件夹保存起来即可。 + +## 如何升级Eclipse才能保留之前安装的插件 + +`File->Import->Install->From Exist Installation`,选择旧的Eclipse安装文件夹,这样以前装的插件就都出现了。直接全选安装,瞬间就从本地的安装中把原来的插件都迁移过来了。 +如果中途报错,直接重启,然后一部分一部分导入即可。揪出哪个插件导致的崩溃。 + + +## Eclipse列编辑 + +其实Eclipse也有列编辑功能,不过要3.5以后的版本。要使用Eclipse的列编辑功能,只需要通过快捷键Alt+Shift+a来打开,关闭也一样。有了列编辑功能,就可以对一块代码进行编辑了,比如一块代码的缩进,只需要选中代码块按Tab就可以了,又比如想在每行第二个字符前加入一个“test”,那么只需要向下拖动光标,使定位在每行的第二个字符,然后就可以插入啦。当然,还有更多好玩的功能可以使用,摸索一下就知道了。 + +## Eclipse中修改注释中@author + +`Window-->Preferences-->Java-->Code Style-->Code Templates`,点击Comments,找到Types 然后双击填入以下几个东西,然后在新建类的时候选择`Generate comments`即可 + + /** + * @author 作者的名字 E-mail: 写自己的Email + * @version 创建时间:${date} ${time} + */ + +## Eclipse中 sysout 按alt+/为什么不出System.out.println(); + +需要重新设置快捷键。按快捷键ctrl+shirt+L,然后在按一下L。设置快捷键的对话框就出来了,然你将Word Completion移除,在将Content Assist 这个设置为alt+/。就可以了。 + +1、myeclpse–>Preferences–>General–>Keys。删掉word completion的快捷键设置alt+/ 【这个跟Content Assist起冲突了】 + +2、把Content Assist的快捷键由ctrl+space改成alt+/ + +## Eclipse Unable to install breakpoint in XXX 解决办法 + +我出现的原因是这样:使用ant进行编译,之后就打不了断点,这个是ant的编译eclipse不认。 + +解决方法: + +1.要么删除class文件 重新在eclipse中编译 + +2.在build.xml里的javac标签里加上一句 debug="true" + +## eclipse调试时鼠标移动到变量上不显示值的问题 + +今天同事问一问题,就说在eclipse中调试时,鼠标移动到变量上不显示值,这个原来自己也遇到过,没注意,反正就使用ctrl+shift+i嘛,也可以的,刚查了一下,解决方法如下: + +Window->Preferences->Java->Editor->Hovers 将[Variable Values]选择即可,如果第一个[Combined Hover]已经勾选,则将这个勾去掉,勾选[Variable Values]。如果还不行,就只能用ctrl+shift+i快捷键了。 + +## Eclipse中如何快速替换变量 + +之前用别的开发工具开发的时候,要替换一个变量,直接“Ctr+H”就好了,可是用Eclipse开发的时候,使用这个快捷键,难用的要死,下面给大家介绍一种更加简便的方法在Eclipse中替换变量。 + +选中要替换掉的变量,按下组合键“Alt+Shift+R”,直接在键盘上输入要改为的变量,按回车键“Enter”,就可以完成替换了,文件中的所有变量都被替换完毕。 + +## 如何使用eclipse打开已有工程 + +在开始使用Eclipse的时候,会发现一个问题,那就是如何打开一个现有的Eclipse工程,开始在菜单中找了好久也没找到。 + +其实,Eclipse生成的结果不像VC,Jcreator那样可以直接打开,若要打开非workspace文件夹下的其他已有工程,可以打开菜单file->import→general→existing project into space.在select root directory中选中要打开的文件夹即可。此时如果选择copy existing project into workspace就会同时将文件拷贝到workspace下。这里首先要保证要保证Eclipse两个文件.classpath和.project还在,不然无法导入,就是说Eclipse的import只认自己家的东西。 + +## Eclipse乱码问题 + +我的eclipse在执行System.out.println("中文出现乱码!");时,控制台上打印的都是乱码,这个是什么问题啊!我整个eclipse的工作空间都设为UTF-8了啊!!!好晕啊! 对啊,设置为GBK的就没有问题,我用maven跑工程的时候为什么控制台又不是乱码了?maven的那些工程都是设置的UTF-8的。 + +把整个工程的“Text file encoding”属性设为GBK,就不会有乱码了。设置方法:在eclipse中右击工程,点击弹出框最下面的“Properties”,然后在弹出的窗口左侧点击“Resource”,便可以在窗口的右部看到“Text file encoding”属性,点击“Other”前的单选框,在下拉列表中选择“GBK”。最后,点击右下部的“Apply”,“OK”退出。这样设置后,你再执行System.out.println("中文出现乱码!");时控制台上就不会是乱码了。 + + + +Eclipse 的控制台必须用GBK编码。所以条件1和条件4必须同时满足否则运行的还是乱码。才能保证不是乱码。 + +条件1,Window | Preferences | Workspace | Text fileencoding | GBK编码。 + +这样定义的是整个工作区间的编码。 + +这样就把整个工作空间的编码格式定死了,但是如果某一个工程用的是不同的编码格式的话这样单独再解决。如下: + +条件2,工程上右键 | Properties | Resource | Text fileencoding | UTF-8编码。或者适合的编码格式。这样定义的是整个工程的编码。 + +这样就把整个工程的编码格式定死了,但是如果某一个文件用的是不同的编码格式的话这样单独再解决。如下: + +条件3,在某个文件上右键| Properties | Resource | Text fileencoding | UTF-8编码。或者适合的编码格式。这样定义的是单独某个文件的编码。 + +这里要说的是文件的实际编码格式优先用的是:第3个,其次再用2,最后先用1。有时候是123,必须满足条件。无论怎样这几种编码格式试一试就全知道了。 + + + +条件4,还有运行时编码设置如下:菜单:Run Configuration | 右侧的选项卡Common 的 Console Encoding 选择GBK编码。这个是用来控制console控制台显示,必须是GBK,就不会乱码。尽管1,2,3条件都不是GBK,只要4是GBK。控制台就不会乱码。 + +这样保证了工作空间和工程代码编程方式和工程里的单独文件的编码格式的不冲突。 + +## 如何修改eclipse的默认工作空间 + +打开eclipse,选择File菜单,再选择switch workspace,最后选择other,接着你就选择你想要存储的工作区间 + +## Eclipse中Build path specifies execution environment J2SE-1.5.There are no JREs installed.. + +提示警告: + +Description Resource Path Location Type + +Build path specifies execution environment J2SE-1.5. There are no JREs installed in the workspace that are strictly compatible with this environment. platform Build path JRE System Library Problem + +该如何去掉这个警告? + +eclipse 菜单上 window > preference 然后在 Java > Installed JRE 下面的 Execution Environment 中的 J2SE-1.5 中勾中一个 JDK,这表示将这个 JDK 展示成为 J2SE-1.5 的 JDK,以后选择 J2SE-1.5 实际上就选择了这个 JDK,因为 J2SE-1.5 有多种 JDK,我们的eclipse 项目可以仅指定要求 J2SE-1.5 的JDK 而不是 Sun JDK 1.5 或 IBM JDK 1.5 这样的具体类型,这比较方便我们使用不同的厂商的 JDK 而不用复制代码到其它机器时还要安装指定的 JDK 或修改eclipse 设置。它的好处主要体现在项目小组的协作上,很多同事可以使用不同的 JDK,我们的项目设置提交到 CVS/SVN 上之后都不用修改项目设置本身,当大家 JDK 不同时只需要自己修改 eclipse 自己的 JDK 参数,这样你使用 32 位还是 64 位没关系,使用 Sun , IBM 还是 BEA Jrocket 或 Open JDK 都没关系。 + +## Eclipse使用第三方jar包 + +1. 右键项目名称,Build Path > Add External Archives + +2. 右键项目名称,Properties > Java Build Path > Libraries > Add jars + +## Eclipse设置编译文件.class输出路径 + +### 为项目设置.class设置输出路径 + +右键项目 > Properties > Java Build Path > Source > Default Output Folder + +设置完成后,src中的.java文件编译后生成的.class文件与package所对应的目录一起 + +存放在classes目录中。 + +### 设置全局.class文件输出路径 + +Window > Preferences > Java > Build Path > Source and Output Folder + +设置完成后再新建项目的时候会自动的将.class文件放置在你所设置的目录中 + +## Eclipse 中给项目自动创建ant的build.xml文件 + +Eclipse 自动生成 Ant的Build.xml 配置文件,生成的方法很隐蔽 + +选择你要生成Build.xml文件的项目,右键. Export-> General -> Ant Buildfiles . + +点Next,再点Finish.生成完毕.希望使用的可以试试了。总算不用再傻傻的自己编写build.xml了。 + + + + + +## Eclipse中添加Src和JavaDoc + +Eclipse有直接查看java文档和类库源码的功能,不过得手工添加才行,下面对如何在Eclipse中添加java文档和类库源码进行总结。 + + + +1. Window->Pereferences...打开参数选择对话框,展开Java节点,单击“InstalledJREs",此时右边窗口会显示已经加载的jre。 + + + +2. 选中要设置的jre版本,单击"Edit",弹出JRE编辑窗口 + + + +3. 添加javadoc:将JREsystem libraries下的所有包选中,单击右边的“JavadocLocation”按钮,弹出javadoc设置窗口。选择“JavadocURL”单选框,单击“Browse”按钮,选中docs/api目录,然后点击“OK” + + + +4. 添加source:将JREsystem libraries下的所有包选中,单击右边的“SourceAttachment”按钮,弹出sourceattachment configuration窗口。单击“ExternalFile”按钮,选中java安装目录中的src.zip文件,然后点击“OK” + + + +5.后面就一路OK、确定就行了。 + + + +在添加好了javadoc与source后,在eclipse中,使用快捷键"Shift+F2",可快速调出选中类的api文档;使用快捷建F3(或在类上点击右键,现在查看声明),可打开类的源文件。 + +## eclipse中禁用javadoc注释的Format功能 + +在用eclipse进行java开发时,经常需要添加一些必要的javadoc注释。可是每当进行Format操作时(亦即按快捷键:Ctrl+Shift+F时),就会对排版进行自作聪明的调整,但往往这种调整是开发者不愿意看到的。举例如下: + +程序员希望的注释格式: + +```java +/** + +* 根据文件开头的BOM(如果存在的话),判断文件的编码格式。 +* 文本文件有各种不同的编码格式,如果判断有误,则会导致显示或保存错误。 +* 为了标识文件的编码格式,便于编辑和保存,则在文件开头加入了BOM,用以标识编码格式。 +* UTF-8格式:0xef 0xbb 0xbf +* Unicode Little Endian格式:0xff 0xfe +* Unicode Big Endian格式:0xfe 0xff +* 而ANSI格式是没有BOM的。 +* 另有一种不含BOM的UTF-8格式的文件,则不易与ANSI相区分,因此未能识别此类格式。 +* +* @param file 待判断的文件 +*/ +``` + +执行Format操作后,注释格式却变为: + +```java +/** + * 根据文件开头的BOM(如果存在的话),判断文件的编码格式。 文本文件有各种不同的编码格式,如果判断有误,则会导致显示或保存错误。 +* 为了标识文件的编码格式,便于编辑和保存,则在文件开头加入了BOM,用以标识编码格式。 UTF-8格式:0xef 0xbb 0xbf, Unicode + * Little Endian格式:0xff 0xfe, Unicode Big Endian格式:0xfe + * 0xff。而ANSI格式是没有BOM的。另有一种不含BOM的UTF-8格式的文件,则不易与ANSI相区分,因此未能识别此类格式。 +* + * @param file + * 待判断的文件 +*/ +``` + +以上2种排版格式,哪一个更直观清晰,相信不用多说。那么如何禁用eclipse对javadoc注释的Format功能呢?其实很简单,操作如下: + +依次选择菜单:Window->Preferences...->java->Code Style->Formatter。 + +如果"Active profile"为默认的profile,则可以选择:New...打开New Profile对话框,输入Profile name为:My-Profile(自定义的名称) + +如果"Active profile"为自定义的profile可直接选择Edit...->Comments,去掉"Enable Javadoc comment formatting"的选择->OK。 + +注:系统默认的profile是不可以直接编辑的,只能新建一个profile,然后才能Edit...。 + +## eclipse 自动补全的设置,不用按 alt-/ 了 + +偶然间看到了这个,或许有和我一样不喜欢按 alt-/ 兄弟用得上。不用老去按那个 alt-/ 了,还是方便不少。 + +打开 Eclipse -> Window -> Perferences,会打开个Perferences 的设置界面。 + +会看到只有一个"."存在。表示:只有输入"."之后才会有代码提示,我们要修改的地方就是这里,可是Eclipse默认只允许输入4个自定义字符。 + +不过我们可以把当前的设置导出,保存为一个文件,然后在文件中修改,再导入设置,这样就可以突破Eclipse的限制。 先把上图中"."的地方输入几个随便的字符,例如"asdf",点最下面的"OK"来保存设置。 然后打开 Eclipse的 File -> Export,在窗口中展开 General -> Perferences-->Export all然后点击 NEXT。然后点击"Browse"选择任意的一个路径,保存配置文件,然后点击"Finish"。 用记事本打开刚才保存的那个配置文件(扩展文件名:*.epf),按"ctrl + F",输入刚才设置的"asdf",找到刚才字符串。把"asdf"修改为"abcdefghijklmnopqrstuvwxyz.",然后保存,退出记事本。 打 开Eclipse的 File -> Import 然后在打开的窗口里展开 General -> Perferences,点击NEXT,选中刚才修改过的配置文件,Finish。现在,再打开Window -> Perferences,并依次展开 Java -> Editor -> Content Assist,会发现已经超过了4个字符,也就是说我们输入任何字母和"."都会有代码提示了。 + +修改之后,默认是你输入某个字符200毫秒之后出现代码提示,如果出现输入很卡的情况,需要把提示延迟调高一些;如果你嫌它太慢,可以修改成更小的数字,不过数字改的越小,对系统性能的要求就越高,我设置的是50毫秒。现在,Eclipse用起来是不是更加顺手了? + +## Eclipse中的classpath拒绝访问 + +文件是隐藏了,取消隐藏之后就可以了 + +## 通过Eclipse中的Java Build Path 时报错Could not write file: xx:\xx\.classpath + +通过Eclipse的import一个项目到工作台。在通过Java Build Path修改classpath时报错,网上收到解决方法:确保Eclipse说的那个文件不是可读,确保文件不是隐藏的。经过检查我的文件时隐藏,修改属性再试。没问题了。 + +## eclipse中,把java函数代码折叠/展开 + +首先在eclipse 中开启设置代码折叠功能 + +1. windows->perferences->General->Editors->Structured Text Editors + +可以看到Enable folding选项,打上勾就可以使用代码折叠功能,但还要在具体的语言中设置。 + +2、 + +windows->perferences->Java->Editors->Folding + +可以看到Enable folding选项,打上勾就可以使用代码折叠功能。 + +其次 使用快捷键 + +下面你就可以用如下快捷键在你的java class 中 折叠或者展开你的代码了. + +代码折叠的快捷键,默认是: + + Ctrl+Shift+Numpad_Divede(小键盘的/号) + + Ctrl+Shift+Numpad_Multiply(小键盘的*号) + +笔记本没小键盘,于是改成: + + Ctrl+Shift+- + + Ctrl+Shift+= + +## Eclipse空心J 实心J + +Eclipse中Java文件图标由实心J变成空心J的问题。空心J的java文件,不被包含在项目中进行编译,而是当做资源存在项目中。在网上搜到的两种解决办法: + +办法1: + +右击该文件 --> BuildPath --> Include + +正常实心J时,该选项为 Exclude + +方法2: + +BuildPath-->configure buildpath--->source中添加需要被包含的代码 + +没太看懂,最后用类似的方法解决的: + +选中工程--右键Properties--Java Build Path--Source + +找到出现空心J的Java文件所在的包,展开树,正常情况为: + +Included:(All) + +Excluded:(None) + +Native library location:(None) + +我的工程中Exclued项有空心J的Java文件的目录,选中Excluded,点左侧Remove,然后确定. + +## eclipse 保存文件时候自动格式化及import 条目优化 + +在eclipse设置页面,java -> editor-> save actions.进行设置,当你ctrl +s 时候,格式和import 条目优化全搞定 + +## Eclipse一直building workspace问题解决 + +在项目右键点击->Properties->Builder->Maven Project Builder取消勾选就可以了。其他保持不变。 + +## build.properties does not exist + +在导入工程时,老是报:build.properties does not exist错误,不甚其烦,原因是.project文件中设置了: + +org.eclipse.pde.PluginNature + +导致对build.properties的引用,但是build.properties 并不存在。处理办法就是将其注释掉: + + + +## Eclipse插件开发相关问题 + +在Eclipse插件开发过程中,运行或调试时总会在控制台中输出一些对于当前开发无用的日志,比如各种插件的快捷键冲突,某些插件的自身报错,某些插件的license交互等等,这些日志会妨碍我们查看真正想看的日志。解决办法是在run/debug设置中去掉对应的启动插件。 + +![](http://images2015.cnblogs.com/blog/611264/201604/611264-20160424232948226-816722543.jpg) \ No newline at end of file diff --git "a/_drafts/Java/Eclipse\350\277\234\347\250\213\350\260\203\350\257\225\345\207\272\347\216\260\342\200\234JDWP Transport dt_socket failed to initialize\342\200\235\347\232\204\350\247\243\345\206\263\346\226\271\346\241\210.md" "b/_drafts/Java/Eclipse\350\277\234\347\250\213\350\260\203\350\257\225\345\207\272\347\216\260\342\200\234JDWP Transport dt_socket failed to initialize\342\200\235\347\232\204\350\247\243\345\206\263\346\226\271\346\241\210.md" new file mode 100644 index 0000000..1817f73 --- /dev/null +++ "b/_drafts/Java/Eclipse\350\277\234\347\250\213\350\260\203\350\257\225\345\207\272\347\216\260\342\200\234JDWP Transport dt_socket failed to initialize\342\200\235\347\232\204\350\247\243\345\206\263\346\226\271\346\241\210.md" @@ -0,0 +1,33 @@ + +工作中经常需要使用Eclipse远程连接Tomcat,调试Web应用程序,关于如何进行远程调试,本文不再赘述,可以参考下面的文章: + + eclipse远程调试Tomcat方法:http://blog.csdn.net/afgasdg/article/details/9236877 + +但是按照上面的方法进行操作可能会有一些小问题,在远程服务器中更改Tomcat的配置文件catalina.sh之后第一次重启Tomcat时,一般是没有问题的(注意设置的DEBUG端口号不要和其它已有应用端口号冲突),但是在之后的重启过程中可能会出现下面的问题: + + cd tomcat/bin + ./shutdown.sh ; ./startup.sh ; tailf ../logs/catalina.out + + ERROR: transport error 202: bind failed + ERROR: JDWP Transport dt_socket failed to initialize, TRANSPORT_INIT(510) + JDWP exit error AGENT_ERROR_TRANSPORT_INIT(197): No transports initialized [../../../src/share/back/debugInit.c:690] + FATAL ERROR in native method: JDWP No transports initialized, jvmtiError=AGENT_ERROR_TRANSPORT_INIT(197) + +之所以出现这个问题,主要是因为,我们添加的DEBUG端口在关闭Tomcat时不能正常关闭,重启时又会重新开启,所以端口被占用,我们可以在关闭Tomcat之后利用下面的命令进行验证会发现,仍然有进程在占用着DEBUG端口。 + + lsof -i:44121(或者 netstat -na|grep 44121) + +这个其实就是我们自己之前开启的。当然我们可以在每次shutdown之后手动kill掉这个进程,但是终归不是解决之道。我现在想到的比较好的方法是在catalina.sh中配置DEBUG端口时,把需要添加的那一行添加到start条件的开始处: + + ... + elif [ "$1" = "start" ] ; then + CATALINA_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n" + ... + +并且在stop条件的开始处把DEBUG端口干掉 + + ... + elif [ "$1" = "stop" ] ; then + debug_pid=`lsof -i:44121 | tail -n 1 | awk -F" " '{print $2}'` + kill -9 ${debug_pid}· + ... \ No newline at end of file diff --git "a/_drafts/Java/FindBugs\350\257\246\350\247\243.md" "b/_drafts/Java/FindBugs\350\257\246\350\247\243.md" new file mode 100644 index 0000000..a365c2f --- /dev/null +++ "b/_drafts/Java/FindBugs\350\257\246\350\247\243.md" @@ -0,0 +1,73 @@ + +## Find bugs误报告警的消除方法 + +### 背景介绍 + +在java工程中,Find bugs的静态检查能够帮助我们挖掘出代码可能存在的缺陷。在我实际使用的过程中,也确实发现了两处由于“缺少else分支”导致“引入未初始化对象”的错误。与之相对应的是,通过Find bugs也发现四处对象中使用静态成员导致Find bugs告警的情况。通过仔细阅读和分析代码逻辑,可以确认代码本身没有问题,这个是属于Find bugs误报的情况。既然我们打算使用Find bugs来做代码的静态检查,那么就有必要保持一个干净的代码环境,这里面没有任何的Find bugs告警。如果确定是代码问题,毫无疑问需要马上纠正。如果确认是Find bugs误报,也应该进行消除,以便后续的检查能够基于一个干净的环境,同样的误报不需要反复确认。Find bugs告警误报的消除非常容易,只需要在两个级别(类级别和方法进行)加上Find bugs的注解就可以消除。这里建议误报消除尽量在方法级别上进行,以控制误报消除的范围,最大限度放置将真正的代码问题也作为误报给隐藏掉了。 + +### 方法 + +* 在工程添加注解依赖的jar包:使用Find bugs注解需要用到两个jar包,annotations.jar和jsr305.jar。在eclipse中装完Find bugs插件后,在eclipse目录下可以找到这两个jar包文件。 +* 添加注解:在疑问代码所在的类或者方法前面添加注解。其中,value的值就是前面提到的find bugs告警信息中的模式,因为value是一个数组,所以可以同时添加多个模式。justification的值是一句描述信息,你可以理解为是这条注解的注释,内容可以是任意的。 + +```java + @edu.umd.cs.findbugs.annotations + SuppressWarnings(value={"NM_CONFUSING"}, justification="remove findbugs") +``` + + +* 重新运行Find bugs进行检查:添加完注解后,接下来应该重新运行find bugs工具进行检查,以确定误报已经被消除。 + +## MS: Field should be package protected (MS_PKGPROTECT) + +A mutable static field could be changed by malicious code or by accident. The field could be made package protected to avoid this vulnerability. + +我这样定义了多个数组,均使用了 public final static 修饰符: + +```java + public final static double[][][] Y_MIN_SCOPE= + { + {{-120, -25}}, + {{0, 254}}, + {{0, 254}}, + {{0, 254}} + + }; + public final static double[] GRID_HEIGHT = {1,1,1,1}; + + public final static String[][] TAG_NAMES= + { + {"RSRQ(dB)","RSRP(dBm)"}, + {"TA(16*Ts)","UE TxPower(dBm)"}, + {"TA(16*Ts)","RSRP(dBm)"}, + {"TA(16*Ts)","RSRQ(dB)"} + }; +``` + +findbugs给的修改提示是: + +``` + In LTE3DConstant + Field LTE3DConstant.Y_MIN_SCOPE + At LTE3DConstant.java:[line 53] + Y_MIN_SCOPE should be package protected + Bug Type: MS_PKGPROTECT + Bug Category:MALICIOUS_CODE (Malicious code vulnerability) + Source File: + Line:53 +``` + +修改成这样就不报错了。 + +```java + protected final double[][][] Y_MIN_SCOPE= + { + {{-120, -25}}, + {{0, 254}}, + {{0, 254}}, + {{0, 254}} + + }; +``` + +可能原因是因为其它地方没有使用到这个类的变量,所以最好将public改成protected,但是为什么要去掉static还是不理解。 \ No newline at end of file diff --git "a/_drafts/Java/HBase\344\273\213\347\273\215.md" "b/_drafts/Java/HBase\344\273\213\347\273\215.md" new file mode 100644 index 0000000..40e9600 --- /dev/null +++ "b/_drafts/Java/HBase\344\273\213\347\273\215.md" @@ -0,0 +1,276 @@ + +转自:http://jiajun.iteye.com/blog/899632 + +原文图片丢失,本文补充图片,优化排版,修正部分错误。 + +## 一、简介 + +### History + +* started by chad walters and jim +* 2006.11 G release paper on BigTable +* 2007.2 inital HBase prototype created as Hadoop contrib +* 2007.10 First useable Hbase +* 2008.1 Hadoop become Apache top-level project and Hbase becomes subproject +* 2008.10 Hbase 0.18,0.19 released + +Hbase是bigtable的开源山寨版本。是建立的HDFS之上,提供高可靠性、高性能、列存储、可伸缩、实时读写的数据库系统。 + +它介于NoSQL和RDBMS之间,仅能通过主键(row key)和主键的range来检索数据,仅支持单行事务(可通过hive支持来实现多表join等复杂操作)。主要用来存储非结构化和半结构化的松散数据。 + +与hadoop一样,Hbase目标主要依靠横向扩展,通过不断增加廉价的商用服务器,来增加计算和存储能力。 + +HBase中的表一般有这样的特点: + +* 大:一个表可以有上亿行,上百万列 +* 面向列:面向列(族)的存储和权限控制,列(族)独立检索。 +* 稀疏:对于为空(null)的列,并不占用存储空间,因此,表可以设计的非常稀疏。 + +下面一幅图是Hbase在Hadoop Ecosystem中的位置。 + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/611264-20170222003636960-1143541416.png) + +## 二、逻辑视图 +HBase以表的形式存储数据。表有行和列组成。列划分为若干个列族(row family) + +![](https://raw.githubusercontent.com/jiangxincode/PicGo/master/611264-20170222003637820-2049220902.png) + +### Row Key + +与nosql数据库们一样,row key是用来检索记录的主键。访问hbase table中的行,只有三种方式: + +l 通过单个row key访问 + +l 通过row key的range + +l 全表扫描 + +Row key行键 (Row key)可以是任意字符串(最大长度是 64KB,实际应用中长度一般为 10-100bytes),在hbase内部,row key保存为字节数组。 + +存储时,数据按照Row key的字典序(byte order)排序存储。设计key时,要充分利用排序存储这个特性,将经常一起读取的行存储放到一起。(位置相关性) + +注意: + +字典序对int排序的结果是1,10,100,11,12,13,14,15,16,17,18,19,2,20,21,…,9,91,92,93,94,95,96,97,98,99。要保持整形的自然序,行键必须用0作左填充。 + +行的一次读写是原子操作 (不论一次读写多少列)。这个设计决策能够使用户很容易的理解程序在对同一个行进行并发更新操作时的行为。 + +### 列族 + +hbase表中的每个列,都归属与某个列族。列族是表的chema的一部分(而列不是),必须在使用表之前定义。列名都以列族作为前缀。例如courses:history,courses:math + +都属于courses 这个列族。 + +访问控制、磁盘和内存的使用统计都是在列族层面进行的。实际应用中,列族上的控制权限能帮助我们管理不同类型的应用:我们允许一些应用可以添加新的基本数据、一些应用可以读取基本数据并创建继承的列族、一些应用则只允许浏览数据(甚至可能因为隐私的原因不能浏览所有数据)。 + +### 时间戳 + +HBase中通过row和columns确定的为一个存贮单元称为cell。每个 cell都保存着同一份数据的多个版本。版本通过时间戳来索引。时间戳的类型是 64位整型。时间戳可以由hbase(在数据写入时自动 )赋值,此时时间戳是精确到毫秒的当前系统时间。时间戳也可以由客户显式赋值。如果应用程序要避免数据版本冲突,就必须自己生成具有唯一性的时间戳。每个 cell中,不同版本的数据按照时间倒序排序,即最新的数据排在最前面。 + +为了避免数据存在过多版本造成的的管理 (包括存贮和索引)负担,hbase提供了两种数据版本回收方式。一是保存数据的最后n个版本,二是保存最近一段时间内的版本(比如最近七天)。用户可以针对每个列族进行设置。 + +### Cell + +由{row key, column(= +