├── C++ STL ├── C++ STL之map与unordered_map.md ├── C++ STL之map与unordered_map │ └── 1.png ├── C++ STL之vector.md ├── C++ STL之正则表达式.md └── emplace_back和push_back的区别[转].md ├── C++ ├── C++中的struct与class.md ├── C++之POD数据类型.md ├── C++之RAII机制[转].md ├── C++之const关键字.md ├── C++之extern关键字.md ├── C++之friend关键字.md ├── C++之lambda表达式.md ├── C++之lambda表达式 │ └── 1.jpg ├── C++之operator关键字[转].md ├── C++之operator关键字[转] │ ├── 1.png │ └── 2.png ├── C++之static关键字[转].md ├── C++之this指针.md ├── C++之virtual关键字.md ├── C++之virtual关键字 │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ └── 6.png ├── C++之函数返回指针.md ├── C++之失控指针、迷途指针、野指针、悬浮指针及空指针[转].md ├── C++之引用类型.md ├── C++之强制转换运算符[转].md ├── C++之强制转换运算符[转] │ ├── 1.png │ ├── 2.png │ └── 3.png ├── C++类的内存分配.md ├── dll与lib.md ├── gdb常用调试命令.md ├── memcpy与memmove区别和实现.md ├── memcpy与memmove区别和实现 │ └── 1.jpg ├── move和forward源码分析[转].md ├── printf格式化输出符号详细说明[转].md ├── shared_ptr线程安全性分析[转].md ├── shared_ptr线程安全性分析[转] │ ├── 1.png │ └── 2.png ├── text段、data段、bss段、堆和栈.md ├── 为什么多线程读写shared_ptr要加锁?[转].md ├── 为什么多线程读写shared_ptr要加锁?[转] │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── 9.png ├── 使用智能指针的注意事项.md ├── 智能指针之make_unique与make_shared.md ├── 智能指针之make_unique与make_shared │ ├── 1.png │ └── 2.png ├── 类中成员函数的重载、覆盖与隐藏[转].md └── 虚函数的实调用与虚调用.md ├── DSP ├── CCSv4新建C6455工程.md └── CCSv4新建C6455工程 │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── 9.png ├── IDE ├── Atom插件Markdown Preview Enhanced的字体大小修改.md ├── vim代码补全插件YouCompleteMe的自动化安装[转].md ├── vim代码补全插件YouCompleteMe的自动化安装[转] │ └── 1.png ├── vim常用命令.md ├── vim常用命令 │ └── 1.png ├── 关于vim在插入模式中Backspace键无法删除的问题[转].md ├── 关于vim在插入模式中Backspace键无法删除的问题[转] │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.jpg │ └── 5.png ├── 超强vim配置文件[转].md └── 超强vim配置文件[转] │ └── 1.png ├── Internet ├── DNS域名解析过程.md ├── DNS域名解析过程 │ ├── 1.jpg │ ├── 2.jpg │ └── 3.jpg ├── HTTP协议[转].md ├── HTTP协议[转] │ ├── 1.jpg │ ├── 2.png │ └── 3.jpg ├── IP、TCP、UDP首部详解[转].md ├── IP、TCP、UDP首部详解[转] │ ├── 1.jpg │ ├── 2.png │ └── 3.jpg ├── OSI七层网络模型.md ├── OSI七层网络模型 │ ├── 1.gif │ ├── 2.png │ └── 3.jpg ├── TCP协议.md └── TCP协议 │ ├── 1.png │ ├── 2.png │ └── 3.jpg ├── Java ├── Java泛型详解[转].md ├── Java的内省机制.md └── Java的反射机制.md ├── Linux └── Linux常用命令.md ├── Matlab ├── Matlab生成exe文件.md ├── Matlab生成exe文件 │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 13.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── 9.png ├── VC调用Matlab生成的c.md ├── VC调用Matlab生成的c │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ └── 5.png ├── VC调用Matlab生成的dll.md ├── VC调用Matlab生成的dll │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ └── 5.png ├── VC调用Matlab生成的exe.md └── VC调用Matlab生成的exe │ └── 1.png ├── README.md ├── UML ├── 合并StarUML的多个Project文件.md └── 合并StarUML的多个Project文件 │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ └── 7.png ├── Windows ├── 不同进程通过共享内存实现数据共享.md ├── 不同进程通过共享内存实现数据共享 │ └── 1.png ├── 多线程.md ├── 消息机制.md ├── 相同程序不同进程下实现数据共享.md └── 相同程序不同进程下实现数据共享 │ ├── 1.png │ ├── 2.png │ ├── 3.png │ └── 4.png ├── WordPress ├── CentOS6系统LNMP环境搭建及WordPress安装.md └── CentOS6系统LNMP环境搭建及WordPress安装 │ ├── 1.PNG │ ├── 10.PNG │ ├── 11.PNG │ ├── 12.png │ ├── 13.PNG │ ├── 14.PNG │ ├── 15.PNG │ ├── 16.PNG │ ├── 17.PNG │ ├── 2.PNG │ ├── 3.PNG │ ├── 4.PNG │ ├── 5.PNG │ ├── 6.png │ ├── 7.PNG │ ├── 8.PNG │ └── 9.PNG ├── git ├── .gitignore设置跟踪忽略文件夹中文件.md ├── git与svn的五个基本区别[转].md ├── git常用命令.md ├── git常用命令 │ ├── 1.png │ └── 2.png ├── 从github下载WDK项目的坑.md └── 从github下载WDK项目的坑 │ ├── 1.png │ ├── 2.png │ └── 3.png ├── 其他 ├── 使用youtube-dl下载YouTube视频.md ├── 使用youtube-dl下载YouTube视频 │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── 9.png └── 研究生阶段的自我总结.md ├── 微信公众平台开发 ├── 使用python-aiohttp搭建微信公众平台.md ├── 使用python-aiohttp爬取今日头条.md ├── 使用python-aiohttp爬取今日头条 │ ├── 1.png │ └── 2.png ├── 使用python-aiohttp爬取网易云音乐.md └── 通过Apache反向代理实现微信服务器80端口访问.md ├── 数据库 └── MySQL高并发.md ├── 数据结构与算法 ├── 字典序算法[转].md ├── 字典序算法[转] │ └── 1.png ├── 散列(hash)表算法[转].md ├── 散列(hash)表算法[转] │ ├── 1.jpg │ └── 2.jpg ├── 最长回文子串:Manacher算法[转].md ├── 最长回文子串:Manacher算法[转] │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ └── 6.png ├── 海量数据处理算法:Bloom Filter[转].md ├── 海量数据处理算法:Bloom Filter[转] │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.png │ ├── 5.png │ ├── 6.png │ └── 7.png └── 红黑树.md └── 读书笔记 ├── 《C#程序设计经典300例》读书笔记.md ├── 《C++面向对象程序设计-基于Visual C++ 2010》读书笔记.md ├── 《C++面向对象程序设计-基于Visual C++ 2010》读书笔记 ├── 1.png ├── 2.png ├── 3.png └── 4.png ├── 《CUDA并行程序设计-GPU编程指南》读书笔记 ├── 1. 线程网格、线程块以及线程.md ├── 1. 线程网格、线程块以及线程 │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ └── 7.png ├── 2. CUDA内存处理.md └── 2. CUDA内存处理 │ ├── 1.png │ ├── 2.png │ ├── 3.png │ └── 4.png ├── 《C陷阱与缺陷》读书笔记.md ├── 《Maven实战》读书笔记 ├── 1. 使用intellij idea搭建并配置maven多模块项目.md └── 1. 使用intellij idea搭建并配置maven多模块项目 │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 13.png │ ├── 14.png │ ├── 15.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ └── 9.png └── 《设计模式之禅》读书笔记 ├── 1. 设计原则.md ├── 10. 命令模式.md ├── 10. 命令模式 └── 1.png ├── 11. 责任链模式.md ├── 11. 责任链模式 └── 1.png ├── 12. 装饰模式.md ├── 12. 装饰模式 └── 1.png ├── 13. 策略模式.md ├── 13. 策略模式 └── 1.png ├── 14. 适配器模式.md ├── 14. 适配器模式 └── 1.png ├── 15. 迭代器模式.md ├── 15. 迭代器模式 └── 1.png ├── 16. 组合模式.md ├── 16. 组合模式 └── 1.png ├── 17. 观察者模式.md ├── 17. 观察者模式 └── 1.png ├── 18. 门面模式.md ├── 18. 门面模式 └── 1.png ├── 19. 备忘录模式.md ├── 19. 备忘录模式 └── 1.png ├── 2. 单例模式.md ├── 2. 单例模式 └── 1.png ├── 20. 访问者模式.md ├── 20. 访问者模式 └── 1.png ├── 21. 状态模式.md ├── 21. 状态模式 └── 1.png ├── 22. 解释器模式.md ├── 22. 解释器模式 └── 1.png ├── 23. 享元模式.md ├── 23. 享元模式 └── 1.png ├── 24. 桥梁模式.md ├── 24. 桥梁模式 └── 1.png ├── 25. 创建类模式比较.md ├── 26. 结构类模式比较.md ├── 27. 行为类模式比较.md ├── 27. 行为类模式比较 ├── 1.png └── 2.png ├── 3. 工厂方法模式.md ├── 3. 工厂方法模式 └── 1.png ├── 4. 抽象工厂模式.md ├── 4. 抽象工厂模式 └── 1.png ├── 5. 模板方法模式.md ├── 5. 模板方法模式 └── 1.png ├── 6. 建造者模式.md ├── 6. 建造者模式 └── 1.png ├── 7. 代理模式.md ├── 7. 代理模式 ├── 1.png └── 2.png ├── 8. 原型模式.md ├── 8. 原型模式 └── 1.png ├── 9. 中介者模式.md └── 9. 中介者模式 └── 1.png /C++ STL/C++ STL之map与unordered_map.md: -------------------------------------------------------------------------------- 1 | ### map 2 | 3 | `map`是STL的一个关联容器,它提供一对一(第一个为`key`,每个`key`只能在`map`中出现一次,第二个为`value`)的数据处理能力。`map`内部自建一颗红黑树(一种非严格意义上的平衡二叉树),所以在`map`内部所有的数据都是有序的,且`map`的查询、插入、删除操作的时间复杂度都是`O(logN)`。在使用时,`map`的`key`需要定义`operator<`。 4 | 5 | ### unordered_map 6 | 7 | `unordered_map`和`map`类似,都是存储的`key-value`的值,可以通过`key`快速索引到`value`。不同的是`unordered_map`不会根据`key`的大小进行排序,存储时是根据`key`的`hash`值判断元素是否相同,即`unordered_map`内部元素是无序的。`unordered_map`的`key`需要定义`hash_value`函数并且重载`operator==`。 8 | 9 | `unordered_map`的底层是一个防冗余的哈希表(采用除留余数法)。哈希表最大的优点,就是把数据的存储和查找消耗的时间大大降低,时间复杂度为`O(1)`;而代价仅仅是消耗比较多的内存。 10 | 11 | #### 基本原理 12 | 13 | 使用一个下标范围比较大的数组来存储元素。可以设计一个函数(哈希函数,也叫做散列函数),使得每个元素的`key`都与一个函数值(即数组下标,`hash`值)相对应,于是用这个数组单元来存储这个元素;也可以简单的理解为,按照`key`为每一个元素“分类”,然后将这个元素存储在相应“类”所对应的地方,称为桶。 14 | 15 | 但是,不能够保证每个元素的`key`与函数值是一一对应的,因此极有可能出现对于不同的元素,却计算出了相同的函数值,这样就产生了“冲突”,换句话说,就是把不同的元素分在了相同的“类”之中。 总的来说,“直接定址”与“解决冲突”是哈希表的两大特点。 16 | 17 | 一般可采用拉链法解决冲突: 18 | 19 | ![](C++%20STL之map与unordered_map/1.png) 20 | 21 | **参考链接** 22 | 23 | [map/unordered_map原理和使用整理](http://blog.csdn.net/blues1021/article/details/45054159)
24 | [解析unordeded_map和unordeded_set的底层实现](http://blog.csdn.net/turn__back/article/details/56005723)
25 | [Linux下map hash_map和unordered_map效率比较](http://blog.csdn.net/whizchen/article/details/9286557)
26 | [C++中map、hash_map、unordered_map、unordered_set通俗辨析](http://blog.csdn.net/u013195320/article/details/23046305)
27 | [C++11 新特性: unordered_map 与 map 的对比](http://www.cnblogs.com/NeilZhang/p/5724996.html) 28 | -------------------------------------------------------------------------------- /C++ STL/C++ STL之map与unordered_map/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/C++ STL/C++ STL之map与unordered_map/1.png -------------------------------------------------------------------------------- /C++ STL/C++ STL之vector.md: -------------------------------------------------------------------------------- 1 | ### 介绍 2 | 3 | `vector`是表示可变大小数组的序列容器。 4 | 5 | 就像数组一样,`vector`也采用的连续存储空间来存储元素。也就意味着使用迭代器、下标或指针偏移的方式对`vector`的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。 6 | 7 | 本质讲,`vector`使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,`vector`并不会每次都重新分配大小。 8 | 9 | `vector`分配空间策略:`vector`会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。 10 | 11 | 所以,`vector`的大小与`vector`的容量是有区别的,大小是指元素的个数,容量是指分配的内存大小。`vector::size()`返回`vector`的大小,`vector::capacity()`返回容量值。若要自己指定分配的容量大小,则可以使用`vector::reserve()`,但规定的值要大于`size()`值。 12 | 13 | 因此,`vector`占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。 14 | 15 | 与其它动态序列容器相比(deques, lists and forward_lists), `vector`在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起lists和forward_lists统一的迭代器和引用更好。 16 | 17 | ### vector的用法 18 | 19 | #### 头文件 20 | ```cpp 21 | #include 22 | ``` 23 | `vector`属于`std`命名域的,建议使用全局的命名域方式: 24 | ```cpp 25 | using namespace std; 26 | ``` 27 | 28 | #### 构造函数与析构函数 29 | 30 | | 操作 | 效果 | 31 | | -- | -- | 32 | | `vector c` | Default构造函数,产生一个空`vector`,没有任何元素 | 33 | | `vector c(c2)` | Copy构造函数,建立`c2`的同型`vector`并成为`c2`的一份拷贝(所有元素都被复制) | 34 | | `vector c=c2` | Copy构造函数,建立一个新的`vector`作为`c2`的拷贝(每个元素都被复制) | 35 | | `vector c(rv)` | Move构造函数,建立一个新的`vector`,取`rvalue rv`的内容(始自C++11) | 36 | | `vector c=rv` | Move构造函数,建立一个新的`vector`,取`rvalue rv`的内容(始自C++11) | 37 | | `vector c(n)` | 利用元素的Default构造函数生成一个大小为`n`的`vector` | 38 | | `vector c(n,val)` | 建立一个大小为`n`的`vector`,每个元素值都是`val` | 39 | | `vector c(beg,end)` | 建立一个`vector`,以区间`[begin,end)`作为元素初值 | 40 | | `vector c(initlist)` | 建立一个`vector`,以初值列`initlist`的元素为初值(始自C++11) | 41 | | `vector c=initlist` | 建立一个`vector`,以初值列`initlist`的元素为初值(始自C++11) | 42 | | `c.~vector()` | 销毁所有元素,释放内存 | 43 | 44 | 45 | ```cpp 46 | // v1是一个空vector,它潜在的元素是int类型的,执行默认初始化 47 | vector v1; 48 | // v2中包含有v1所有元素的副本 49 | vector v2(v1); 50 | // 等价于v3(v2),v3中包含有v2所有元素的副本 51 | vector v3 = v2; 52 | // v4中包含了2个重复元素,每个元素的值都为4 53 | vector v4(2,4); 54 | // v5中包含了2个重复地执行了值初始化的对象 55 | vector v5(2); 56 | // v6包含了初始值个数的元素,每个元素被赋予相应的初始值 57 | vector v6{5,6}; 58 | // 等价于v7{6,7} 59 | vector v7 = {6,7}; 60 | // 用向量v7的第0个到第1个值初始化v8 61 | vector v8(v7.begin(),v7.begin()+2); 62 | // 用数组a的第0个到第1个值初始化v9 63 | array a = {1,2}; 64 | vector v9(a.begin(),a.begin()+2); 65 | // 用数组b的第0个到第1个值初始化v10与v11 66 | int b[] = {3,4}; 67 | vector v10(b,b+2); 68 | vector v11(&b[0],&b[2]); 69 | ``` 70 | #### 非更易型操作 71 | 72 | #### 算法 73 | 74 | **遍历** 75 | 76 | **查找** 77 | 78 | **排序** 79 | 80 | **参考链接** 81 | 82 | [Vector的用法](http://blog.csdn.net/lsh_2013/article/details/21191289)
83 | [C++ STL之vector用法总结](http://www.cnblogs.com/zhonghuasong/p/5975979.html)
84 | [C++STL中vector容器的用法](http://www.cnblogs.com/ziyi--caolu/archive/2013/07/04/3170928.html) 85 | -------------------------------------------------------------------------------- /C++ STL/emplace_back和push_back的区别[转].md: -------------------------------------------------------------------------------- 1 | 原文:[emplace_back() 和 push_back 的区别](http://blog.csdn.net/xiaolewennofollow/article/details/52559364) 2 | 3 | 在引入右值引用,转移构造函数,转移复制运算符之前,通常使用`push_back()`向容器中加入一个右值元素(临时对象)的时候,首先会调用构造函数构造这个临时对象,然后需要调用拷贝构造函数将这个临时对象放入容器中。原来的临时变量释放。这样造成的问题是临时变量申请的资源就浪费。 4 | 5 | 引入了右值引用,转移构造函数后,`push_back()`右值时就会调用构造函数和转移构造函数。 6 | 7 | 在这上面有进一步优化的空间就是使用`emplace_back()`,使用`emplace_back()`在容器尾部添加一个元素,这个元素原地构造,不需要触发拷贝构造和转移构造。而且调用形式更加简洁,直接根据参数初始化临时对象的成员。 8 | 9 | 测试代码: 10 | ```cpp 11 | #include 12 | #include 13 | #include 14 | 15 | struct President { 16 | std::string name; 17 | std::string country; 18 | int year; 19 | 20 | President(std::string p_name, std::string p_country, int p_year) 21 | : name(std::move(p_name)), country(std::move(p_country)), year(p_year) { 22 | std::cout << "I am being constructed.\n"; 23 | } 24 | President(const President& other) 25 | : name(std::move(other.name)), country(std::move(other.country)), year(other.year){ 26 | std::cout << "I am being copy constructed.\n"; 27 | } 28 | President(President&& other) 29 | : name(std::move(other.name)), country(std::move(other.country)), year(other.year) { 30 | std::cout << "I am being moved.\n"; 31 | } 32 | President& operator=(const President& other); 33 | }; 34 | 35 | int main() { 36 | std::vector elections; 37 | std::cout << "emplace_back:\n"; 38 | elections.emplace_back("Nelson Mandela", "South Africa", 1994); //没有类的创建 39 | 40 | std::vector reElections; 41 | std::cout << "\npush_back:\n"; 42 | reElections.push_back(President("Franklin Delano Roosevelt", "the USA", 1936)); 43 | 44 | std::cout << "\nContents:\n"; 45 | for (President const& president: elections) { 46 | std::cout << president.name << " was elected president of " 47 | << president.country << " in " << president.year << ".\n"; 48 | } 49 | for (President const& president: reElections) { 50 | std::cout << president.name << " was re-elected president of " 51 | << president.country << " in " << president.year << ".\n"; 52 | } 53 | 54 | } 55 | ``` 56 | 测试结果: 57 | ``` 58 | emplace_back: 59 | I am being constructed. 60 | 61 | push_back: 62 | I am being constructed. 63 | I am being moved. 64 | 65 | Contents: 66 | Nelson Mandela was elected president of South Africa in 1994. 67 | Franklin Delano Roosevelt was re-elected president of the USA in 1936. 68 | ``` 69 | -------------------------------------------------------------------------------- /C++/C++中的struct与class.md: -------------------------------------------------------------------------------- 1 | **struct与class使用{}初始化** 2 | 3 | * struct与class若是定义了构造函数,则都不能用大括号进行初始化 4 | * struct若没有定义构造函数,可以用大括号初始化 5 | * class若没有定义构造函数,且所有成员变量都为public,可以用大括号初始化 6 | 7 | **struct在C与C++中的区别** 8 | 9 | * 在C中,struct是用户自定义数据类型(UDT);在C++中,struct是抽象数据类型(ADT),支持成员函数的定义 10 | * C中的struct是没有权限设置的,可以封装数据却不可以隐藏数据,而且成员不可以是函数;C++中struct增加了访问权限,且可以和类一样有成员函数 11 | * 在C中struct不可以继承;C++中struct可以进行复杂的继承甚至多重继承,一个struct可以继承自一个class,反之亦可 12 | * 在定义结构体与定义结构体变量时的区别: 13 | ```cpp 14 | // 定义C结构体 15 | struct A{ 16 | int a; 17 | }; 18 | // 定义C结构体变量 19 | struct A aa; 20 | 21 | // 或者 22 | // 定义C结构体 23 | typedef struct{ 24 | int a; 25 | }A; 26 | // 定义C结构体变量 27 | A aa; 28 | 29 | // 定义C++结构体 30 | struct A{ 31 | int a; 32 | }; 33 | // 定义C++结构体变量 34 | A aa; 35 | ``` 36 | 37 | **C++中struct与class的区别** 38 | 39 | * class中成员的默认访问权限与默认继承方式都是private的,而struct中则是public的 40 | * class作为关键字还用于定义模板参数,就像typename,但关键字struct不用于定义模板参数 41 | 42 | 除了这两点,struct和class基本就是一个东西,使用上没有任何其它区别。 43 | 44 | **参考链接** 45 | 46 | [C++的类和C里面的struct有什么区别](http://blog.csdn.net/wfq_1985/article/details/5390398)
47 | [struct 区别 在C 和C++ 中](http://www.cnblogs.com/chip/p/4955137.html) 48 | -------------------------------------------------------------------------------- /C++/C++之extern关键字.md: -------------------------------------------------------------------------------- 1 | ### 修饰变量 2 | 3 | 使用extern关键字声明变量可以告诉编译器,该变量如果在当前文件的当前编译语句的之前未被定义,那么就会在当前文件的后面或者其它文件中定义。 4 | 5 | 这是由于在编译阶段,我们所定义的全局变量的可见性仅局限于所在文件的定义位置之后,而其可见性扩展到整个程序则是在链接完成之后。 6 | 7 | 在下面的代码中,使用static关键字与const关键字修饰的全局变量a与c被分配到不同空间,其作用域都只局限于各自所在文件,但是使用static关键字修饰的全局变量a不能再使用extern关键字进行变量声明,而使用const关键字修饰的全局变量d则可以通过使用extern关键字进行变量声明。 8 | 9 | test.cpp文件: 10 | ```cpp 11 | #include 12 | using namespace std; 13 | 14 | void print_test1(); 15 | 16 | //extern int a; // 错误 17 | static int a = 0xa0; 18 | extern int b; 19 | const int c = 0xc0; 20 | extern const int d; 21 | 22 | void print_test(){ 23 | cout< 84 | using namespace std; 85 | 86 | extern "C"{ 87 | extern int a; 88 | void fun_testc(); 89 | } 90 | 91 | void fun_test(){ 92 | cout << "come from C++" << endl; 93 | cout << hex << "a=" << a << dec << endl; 94 | } 95 | 96 | int main() { 97 | fun_test(); 98 | fun_testc(); 99 | return 0; 100 | } 101 | ``` 102 | testc.c文件: 103 | ```c 104 | #include 105 | 106 | int a = 0xa; 107 | 108 | void fun_testc(){ 109 | printf("come from C\n"); 110 | printf("a=%x\n", a); 111 | } 112 | ``` 113 | 运行结果: 114 | ``` 115 | come from C++ 116 | a=a 117 | come from C 118 | a=a 119 | ``` 120 | 为了方便C\+\+或C调用,我们可以将testc.c的变量与函数声明到头文件中,并通过define一个跟文件同名大写的宏来防止重定义,其中,__cplusplus宏在C++中定义,C中没有该定义。 121 | 122 | testc.h文件: 123 | ```cpp 124 | #ifndef TESTC_H 125 | #define TESTC_H 126 | 127 | #ifdef __cplusplus 128 | extern "C"{ 129 | #endif /* __cplusplus */ 130 | 131 | extern int a; 132 | extern void fun_testc(); 133 | 134 | #ifdef __cplusplus 135 | } 136 | #endif /* __cplusplus */ 137 | 138 | #endif //TESTC_H 139 | ``` 140 | 141 | **C调用C++函数** 142 | 143 | testc.c文件: 144 | ```c 145 | #include 146 | #include "test.h" 147 | 148 | void fun_testc(){ 149 | printf("come from C\n"); 150 | printf("a=%x\n", a); 151 | } 152 | 153 | int main() { 154 | fun_test(); 155 | fun_testc(); 156 | return 0; 157 | } 158 | ``` 159 | test.h文件: 160 | ```cpp 161 | #ifndef TEST_H 162 | #define TEST_H 163 | 164 | #ifdef __cplusplus 165 | extern "C"{ 166 | #endif /* __cplusplus */ 167 | 168 | extern int a; 169 | void fun_test(); 170 | 171 | #ifdef __cplusplus 172 | } 173 | #endif /* __cplusplus */ 174 | 175 | #endif //TEST_H 176 | ``` 177 | test.cpp文件: 178 | ```cpp 179 | #include 180 | #include "test.h" 181 | using namespace std; 182 | 183 | int a = 0xa; 184 | void fun_test(){ 185 | cout << "come from C++" << endl; 186 | cout << hex << "a=" << a << dec << endl; 187 | } 188 | ``` 189 | 运行结果: 190 | ``` 191 | come from C++ 192 | a=a 193 | come from C 194 | a=a 195 | ``` 196 | 197 | **参考文献** 198 | 199 | [解析“extern”](http://blog.csdn.net/keensword/article/details/401114)
200 | [C++中extern关键字使用](http://blog.csdn.net/sruru/article/details/7951019)
201 | [关于C++的extern关键字](http://www.cnblogs.com/ForFreeDom/archive/2012/03/21/2409950.html)
202 | [C和C++混合编程(__cplusplus 与 extern "c" 的使用)](http://www.cnblogs.com/x_wukong/p/5630143.html) 203 | -------------------------------------------------------------------------------- /C++/C++之lambda表达式.md: -------------------------------------------------------------------------------- 1 | lambda表达式的语法归纳如下: 2 | 3 | ![](C++之lambda表达式/1.jpg) 4 | 5 | 1. capture子句(在 C++ 规范中也称为 lambda 引导) 6 | 2. 参数列表(可选) 7 | 3. 可变规范(可选) 8 | 4. 异常规范(可选) 9 | 4. 尾随返回类型(可选) 10 | 5. lambda函数体 11 | 12 | #### capture子句 13 | 14 | capture子句指定要捕获的变量以及是通过值还是引用进行捕获。有与号`&`前缀的变量通过引用访问,没有该前缀的变量通过值访问。空capture子句`[]`指示 lambda 表达式的主体不访问封闭范围中的变量。 15 | 16 | 可以使用默认捕获模式来指示如何捕获lambda中引用的任何外部变量:`[&]`表示通过引用捕获引用的所有变量,而`[=]`表示通过值捕获它们。另外,可以使用默认捕获模式,然后为特定变量显式指定相反的模式。 17 | ```cpp 18 | [a,&b] // a变量以值的方式呗捕获,b以引用的方式被捕获 19 | [this] // 以值的方式捕获this指针 20 | [&] // 以引用的方式捕获所有的外部自动变量 21 | [=] // 以值的方式捕获所有的外部自动变量 22 | [] // 不捕获外部的任何变量 23 | [=,&a] // 按值捕获外部作用域中所有变量,并按引用捕获a变量。 24 | ``` 25 | 26 | #### 参数列表 27 | 28 | 除了捕获变量,lambda还可接受输入参数。参数列表是可选的,它在大多数方面类似于函数的参数列表。 29 | ```cpp 30 | int y = [] (int first, int second) { 31 | return first + second; 32 | }; 33 | ``` 34 | 35 | #### 可变规范 36 | 37 | 通常,lambda的函数调用运算符为`const-by-value`,但对`mutable`关键字的使用可将其取消。它不会生成可变的数据成员。利用可变规范,lambda表达式的主体可以修改通过值捕获的变量。 38 | ```cpp 39 | #include 40 | using namespace std; 41 | 42 | int main() { 43 | int m = 0; 44 | int n = 0; 45 | [&, n] (int a) mutable { m = ++n + a; }(4); 46 | cout << m << " " << n << endl; 47 | } 48 | ``` 49 | 运行结果: 50 | ``` 51 | 5 0 52 | ``` 53 | 由于变量`n`是通过值捕获的,因此在调用lambda表达式后,变量的值仍保持`0`不变。`mutable`规范允许在lambda中修改`n`。 54 | 55 | #### 异常规范 56 | 57 | 可以使用`throw()`异常规范来指示lambda表达式不会引发任何异常。 58 | ```cpp 59 | #include 60 | using namespace std; 61 | 62 | int main() { 63 | []() { 64 | try { 65 | throw 5; 66 | } catch (int ex){ 67 | cout<& v) { 94 | static int nextValue = 1; 95 | generate(v.begin(), v.end(), [] { return nextValue++; }); 96 | } 97 | ``` 98 | 99 | **参考链接** 100 | 101 | [C++ 中的 Lambda 表达式](https://msdn.microsoft.com/zh-cn/library/dd293608.aspx)
102 | [C++11 学习笔记 lambda表达式](http://blog.csdn.net/fjzpdkf/article/details/50249287)
103 | [C++11 lambda 表达式解析](http://www.cnblogs.com/haippy/archive/2013/05/31/3111560.html) 104 | -------------------------------------------------------------------------------- /C++/C++之lambda表达式/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/C++/C++之lambda表达式/1.jpg -------------------------------------------------------------------------------- /C++/C++之operator关键字[转].md: -------------------------------------------------------------------------------- 1 | 原文:[C++ operator关键字(重载操作符)](http://blog.csdn.net/my_heart_/article/details/51534624) 2 | 3 | `operator`是C\+\+的关键字,它和运算符一起使用,表示一个运算符函数,理解时应将`operator=`整体上视为一个函数名。 4 | 5 | ### 为什么使用操作符重载? 6 | 7 | 对于系统的所有操作符,一般情况下,只支持基本数据类型和标准库中提供的`class`,对于用户自己定义`class`,如果想支持基本操作,比如比较大小,判断是否相等,等等,则需要用户自己来定义关于这个操作符的具体实现。 8 | 9 | 比如,判断两个人是否一样大,我们默认的规则是按照其年龄来比较,所以,在设计`person`这个`class`的时候,我们需要考虑操作符`==`,而且,根据刚才的分析,比较的依据应该是`age`。 10 | 11 | 那么为什么叫重载呢?这是因为,在编译器实现的时候,已经为我们提供了这个操作符的基本数据类型实现版本,但是现在他的操作数变成了用户定义的数据类型`class`,所以,需要用户自己来提供该参数版本的实现。 12 | 13 | ### 如何声明一个重载的操作符? 14 | 15 | #### 操作符重载实现为类成员函数 16 | 17 | 重载的操作符在类体中被声明,声明方式如同普通成员函数一样,只不过他的名字包含关键字`operator`,以及紧跟其后的一个C\+\+预定义的操作符。 18 | 19 | 可以用如下的方式来声明一个预定义的`==`操作符: 20 | ```cpp 21 | #include 22 | using namespace std; 23 | 24 | class Person{ 25 | private: 26 | int age; 27 | public: 28 | Person(int age):age(age){} 29 | inline bool operator==(const Person &p) const{ 30 | return this->age == p.age; 31 | } 32 | }; 33 | 34 | int main() { 35 | Person a(10),b(12); 36 | cout<<(a==b)< 47 | using namespace std; 48 | 49 | class Person{ 50 | public: 51 | int age; 52 | Person(int age):age(age){} 53 | }; 54 | 55 | inline bool operator==(const Person &a,const Person &b){ 56 | return a.age == b.age; 57 | } 58 | 59 | int main() { 60 | Person a(10),b(12); 61 | cout<<(a==b)< 68 | using namespace std; 69 | 70 | class Person{ 71 | private: 72 | int age; 73 | public: 74 | Person(int age):age(age){} 75 | friend inline bool operator==(const Person &a,const Person &b); 76 | }; 77 | 78 | inline bool operator==(const Person &a,const Person &b){ 79 | return a.age == b.age; 80 | } 81 | 82 | int main() { 83 | Person a(10),b(12); 84 | cout<<(a==b)<`操作符必须被定义为类成员操作符 93 | * 如果有一个操作数是类类型如string类的情形那么对于对称操作符比如等于操作符最好定义为全局函数 94 | 95 | #### 操作符重载适用范围 96 | 97 | ![](C++之operator关键字[转]/1.png) 98 | 99 | ![](C++之operator关键字[转]/2.png) 100 | 101 | #### 重载运算符的限制 102 | 103 | 1. 只有C\+\+预定义的操作符才可以被重载 104 | 2. 对于内置类型的操作符,它的预定义不能改变,即不能改变操作符原来的功能 105 | 3. 重载操作符不能改变他们的操作符优先级 106 | 4. 重载操作符不能改变操作数的个数 107 | 5. 除了对`()`操作符外,对其他重载操作符提供缺省实参都是非法的 108 | -------------------------------------------------------------------------------- /C++/C++之operator关键字[转]/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/C++/C++之operator关键字[转]/1.png -------------------------------------------------------------------------------- /C++/C++之operator关键字[转]/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/C++/C++之operator关键字[转]/2.png -------------------------------------------------------------------------------- /C++/C++之this指针.md: -------------------------------------------------------------------------------- 1 | this指针是类的普通成员函数的隐含形参,只能在成员函数中使用,指向调用该成员函数的实例地址,当形参与成员变量重名时,我们可以通过this指针加以区分。 2 | ```cpp 3 | #include 4 | using namespace std; 5 | 6 | class A { 7 | public: 8 | int a; 9 | A* set(int a){ 10 | this->a = a; 11 | return this; 12 | } 13 | }; 14 | 15 | int main() { 16 | A a={0}; 17 | cout<<&a< 34 | using namespace std; 35 | 36 | class A { 37 | public: 38 | int a; 39 | static int b; 40 | A* set(){ 41 | // this->a = 1; // 错误 42 | this->b = 1; 43 | return this; 44 | } 45 | }; 46 | int A::b = 0; 47 | 48 | int main() { 49 | A *pa=NULL; 50 | cout<b<set()<b<::<静态成员变量>`的方式访问静态成员变量。 65 | ```cpp 66 | #include 67 | using namespace std; 68 | 69 | class A { 70 | public: 71 | static int a; 72 | static void set(int a){ 73 | A::a = a; 74 | } 75 | }; 76 | int A::a = 0; 77 | 78 | int main() { 79 | cout< 5 | #include 6 | using namespace std; 7 | 8 | char* fun(int mod){ 9 | switch(mod){ 10 | case 0:{ 11 | char a[] = "0123456"; 12 | char *pa = a; 13 | pa[0]++; 14 | cout<<&pa<<"->"<<(void *)pa<<":"<"<<(void *)a<<":"<"<<(void *)a<<":"<"<<(void *)a<<":"<"<<(void *)a<<":"<"<<(void *)a<<":"<"<<(void *)a<<":"<"<<(void *)a<<":"<"<<(void *)a<<":"<"<<(void *)a<<":"<"<<(void *)a<<":"<"<<(void *)a<<":"<0x28fee8:1123456 76 | 456 1123456 77 | 0x28ff14->0x28fee8:╔ 78 | 79 | 0x405004->0x405004:2234567 80 | 0x28ff14->0x405004:2234567 81 | 82 | 0x28fee0->0x40606a:2345678 83 | 0x28ff14->0x40606a:2345678 84 | 85 | 0x28fedc->0x5315d0:4456789 86 | 0x28ff14->0x5315d0:4456789 87 | 88 | 0x40500c->0x40608e:4567890 89 | 0x28ff14->0x40608e:4567890 90 | 91 | 0x408030->0x5315d0:6678901 92 | 0x28ff14->0x5315d0:6678901 93 | ``` 94 | 在case 0中所声明的字符串数组保存在栈中,在函数返回时空间被释放,在测试结果中我们可以看到,函数返回后该地址处的数据还在,但随即便被其他值覆盖。 95 | 96 | 在case 1中所声明的字符串数组由于添加了static关键字,字符串被保存在全局数据区,该区域在程序结束时由系统释放,所以我们可以通过指针访问到字符串。 97 | 98 | 在case 2中的字符串存放在常量区,不可修改,同样是在程序结束时由系统释放。 99 | 100 | 在case 3中通过new申请的内存空间位于堆中,并通过delete进行释放,由于在函数中我们并没有释放该处内存,所以当函数返回后可以通过指针进行访问。 101 | 102 | 在case 4、5中字符串分别保存在常量区与堆中,而指向字符串的指针位于全局数据区,所以在gdb中我们可以通过存放在全局数据区的字符串指针来访问字符串 103 | ```cpp 104 | Breakpoint 2, main () at E:\Code\Cpp\test\test.cpp:62 105 | 62 delete a; 106 | (gdb) p (char *)0x405004 107 | $1 = 0x405004 "2234567" 108 | (gdb) p (char *)*(long *)0x40500c 109 | $2 = 0x40608e "4567890" 110 | 111 | Breakpoint 3, main () at E:\Code\Cpp\test\test.cpp:67 112 | 67 delete a; 113 | (gdb) p (char *)*(long *)0x408030 114 | $3 = 0x6216a0 "6678901" 115 | ``` 116 | -------------------------------------------------------------------------------- /C++/C++之失控指针、迷途指针、野指针、悬浮指针及空指针[转].md: -------------------------------------------------------------------------------- 1 | 原文:[C++基础---失控指针、迷途指针、野指针、悬浮指针及空指针](http://blog.csdn.net/cainv89/article/details/47209449) 2 | 3 | 指针就是用来保存内存地址的变量,因此定义了一个指针后就要给它赋一个地址,或初始化为空指针。使用`delete`释放掉指针指向的内存后,不再使用该指针,将其置为空指针。 4 | 5 | **1. 失控指针** 6 | 7 | * 失控指针:也称迷途指针、野指针、悬浮指针,**指的是不指向任何合法的对象的指针,可以指向任何地址,并且对该地址的数值进行修改或删除,可能会造成意想不到的后果。** 8 | 9 | **2. 迷途指针(悬浮指针)** 10 | 11 | * 迷途指针:**当所指向的对象被释放或者收回,但是对该指针没有作任何的修改,以至于该指针仍旧指向已经回收的内存地址,此情况下该指针称为迷途指针。** 12 | * 若操作系统将这部分已经释放的内存重新分配给另外一个进程,而原来的程序重新引用现在的迷途指针,则将产生无法预料的后果。因为此时迷途指针所指向的内存现在包含的已经完全是不同的数据。通常来说,若原来的程序继续往迷途指针所指向的内存地址写入数据,这些和原来程序不相关的数据将被损坏,进而导致不可预料的程序错误。 13 | * 这种类型的程序错误,不容易找到问题的原因,通常会导致段错误(Linux系统中)和一般保护错误(Windows系统中)。如果操作系统的内存分配器将已经被覆盖的数据区域再分配,就可能会影响系统的稳定性。 14 | 15 | **3. 野指针** 16 | 17 | * 野指针:**未被初始化的指针**,野指针所导致的错误和迷途指针非常相似,但野指针的问题更容易被发现。 18 | 19 | **4. 空指针** 20 | 21 | * 空指针:就是一个**被赋值为0的指针**,它不指向任何的对象或者函数。 22 | 23 | **5. 重踏指针** 24 | 25 | * 重踏指针:**被释放后的指针不置为空指针,不再指向任何合法的内存,它仍可能指向原来的内存块,此时再定义一个新的指针,两个指针都指向同一块内存。** 26 | * 经典重踏指针程序实例: 27 | ```cpp 28 | #include 29 | using namespace std; 30 | typedef unsigned short int USHORT; 31 | int main() { 32 | USHORT *pShort = new USHORT; 33 | *pShort = 10; 34 | cout<<"pShort = "<m`,则自动取`n`值,即保证`n`个字符正常输出。 55 | * `f`格式:用来输出实数(包括单、双精度),以小数形式输出。有以下几种用法: 56 | * `%f`:不指定宽度,整数部分全部输出并输出6位小数。 57 | * `%m.nf`:输出共占`m`列,其中有`n`位小数,若数值宽度小于`m`左端补空格。 58 | * `%-m.nf`:输出共占`m`列,其中有`n`位小数,若数值宽度小于`m`右端补空格。 59 | * `e`格式:以指数形式输出实数。可用以下形式: 60 | * `%e`:数字部分(又称尾数)输出6位小数,指数部分占5位或4位。 61 | * `%m.ne,%-m.ne`:`m,n`和`-`字符含义与前相同。此处`n`指数据的数字部分的小数位数,`m`表示整个输出数据所占的宽度。 62 | * `g`格式:自动选`f`格式或`e`格式中较短的一种输出,且不输出无意义的零。 63 | 64 | **可变宽度参数** 65 | 66 | 对于`m.n`的格式还可以用如下方法表示: 67 | ```cpp 68 | char ch[20]; 69 | printf("%*.*s\n",m,n,ch); 70 | ``` 71 | 前边的`*`定义的是总的宽度,后边的定义的是输出的个数。分别对应外面的参数`m,n`。这种方法的好处是可以在语句之外对参数`m,n`赋值,从而控制输出格式。 72 | -------------------------------------------------------------------------------- /C++/shared_ptr线程安全性分析[转]/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/C++/shared_ptr线程安全性分析[转]/1.png -------------------------------------------------------------------------------- /C++/shared_ptr线程安全性分析[转]/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/C++/shared_ptr线程安全性分析[转]/2.png -------------------------------------------------------------------------------- /C++/text段、data段、bss段、堆和栈.md: -------------------------------------------------------------------------------- 1 | **text段** 2 | 3 | 代码段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。 4 | 5 | **文字常量区** 6 | 7 | 文字常量区:常量字符串、全局常量与静态常量存放在文字常量区(局部非静态常量存放在栈),此处存放的数据不可修改,程序结束后由系统释放。注意,若全局常量与静态常量被volatile关键字修饰,此时存放位置可能在文字常量区也可能不在,跟编译器有关。 8 | 9 | **bss段** 10 | 11 | bss段:bss段(bss segment)通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域,bss段并不给该段的数据分配空间,只是记录数据所需空间的大小,具体体现为一个占位符,所以,bss段是不占用exe文件空间的,其内容由操作系统初始化(清零)。bss是英文Block Started by Symbol的简称。bss段属于静态内存分配。 12 | 13 | **data段** 14 | 15 | 数据段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量和静态变量的一块内存区域,数据保存在目标文件中。数据段属于静态内存分配。bss段的大小从可执行文件中得到 ,然后链接器得到这个大小的内存块,紧跟在数据段后面。当这个内存区进入程序的地址空间后全部清零。包含data段和bss段的整个区段通常称为数据区。 16 | 17 | **堆(heap)** 18 | 19 | 堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减),若程序员不释放,程序结束时可能由系统回收。 20 | 21 | 堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。 22 | 23 | 当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。 24 | 25 | **栈(stack)** 26 | 27 | 栈(stack):栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。由于栈的先进后出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。它是由操作系统分配的,内存的申请与回收都由系统管理。 28 | 29 | 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。 30 | 31 | 在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在Windows下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。 32 | 33 | **示例** 34 | ```cpp 35 | #include 36 | using namespace std; 37 | 38 | int a = 0; // data段 39 | int b; // bss段 40 | int c = a; // bss段 41 | const int d = 0; // 文字常量区 42 | int e = d; // data段 43 | volatile const int f = 0; // 可能在文字常量区,也可能不在,跟编译器有关 44 | 45 | int main() { 46 | static int g = 0; // data段 47 | static int h; // bss段 48 | static const int i = 0; // 文字常量区 49 | volatile static const int j = 0; // 可能在文字常量区,也可能不在,跟编译器有关 50 | int k = 0; // 变量i在栈,字面值0在text段 51 | const int m = 0; // 常量j在栈,字面值0在text段 52 | char *pa = "pa"; // 指针pa在栈,字符串在文字常量区 53 | char pb[] = "pb"; // 数组pb在栈,字符串在text段 54 | char *pc = new char[3]{'p', 'c', '\0'}; // 指针pc在栈,指针所指向的空间在堆 55 | delete[]pc; // 释放内存 56 | return 0; 57 | } 58 | ``` 59 | 60 | **补充1:静态局部变量在什么时候被初始化?** 61 | 62 | 我们已经知道,已初始化的静态局部变量,其初始值保存在可执行文件中,内核在启动该程序时从源程序文件读入。换句话说,已初始化的普通局部变量,其初始值保存在text段,当程序运行到此处时,对该局部变量赋初始值,但已初始化的静态局部变量却并不是这样,**当初始值为常量时,静态局部变量保存在data段,早在程序加载时,系统就已经对其赋初始值**,而不是等到程序运行到我们给其赋初始值的时候才赋值;当初始值为变量时,静态局部变量保存在bss段,在程序加载时,系统就对其赋默认值0,静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化。 63 | 64 | 在下面的测试代码中,`static int b = 0xb;`并没有生成相对应的汇编代码,即,初始值不是保存在text段,程序也不执行该语句,从测试结果我们也可以看到,在我们给变量`b`赋初值之前,该变量就已经被赋值为`0xb`了。 65 | 66 | 测试代码: 67 | ```cpp 68 | #include 69 | using namespace std; 70 | 71 | int a = 0xa; 72 | 73 | int main() { 74 | int *p = &a; 75 | cout< 103 | [数据段、代码段、堆栈段、BSS段的区别](http://blog.csdn.net/jxhui23/article/details/8064766)
104 | [.bss段和.data段的区别](http://blog.csdn.net/jumper511/article/details/19902013)
105 | [text段,data段,bss段,堆和栈](http://www.cnblogs.com/hfww/archive/2011/06/04/2223366.html)
106 | [为什么栈的速度比堆要快](http://blog.csdn.net/maochengtao/article/details/8840690) 107 | -------------------------------------------------------------------------------- /C++/为什么多线程读写shared_ptr要加锁?[转].md: -------------------------------------------------------------------------------- 1 | 原文:[为什么多线程读写 shared_ptr 要加锁?](http://blog.csdn.net/solstice/article/details/8547547) 2 | 3 | `shared_ptr`的引用计数本身是安全且无锁的,但对象的读写则不是,因为`shared_ptr`有两个数据成员,读写操作不能原子化。`shared_ptr`的线程安全级别和内建类型、标准库容器、`std::string`一样,即: 4 | 5 | 1. 一个`shared_ptr`对象实体可被多个线程同时读取 6 | 2. 两个`shared_ptr`对象实体可以被两个线程同时写入 7 | 3. 如果要从多个线程读写同一个`shared_ptr`对象,那么需要加锁 8 | 9 | 请注意,以上是`shared_ptr`对象本身的线程安全级别,不是它管理的对象的线程安全级别。 10 | 11 | ### `shared_ptr`的数据结构 12 | 13 | `shared_ptr`是引用计数型(reference counting)智能指针,几乎所有的实现都采用在堆(heap)上放个计数值(count)的办法(除此之外理论上还有用循环链表的办法,不过没有实例)。具体来说,`shared_ptr`包含两个成员,一个是指向`Foo`的指针`ptr`,另一个是`ref_count`指针(其类型不一定是原始指针,有可能是`class`类型,但不影响这里的讨论),指向堆上的`ref_count`对象。`ref_count`对象有多个成员,具体的数据结构如图所示,其中`deleter`和`allocator`是可选的。 14 | 15 | ![](为什么多线程读写shared_ptr要加锁?[转]/1.png) 16 | 17 | 为了简化并突出重点,后文只画出`use_count`的值: 18 | 19 | ![](为什么多线程读写shared_ptr要加锁?[转]/2.png) 20 | 21 | 以上是`shared_ptr x(new Foo);`对应的内存数据结构。 22 | 23 | 如果再执行`shared_ptr y = x;`那么对应的数据结构如下: 24 | 25 | ![](为什么多线程读写shared_ptr要加锁?[转]/3.png) 26 | 27 | 但是`y=x`涉及两个成员的复制,这两步拷贝不会同时(原子)发生。 28 | 29 | * 步骤1:复制`ptr`指针: 30 | 31 | ![](为什么多线程读写shared_ptr要加锁?[转]/4.png) 32 | 33 | * 步骤2:复制`ref_count`指针,导致引用计数加1: 34 | 35 | ![](为什么多线程读写shared_ptr要加锁?[转]/5.png) 36 | 37 | 步骤1和步骤2的先后顺序跟实现相关(因此步骤2里没有画出`y.ptr`的指向),我见过的都是先1后2。 38 | 39 | 既然`y=x`有两个步骤,如果没有`mutex`保护,那么在多线程里就有race condition。 40 | 41 | ### 多线程无保护读写`shared_ptr`可能出现的race condition 42 | 43 | 考虑一个简单的场景,有3个`shared_ptr`对象`x,g,n`: 44 | ```cpp 45 | shared_ptr g(new Foo); // 线程之间共享的 shared_ptr 46 | shared_ptr x; // 线程 A 的局部变量 47 | shared_ptr n(new Foo); // 线程 B 的局部变量 48 | ``` 49 | ![](为什么多线程读写shared_ptr要加锁?[转]/6.png) 50 | 51 | 线程A执行`x = g;`(即read g),以下完成了步骤1,还没来及执行步骤2。这时切换到了B线程。 52 | 53 | ![](为什么多线程读写shared_ptr要加锁?[转]/7.png) 54 | 55 | 同时线程B执行`g = n;`(即write g),两个步骤一起完成了。 56 | 57 | 先是步骤1: 58 | 59 | ![](为什么多线程读写shared_ptr要加锁?[转]/8.png) 60 | 61 | 再是步骤2: 62 | 63 | ![](为什么多线程读写shared_ptr要加锁?[转]/9.png) 64 | 65 | 这是`Foo1`对象已经销毁,`x.ptr`成了空悬指针! 66 | 67 | 最后回到线程A,完成步骤2: 68 | 69 | ![](为什么多线程读写shared_ptr要加锁?[转]/10.png) 70 | 71 | 多线程无保护地读写`g`,造成了`x`是空悬指针的后果。这正是多线程读写同一个`shared_ptr`必须加锁的原因。 72 | 73 | 当然,race condition远不止这一种,其他线程交织(interweaving)有可能会造成其他错误。 74 | 75 | ### 其他 76 | 77 | **1. 为什么`ref_count`也有指向`Foo`的指针?** 78 | 79 | `shared_ptr sp(new Foo)`在构造`sp`的时候捕获了`Foo`的析构行为。实际上`shared_ptr.ptr`和`ref_count.ptr`可以是不同的类型(只要它们之间存在隐式转换),这是`shared_ptr`的一大功能。分3点来说: 80 | 81 | 1)无需虚析构。假设`Bar`是`Foo`的基类,但是`Bar`和`Foo`都没有虚析构。 82 | ```cpp 83 | shared_ptr sp1(new Foo); // ref_count.ptr的类型是Foo* 84 | shared_ptr sp2 = sp1; // 可以赋值,自动向上转型(up-cast) 85 | sp1.reset(); // 这时Foo对象的引用计数降为1 86 | ``` 87 | 此后`sp2`仍然能安全地管理`Foo`对象的生命期,并安全完整地释放`Foo`,因为其`ref_count`记住了`Foo`的实际类型。 88 | 89 | 2)`shared_ptr`可以指向并安全地管理(析构或防止析构)任何对象。 90 | ```cpp 91 | shared_ptr sp1(new Foo); // ref_count.ptr的类型是Foo* 92 | shared_ptr sp2 = sp1; // 可以赋值,Foo*向void*自动转型 93 | sp1.reset(); // 这时Foo对象的引用计数降为1 94 | ``` 95 | 此后`sp2`仍然能安全地管理`Foo`对象的生命期,并安全完整地释放`Foo`,不会出现`delete void*`的情况,因为`delete`的是`ref_count.ptr`,不是`sp2.ptr`。 96 | 97 | 3)多继承。假设`Bar`是`Foo`的多个基类之一,那么: 98 | ```cpp 99 | shared_ptr sp1(new Foo); 100 | shared_ptr sp2 = sp1; // 这时sp1.ptr和sp2.ptr可能指向不同的地址,因为Bar subobject在Foo object中的offset可能不为0。 101 | sp1.reset(); // 此时Foo对象的引用计数降为1 102 | ``` 103 | 但是`sp2`仍然能安全地管理`Foo`对象的生命期,并安全完整地释放`Foo`,因为`delete`的不是`Bar*`,而是原来的`Foo*`。换句话说,`sp2.ptr`和`ref_count.ptr`可能具有不同的值(当然它们的类型也不同)。 104 | 105 | **2. 为什么要尽量使用`make_shared()`?** 106 | 107 | 为了节省一次内存分配,原来`shared_ptr x(new Foo);`需要为`Foo`和`ref_count`各分配一次内存,现在用`make_shared()`的话,可以一次分配一块足够大的内存,供`Foo`和`ref_count`对象容身。数据结构是: 108 | 109 | ![](为什么多线程读写shared_ptr要加锁?[转]/11.png) 110 | 111 | 不过`Foo`的构造函数参数要传给`make_shared()`,后者再传给`Foo::Foo()`,这只有在C\+\+11里通过perfect forwarding才能完美解决。 112 | -------------------------------------------------------------------------------- /C++/为什么多线程读写shared_ptr要加锁?[转]/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/C++/为什么多线程读写shared_ptr要加锁?[转]/1.png -------------------------------------------------------------------------------- /C++/为什么多线程读写shared_ptr要加锁?[转]/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/C++/为什么多线程读写shared_ptr要加锁?[转]/10.png -------------------------------------------------------------------------------- /C++/为什么多线程读写shared_ptr要加锁?[转]/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/C++/为什么多线程读写shared_ptr要加锁?[转]/11.png -------------------------------------------------------------------------------- /C++/为什么多线程读写shared_ptr要加锁?[转]/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/C++/为什么多线程读写shared_ptr要加锁?[转]/2.png -------------------------------------------------------------------------------- /C++/为什么多线程读写shared_ptr要加锁?[转]/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/C++/为什么多线程读写shared_ptr要加锁?[转]/3.png -------------------------------------------------------------------------------- /C++/为什么多线程读写shared_ptr要加锁?[转]/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/C++/为什么多线程读写shared_ptr要加锁?[转]/4.png -------------------------------------------------------------------------------- /C++/为什么多线程读写shared_ptr要加锁?[转]/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/C++/为什么多线程读写shared_ptr要加锁?[转]/5.png -------------------------------------------------------------------------------- /C++/为什么多线程读写shared_ptr要加锁?[转]/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/C++/为什么多线程读写shared_ptr要加锁?[转]/6.png -------------------------------------------------------------------------------- /C++/为什么多线程读写shared_ptr要加锁?[转]/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/C++/为什么多线程读写shared_ptr要加锁?[转]/7.png -------------------------------------------------------------------------------- /C++/为什么多线程读写shared_ptr要加锁?[转]/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/C++/为什么多线程读写shared_ptr要加锁?[转]/8.png -------------------------------------------------------------------------------- /C++/为什么多线程读写shared_ptr要加锁?[转]/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/C++/为什么多线程读写shared_ptr要加锁?[转]/9.png -------------------------------------------------------------------------------- /C++/智能指针之make_unique与make_shared/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/C++/智能指针之make_unique与make_shared/1.png -------------------------------------------------------------------------------- /C++/智能指针之make_unique与make_shared/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/C++/智能指针之make_unique与make_shared/2.png -------------------------------------------------------------------------------- /C++/类中成员函数的重载、覆盖与隐藏[转].md: -------------------------------------------------------------------------------- 1 | 原文:[C++类成员函数的 重载、覆盖和隐藏区别](http://www.cnblogs.com/jiayith/p/3939683.html) 2 | 3 | **重载** 4 | 5 | 成员函数被重载的特征: 6 | 7 | * 相同的范围(在同一个类中) 8 | * 函数名字相同 9 | * 参数不同 10 | * virtual 关键字可有可无 11 | 12 | ```cpp 13 | #include 14 | using namespace std; 15 | 16 | class A { 17 | public: 18 | void show(int val) { 19 | cout << val << endl; 20 | } 21 | void show(double val) { 22 | cout << val << endl; 23 | } 24 | }; 25 | 26 | int main(void) { 27 | A a; 28 | a.show(4); 29 | a.show(4.2); 30 | return 0; 31 | } 32 | ``` 33 | 运行结果: 34 | ``` 35 | 4 36 | 4.2 37 | ``` 38 | 39 | **覆盖(重写)** 40 | 41 | 覆盖是指派生类函数覆盖基类函数,特征是: 42 | 43 | * 不同的范围(分别位于派生类与基类) 44 | * 函数名字相同 45 | * 参数相同 46 | * 基类函数必须有virtual 关键字 47 | 48 | ```cpp 49 | #include 50 | using namespace std; 51 | 52 | class A { 53 | public: 54 | virtual void show(int val) { 55 | cout << "A:" << val << endl; 56 | } 57 | }; 58 | 59 | class B : public A { 60 | public: 61 | void show(int val) { 62 | cout << "B:" << val << endl; 63 | } 64 | }; 65 | 66 | int main() { 67 | A a; 68 | a.show(4); 69 | A *p = new B; 70 | p->show(5); 71 | return 0; 72 | } 73 | ``` 74 | 运行结果: 75 | ``` 76 | A:4 77 | B:5 78 | ``` 79 | 80 | **隐藏** 81 | 82 | 隐藏是指派生类的函数屏蔽了与其同名的基类函数,规则如下: 83 | 84 | * 如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。 85 | * 如果派生类的函数与基类的函数同名,且参数也相同,但基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。 86 | * 《Effective C++》条款36说:绝不重新定义继承而来的non-virtual函数,所以就不要这么做了 87 | 88 | ```cpp 89 | #include 90 | using namespace std; 91 | 92 | class A { 93 | public: 94 | void show1(int val) { 95 | cout << "A:" << val << endl; 96 | } 97 | void show2(int val) { 98 | cout << "A:" << val << endl; 99 | } 100 | virtual void show3(int val) { 101 | cout << "A:" << val << endl; 102 | } 103 | }; 104 | 105 | class B : public A { 106 | public: 107 | void show1(int val) { 108 | cout << "B:" << val << endl; 109 | } 110 | void show2() { 111 | cout << "B:" << endl; 112 | } 113 | virtual void show3() { 114 | cout << "B:" << endl; 115 | } 116 | }; 117 | 118 | int main() { 119 | B b; 120 | b.show1(1); 121 | b.A::show1(2); 122 | b.show2(); 123 | // b.show2(3); // 报错 124 | b.A::show2(3); 125 | b.show3(); 126 | // b.show3(4); // 报错 127 | b.A::show3(4); 128 | return 0; 129 | } 130 | ``` 131 | 运行结果: 132 | ``` 133 | B:1 134 | A:2 135 | B: 136 | A:3 137 | B: 138 | A:4 139 | ``` 140 | -------------------------------------------------------------------------------- /DSP/CCSv4新建C6455工程.md: -------------------------------------------------------------------------------- 1 | **第1步:** 选择File->New->CCS Project,输入工程名为test,点击Next 2 | 3 | ![](CCSv4新建C6455工程/1.png) 4 | 5 | **第2步:** 由于芯片型号为C6455,选择Project Type为C6000系列,点击Next 6 | 7 | ![](CCSv4新建C6455工程/2.png) 8 | 9 | **第3步:** 选择存在相依性的工程。由于现在新建的工程是独立的,此步可跳过,直接Next 10 | 11 | ![](CCSv4新建C6455工程/3.png) 12 | 13 | **第4步:** 设置工程的相关属性:具体的芯片类型,大小端模式,Code Generation Tools,输出文件类,C语言运行库等等 14 | 1) **Device Variant:** C6000系列下面也有很多子系列,这个很好理解 15 | 2) **Device Endianness:** 编程的人都知道什么是大小端,小端模式比较常用。这里要注意的就是大小端模式一定要和后面的C语言运行库对应上。对我而言即**rts64plus.lib**。一般来说,小端模式的运行库是**rtsxxxx.lib**,大端模式的C运行库命名为**rtsxxxxe.lib**,这一点一定要注意 16 | 3) **Code Generation Tools:** 如果是在CCS4.2中导入CCS3.3的工程,那么编译的时候很有可能报关于Code Generation Tools相关的错误,很大的可能就是CCS3.3的工程使用的Code Generation Tools的版本在CCS4.2中没有安装,那么只需要右击工程,选择Properties,然后把对应的Code Generation Tools的版本改成当前CCS支持的版本即可 17 | 4) **输出文件类型:** 一般选择COFF 18 | 5) **Cmd文件:** 这个可以先不写,建好工程以后再添加 19 | 6) **C语言运行库:** 这个一定要设置正确,注意芯片类型和大小端模式 20 | 21 | ![](CCSv4新建C6455工程/4.png) 22 | 23 | **第5步:** 选择dsk6455 Examples->Empty Example,点击Finish,工程建立过程到此完毕。 24 | 25 | ![](CCSv4新建C6455工程/5.png) 26 | 27 | **第6步:** 将C6455的CSL库复制到新建工程目录下 28 | 29 | ![](CCSv4新建C6455工程/6.png) 30 | 31 | **第7步:** 右键点击工程->Properties->C++ Build->C6000 Compiler ->Include Options,添加CSL库头文件所在路径 32 | ``` 33 | "${PROJECT_ROOT}/csl_c64xplus_intc/inc" 34 | "${PROJECT_ROOT}/csl_c6455/inc" 35 | ``` 36 | ![](CCSv4新建C6455工程/7.png) 37 | 38 | **第8步:** 点击Properties->C++ Build->C6000 Linker ->File Search Path, 添加CSL库文件所在路径 39 | ``` 40 | "${PROJECT_ROOT}/csl_c6455/lib/csl_c6455.lib" 41 | "${PROJECT_ROOT}/csl_c64xplus_intc/lib/csl_c64xplus_intc.lib" 42 | ``` 43 | ![](CCSv4新建C6455工程/8.png) 44 | 45 | **第9步:** 将D:\\Texas Instruments\\ccsv4\\boards\\dsk6455_v2\\csl_c6455\\example\\gpio目录下的.c与.cmd文件复制到工程目录下,编译。可以看到编译通过 46 | 47 | **第10步:** 如果是裸跑dsp需要写好main.c、cmd文件和intvecs.asm即可。而dspbios则更方便易用。在main.c文件基础上再用CCS创建tcf(DSP/BIOS configuration file)即可,而cmd文件和intvecs.asm(中断向量表的声明)在编译的时候有tcf文件自动生成。下面说明下如何从裸跑dsp核触发中断切换到使用dspbios实现这些功能。原工程是裸跑触发中断的,将改成使用dspbios的HWI线程方式来实现中断。 48 | 49 | **第11步:** 把invecs.asm文件从工程remove掉。 50 | 51 | **第12步:** 右键点击工程->New->DSP/BIOS v5.x Configuration File新建dspbios configuration file,点击Next,选择dsk6455,Finish 52 | 53 | ![](CCSv4新建C6455工程/9.png) 54 | 55 | **第13步:** 开始配置tcf文件,Scheduling->HWI-Hardware Interrupt Service Routine Manager->HWI_INT4右键单击选择Propertie,interrupt selection number中选择15,function填上中断相应函数的名称,如果是C语言实现的要在前面加下划线,这里是_PCIinterrupt,其中interrupt selection number是指事件号,在TI的C6000系列中,有128个事件和15个中断源,需要通过配置把需要用到的事件号和中断源映射起来。 56 | 57 | ![](CCSv4新建C6455工程/10.png) 58 | 59 | **第14步:** 修改cmd文件,由于MEMORY在tcf文件的System->MEM下已定义,所以要把注释掉,并将L2修改为MEM下对应的名称 60 | 61 | ![](CCSv4新建C6455工程/11.png) ![](CCSv4新建C6455工程/12.png) 62 | 63 | **第15步:** 在头文件中添加dspbios声明的头文件,其中testcfg.h文件是编译tcf文件生成的,test7cfg.h文件声明了各个线程如HWI、SWI、LOG等。并添加void PCIinterrupt()中断函数,这里必须去掉interrupt前缀。因为dspbios不需要关键字interrupt来注明这是中断响应函数,如果加上interrupt关键字有时会造成很大麻烦。 64 | 65 | **第16步:** 整个dspbios工程建立完毕,编译直接把.out下载dsp核中就可触发中断。 66 | 67 | **参考文献** 68 | 69 | [使用CCS4进行DSP开发(Win7-64bit下DSP开发环境搭建)](http://blog.csdn.net/gjy938815/article/details/10023245)
70 | [DSPBIOS的HWI线程应用](http://blog.chinaunix.net/uid-29314073-id-4170817.html) 71 | -------------------------------------------------------------------------------- /DSP/CCSv4新建C6455工程/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/DSP/CCSv4新建C6455工程/1.png -------------------------------------------------------------------------------- /DSP/CCSv4新建C6455工程/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/DSP/CCSv4新建C6455工程/10.png -------------------------------------------------------------------------------- /DSP/CCSv4新建C6455工程/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/DSP/CCSv4新建C6455工程/11.png -------------------------------------------------------------------------------- /DSP/CCSv4新建C6455工程/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/DSP/CCSv4新建C6455工程/12.png -------------------------------------------------------------------------------- /DSP/CCSv4新建C6455工程/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/DSP/CCSv4新建C6455工程/2.png -------------------------------------------------------------------------------- /DSP/CCSv4新建C6455工程/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/DSP/CCSv4新建C6455工程/3.png -------------------------------------------------------------------------------- /DSP/CCSv4新建C6455工程/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/DSP/CCSv4新建C6455工程/4.png -------------------------------------------------------------------------------- /DSP/CCSv4新建C6455工程/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/DSP/CCSv4新建C6455工程/5.png -------------------------------------------------------------------------------- /DSP/CCSv4新建C6455工程/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/DSP/CCSv4新建C6455工程/6.png -------------------------------------------------------------------------------- /DSP/CCSv4新建C6455工程/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/DSP/CCSv4新建C6455工程/7.png -------------------------------------------------------------------------------- /DSP/CCSv4新建C6455工程/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/DSP/CCSv4新建C6455工程/8.png -------------------------------------------------------------------------------- /DSP/CCSv4新建C6455工程/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/DSP/CCSv4新建C6455工程/9.png -------------------------------------------------------------------------------- /IDE/Atom插件Markdown Preview Enhanced的字体大小修改.md: -------------------------------------------------------------------------------- 1 | 升级了`Markdown Preview Enhanced`之后发现原来的styles.less中对该插件字体大小的设置失效了。 2 | 3 | 记得之前是`[Ctrl]+[Shift]+i`打开开发者工具,然后定位到`Markdown Preview Enhanced`的预览窗口并进行字体调整,再将相关修改写入到styles.less文件中。 4 | ```less 5 | .markdown-preview-enhanced[for="preview"] { 6 | // background-color: #444; 7 | font-size: 23px; 8 | } 9 | ``` 10 | 然而升级之后这个方法行不通了,毕竟我对前端只是略微了解,折腾了半天,搞不定,也不想继续浪费时间了,所以确定直接修改该插件的css。 11 | 12 | 1. 选择`Packages->Settings View->Manage Packages->Markdown Preview Enhanced`,将`Preview Theme`设置为`github-dark.css` 13 | 2. 点击`View Code`,打开文件`node_modules\@shd101wyy\mume\styles\preview.css` 14 | 3. 将`.preview-container .mume[for="preview"] {...;font-size:16px;...}`修改为`.preview-container .mume[for="preview"] {...;font-size:23px;...}`,保存修改 15 | 4. 重新触发`Markdown Preview Enhanced`,修改生效 16 | 17 | **注意:**`Preview Theme`的其他主题可能需要进一步修改路径`node_modules\@shd101wyy\mume\styles\preview_theme`下相关主题的css。 18 | 19 | **另外:** 如果哪位知道怎么通过修改styles.less来改变`Markdown Preview Enhanced`的字体大小,可以在下面留言,感激不尽~ 20 | -------------------------------------------------------------------------------- /IDE/vim代码补全插件YouCompleteMe的自动化安装[转]/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/IDE/vim代码补全插件YouCompleteMe的自动化安装[转]/1.png -------------------------------------------------------------------------------- /IDE/vim常用命令.md: -------------------------------------------------------------------------------- 1 | #### 文件操作 2 | 3 | ```bash 4 | :new a.cpp # 新建文件a.cpp 5 | :e a.cpp # 打开文件a.cpp(若不存在则新建) 6 | :sp a.cpp # 打开文件a.cpp(水平分割窗口) 7 | :vsp a.cpp # 打开文件a.cpp(垂直分割窗口) 8 | :w # 保存文件 9 | :w a.cpp # 保存为文件a.cpp 10 | :q # 退出编辑器 11 | :q! # 退出编辑器,且不保存 12 | :wq # 退出编辑器,且保存文件 13 | ``` 14 | 15 | #### 文件目录 16 | 17 | ```bash 18 | [Shift]+r # 刷新文件列表 19 | ``` 20 | 21 | #### 设置行号 22 | 23 | ```bash 24 | :set nu # 显示行号 25 | :set nonu # 取消显示行号 26 | ``` 27 | 28 | #### 切换回shell 29 | 30 | ```bash 31 | :shell # 切换到shell,此时vim在后台运行 32 | exit # 在shell中输入命令exit,切换回vim 33 | ``` 34 | 35 | #### 移动光标 36 | 37 | ```bash 38 | h,j,k,l # 控制光标的左下上右移动,也可以使用方向键或鼠标 39 | 5+ # 向下跳5行 40 | 3- # 向上跳3行 41 | 3G # 跳到行号为3的行 42 | G # 跳到最后一行 43 | ``` 44 | 45 | #### 删除(剪切) 46 | 47 | ```bash 48 | x,X # x为删除当前字符 49 | # X为向左删除一个字符 50 | 5x,3X # 5x为删除从光标开始的5个字符 51 | # 3X为向左删除3个字符 52 | dd # 删除当前行 53 | D # 删除从光标开始的当前行的所有字符 54 | 2dd # 向下删除包括当前行的2行 55 | d5G # 删除当前行(包括)与第5行(包括)之间所有数据 56 | dG # 删除当前行(包括)到最后一行(包括)的所有数据 57 | d3j # 删除当前行与向下3行的所有数据 58 | ``` 59 | 60 | #### 复制与粘贴 61 | 62 | ```bash 63 | yy # 复制当前行 64 | 3yy # 复制包括当前行在内的向下3行 65 | y5G # 复制当前行(包括)与第5行(包括)之间所有数据 66 | yG # 复制当前行(包括)到最后一行(包括)的所有数据 67 | y3j # 复制当前行与向下3行的所有数据 68 | y^ # 复制从光标到行首的内容 69 | y$ # 复制从光标到行尾的内容 70 | ``` 71 | ```bash 72 | p,P # p为粘贴在光标后 73 | # P为粘贴在光标前 74 | ``` 75 | ```bash 76 | v,V # visual模式,通过上下左右键选择光标“扫过”的所有字符 77 | # visual line 模式,通过上下键选择光标“扫过”的所有行 78 | [Ctrl]+v # visual block 模式,通过上下左右键选择一个矩形文本 79 | y # 复制在visual、visual line和visual block模式下选择的文本 80 | d # 剪切在visual、visual line和visual block模式下选择的文本 81 | ``` 82 | 83 | #### 搜索与替换 84 | 85 | ```bash 86 | /atool # 向光标下搜索atool字符串 87 | ?atool # 向光标上搜索atool字符串 88 | n # 向下搜索前一个搜素动作 89 | N # 向上搜索前一个搜索动作 90 | ``` 91 | ```bash 92 | :s/old/new # 用new替换行中首次出现的old 93 | :s/old/new/g # 用new替换行中所有的old 94 | :1,5 s/old/new/g # 用new替换从第1行到第5行里所有的old 95 | :%s/old/new/g # 用new替换当前文件里所有的old 96 | ``` 97 | 98 | #### 撤销与恢复 99 | 100 | ```bash 101 | u # 撤销上一步操作 102 | U # 撤销对当前行的所有操作 103 | ``` 104 | ```bash 105 | [Ctrl]+r # 恢复撤销操作修改的内容 106 | ``` 107 | 108 | #### 插入与退出 109 | 110 | ```bash 111 | a,A # a为在当前光标位置的右边添加文本 112 | # A为在当前行的末尾位置添加文本 113 | i,I # i为在当前光标位置的左边添加文本 114 | # I为在当前行的开始处添加文本(非空字符的行首) 115 | o,O # o为在当前行的下面新建一行 116 | # O为在当前行的上面新建一行 117 | r,R # r为替换当前光标位置的文本 118 | # R为替换当前光标位置及后面的若干文本 119 | J # 合并光标所在行及下一行为一行 120 | ``` 121 | ```bash 122 | [Esc] # 退出,回到一般模式 123 | ``` 124 | 125 | #### 命令图解 126 | 127 | ![](vim常用命令/1.png) 128 | 129 | **参考链接** 130 | 131 | [Linux下创建文本文件(vi/vim命令使用详解)](http://www.cnblogs.com/EasonJim/p/6169985.html)
132 | [Windows上使用VIM入门之文件操作](http://www.cnblogs.com/tambor/archive/2011/12/28/vim_rumen_file_edit.html)
133 | [VIM/VI基本命令](http://www.atool.org/vim.php) 134 | -------------------------------------------------------------------------------- /IDE/vim常用命令/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/IDE/vim常用命令/1.png -------------------------------------------------------------------------------- /IDE/关于vim在插入模式中Backspace键无法删除的问题[转].md: -------------------------------------------------------------------------------- 1 | 原文:[Vim 里如何映射 CTRL-h 为 left ?](http://www.skywind.me/blog/archives/1857) 2 | 3 | 很多人习惯在配置文件中映射`ctrl+hjkl`为光标移动,却碰到了一些问题: 4 | ```vim 5 | inoremap 6 | inoremap 7 | inoremap 8 | inoremap 9 | ``` 10 | 映射后无效或者映射以后按``键不能删除,这是什么原因呢? 11 | 12 | 很简单,因为你的终端软件的默认配置是按下``键以后 13 | 发送的是:`^H`(`ASCII`码`0x08`),而`^H`在你的`vim`里被你 `inoremap`成了`ctrl+h`,所以你按了``会被认为按下了左键。 14 | 15 | 早在VT100终端时代,`^H`(`ASCII`码`0x08`)表示``而`^?`(`ASCII`码`0x7f`)表示``。过去`0x7f`是留给`DELETE`键使用的。而到了VT220时代,`DELETE`已经变为`^[[3~`(`ASCII`码 `0x1b,0x5b,0x33,0x7e`共4个字节),而`^?`的`0x7f`换给了我们的``,有些终端软件,默认``还是使用VT100的`^H`,你需要做的就是改一下终端默认配置而已。 16 | 17 | 你可以在你服务端下面查看下默认的键位设置: 18 | ```bash 19 | $ stty -a 20 | ``` 21 | 现在所有Linux服务器的`erase`(`bs`)基本都是`^?`了(如果链接到非Linux老操作系统`erase`不是这个的话,需要改一下,可以在系统层改,也可以vim里面`set t_kb=…`),vim里面也是认可`^?`的,可有些终端软件却默认发送`^H`,不过好在他们都支持修改: 22 | 23 | **Xshell的修改方法** 24 | 25 | `Properties->Terminal->Keyboard`里,把``设置成`127`,而 ``设置成`VT220 Del`。 26 | 27 | ![](关于vim在插入模式中Backspace键无法删除的问题[转]/1.png) 28 | 29 | **SecureCRT 6的设置方法** 30 | 31 | `Session Options->Terminal->Emulation->Mapped Keys`,勾选 `Backspace sends delete`。 32 | 33 | ![](关于vim在插入模式中Backspace键无法删除的问题[转]/2.png) 34 | 35 | **Putty 设置方法** 36 | 37 | 好像默认是`^?`的,不过需要到`Configuration->Terminal->Keyboard`下面确认下`The Backspace key`是`Control-? (127)`。 38 | 39 | ![](关于vim在插入模式中Backspace键无法删除的问题[转]/3.png) 40 | 41 | **Terminal.app** 42 | 43 | Mac下面的`Terminal.app`默认是发送`^?`的,你也可以到`Profiles Advanced`下面确认下`Delete sends Control-H`没有勾选。 44 | 45 | ![](关于vim在插入模式中Backspace键无法删除的问题[转]/4.jpg) 46 | 47 | **iTerm2** 48 | 49 | `iTerm2`下面默认也是发送`^?`的,可以到`Profiles->Keys`下面确认一下`Delete key sends ^H`没有被勾选。 50 | 51 | ![](关于vim在插入模式中Backspace键无法删除的问题[转]/5.png) 52 | 53 | **Gnome-Terminal** 54 | 55 | 至于`Gnome-Terminal`和`MinTTY`之流的默认是`^?`要修改可以看具体文本配置文件。 56 | 57 | 好了,默认vim同时识别`^H`(`ASCII`码`0x08`)和`^?`(`ASCII`码`0x7f`)都把这两个当成``,现在我们统一把终端软件的``改为`0x7f`以后,`^H`的`0x08`就空出来给我们`noremap`了: 58 | ```vim 59 | noremap 60 | noremap 61 | noremap 62 | noremap 63 | inoremap 64 | inoremap 65 | inoremap 66 | inoremap 67 | cnoremap 68 | cnoremap 69 | cnoremap 70 | cnoremap 71 | ``` 72 | -------------------------------------------------------------------------------- /IDE/关于vim在插入模式中Backspace键无法删除的问题[转]/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/IDE/关于vim在插入模式中Backspace键无法删除的问题[转]/1.png -------------------------------------------------------------------------------- /IDE/关于vim在插入模式中Backspace键无法删除的问题[转]/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/IDE/关于vim在插入模式中Backspace键无法删除的问题[转]/2.png -------------------------------------------------------------------------------- /IDE/关于vim在插入模式中Backspace键无法删除的问题[转]/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/IDE/关于vim在插入模式中Backspace键无法删除的问题[转]/3.png -------------------------------------------------------------------------------- /IDE/关于vim在插入模式中Backspace键无法删除的问题[转]/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/IDE/关于vim在插入模式中Backspace键无法删除的问题[转]/4.jpg -------------------------------------------------------------------------------- /IDE/关于vim在插入模式中Backspace键无法删除的问题[转]/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/IDE/关于vim在插入模式中Backspace键无法删除的问题[转]/5.png -------------------------------------------------------------------------------- /IDE/超强vim配置文件[转]/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/IDE/超强vim配置文件[转]/1.png -------------------------------------------------------------------------------- /Internet/DNS域名解析过程.md: -------------------------------------------------------------------------------- 1 | `DNS`,就是`Domain Name System`的缩写,翻译过来就是域名系统,是互联网上作为域名和`IP`地址相互映射的一个分布式数据库。`DNS`能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的`IP`数串。通过域名,最终得到该域名对应的`IP`地址的过程叫做域名解析(或主机名解析)。 2 | 3 | 下面这张图,详细说明了一个`DNS`域名解析的全过程: 4 | 5 | ![](DNS域名解析过程/1.jpg) 6 | 7 | 下面来详细解释`DNS`域名解析的过程: 8 | 9 | **第1步:** 客户机就是我们平常使用的电脑,打开浏览器,输入一个域名。比如输入`www.163.com`,这时,浏览器会检查缓存中有没有这个域名对应的解析过的`IP`地址,如果缓存中有,这个解析过程就将结束。 10 | 11 | 浏览器缓存域名也是有限制的,不仅浏览器缓存大小有限制,而且缓存的时间也有限制,通常情况下为几分钟到几小时不等,域名被缓存的时间限制可以通过`TTL(Time To Live)`属性来设置。 12 | 13 | 这个缓存时间太长和太短都不好,如果缓存时间太长,一旦域名被解析到的`IP`有变化,会导致被客户端缓存的域名无法解析到变化后的`IP`地址,以致该域名不能正常解析,这段时间内有可能会有一部分用户无法访问网站。如果时间设置太短,会导致用户每次访问网站都要重新解析一次域名。 14 | 15 | 阿里云解析默认的`TTL`是10分钟,10分钟的含义是,本地`DNS`服务器对于域名的缓存时间是10分钟,10分钟之后,本地`DNS`服务器就会删除这条记录,删除之后,如果有用户访问这个域名,就要重复一遍上述复杂的流程。其实,如果网站已经进入稳定发展的状态,不会轻易更换`IP`地址,我们完全可以将`TTL`设置到协议最大值,即24小时。带来的好处是,让域名解析记录能够更长时间的存放在本地`DNS`服务器中,以加快所有用户的访问。 16 | 17 | **第2步:** 如果用户的浏览器缓存中没有,操作系统会检查自己本地的`hosts`文件是否有这个网址映射关系,如果有,就先调用这个IP地址映射,完成域名解析。 18 | 19 | 在操作系统中也有一个域名解析的过程,在`Windows`中可以通过`C:\Windows\System32\drivers\etc\hosts`文件来设置,你可以将任何域名解析到任何能够访问的`IP`地址。 20 | 21 | 如果你在这里指定了一个域名对应的`IP`地址,那么浏览器会首先使用这个`IP`地址。例如,我们在测试时可以将一个域名解析到一台测试服务器上,这样不用修改任何代码就能测试到单独服务器上的代码的业务逻辑是否正确。 22 | 23 | 正是因为有这种本地`DNS`解析的过程,所以黑客就有可能通过修改你的域名解析来把特定的域名解析到它指定的`IP`地址上,导致这些域名被劫持。早期的`Windows`版本中出现过很严重的问题,而且对于一般没有太多电脑知识的用户来说,出现问题后很难发现,即使发现也很难自己解决,所以`Windows 7`中将`hosts`文件设置成了只读的,防止这个文件被轻易修改。 24 | 25 | 在`Linux`中这个配置文件是`/etc/named.conf`,修改这个文件可以达到同样的目的,当解析到这个配置文件中的某个域名时,操作系统会在缓存中缓存这个解析结果,缓存的时间同样是受这个域名的失效时间和缓存的空间大小控制的。 26 | 27 | **第3步:** 如果本机缓存没有相应的网址映射关系,客户机会发出一个`DNS`请求到本地`DNS`服务器。本地`DNS`服务器一般都是你的网络接入服务器商提供,比如中国电信,中国移动。 28 | 29 | 这个专门的本地`DNS`服务器性能都会很好,它们一般都会缓存域名解析结果,当然缓存时间是受域名的失效时间控制的,一般缓存空间不是影响域名失效的主要因素。大约80%的域名解析都到这里就已经完成了,所以本地`DNS`服务器主要承担了域名的解析工作。 30 | 31 | 在`Windows`下可以通过`ipconfig`查询这个地址: 32 | 33 | ![](DNS域名解析过程/2.jpg) 34 | 35 | 在`Linux`下可以通过如下方式查询配置的`DNS Server`: 36 | 37 | ![](DNS域名解析过程/3.jpg) 38 | 39 | **第4步:** 查询`www.163.com`的`DNS`请求到达本地`DNS`服务器后,若要查询的域名包含在本地配置区域资源中,则返回解析结果给客户机,完成域名解析,此解析具有权威性。如果要查询的域名,不由本地`DNS`服务器区域解析,但该服务器已缓存了此网址映射关系,则调用这个`IP`地址映射,完成域名解析,此解析不具有权威性。 40 | 41 | **第5步:** 如果本地`DNS`服务器本地区域文件与缓存解析都失效,则根据本地`DNS`服务器的设置(是否设置转发器)进行查询。 42 | 43 | 如果未用转发模式,本地`DNS`就把请求发至13台根`DNS`,根`DNS`服务器收到请求后会判断`.com`域名是谁来授权管理,并会返回一个负责该顶级域名服务器的一个`IP`。本地`DNS`服务器收到`IP`信息后,将会联系负责`.com`域的这台服务器。这台负责`.com`域的服务器收到请求后,如果自己无法解析,它就会找一个管理`.com`域的下一级`DNS`服务器地址(`163.com`)给本地`DNS`服务器。当本地`DNS`服务器收到这个地址后,就会找`163.com`域服务器,重复上面的动作,进行查询,直至找到`www.163.com`主机。 44 | 45 | 如果用的是转发模式,此`DNS`服务器就会把请求转发至上一级`DNS`服务器,由上一级服务器进行解析,上一级服务器如果不能解析,或找根`DNS`或把转请求转至上上级,以此循环。 46 | 47 | 不管是本地`DNS`服务器用是哪种模式,最后都是把结果返回给本地`DNS`服务器,由此`DNS`服务器再返回给客户机。 48 | 49 | **第6步:** 返回该域名对应的`IP`和`TTL`值,本地`DNS`服务器会缓存这个域名和`IP`的对应关系,缓存的时间由`TTL`值控制。 50 | 51 | **第7步:** 把解析的结果返回给用户,用户根据`TTL`值缓存在本地系统缓存中,以备下次别的用户查询时,可以直接返回结果,加快网络访问,域名解析过程结束。 52 | 53 | 54 | 55 | **参考链接** 56 | 57 | [一张图看懂DNS域名解析全过程](http://www.maixj.net/ict/dns-chaxun-9208)
58 | [DNS原理及其解析过程【精彩剖析】](http://369369.blog.51cto.com/319630/812889/)
59 | [DNS域名解析过程](http://www.360doc.com/content/13/0527/17/11253639_288596772.shtml)
60 | [DNS解析过程详解](http://blog.chinaunix.net/uid-28216282-id-3757849.html) 61 | -------------------------------------------------------------------------------- /Internet/DNS域名解析过程/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Internet/DNS域名解析过程/1.jpg -------------------------------------------------------------------------------- /Internet/DNS域名解析过程/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Internet/DNS域名解析过程/2.jpg -------------------------------------------------------------------------------- /Internet/DNS域名解析过程/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Internet/DNS域名解析过程/3.jpg -------------------------------------------------------------------------------- /Internet/HTTP协议[转]/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Internet/HTTP协议[转]/1.jpg -------------------------------------------------------------------------------- /Internet/HTTP协议[转]/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Internet/HTTP协议[转]/2.png -------------------------------------------------------------------------------- /Internet/HTTP协议[转]/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Internet/HTTP协议[转]/3.jpg -------------------------------------------------------------------------------- /Internet/IP、TCP、UDP首部详解[转].md: -------------------------------------------------------------------------------- 1 | 原文:[IP、TCP、UDP首部详解](http://blog.csdn.net/zhangliangzi/article/details/52554439) 2 | 3 | ### IP首部 4 | 5 | ![](IP、TCP、UDP首部详解[转]/1.jpg) 6 | 7 | **(1)第一个4字节(也就是第一行)** 8 | 9 | 1. 版本号(Version),4位;用于标识IP协议版本,IPv4是0100,IPv6是0110,也就是二进制的4和6。 10 | 2. 首部长度(Internet Header Length),4位;用于标识首部的长度,单位为4字节,所以首部长度最大值为:(2^4 - 1) * 4 = 60字节,但一般只推荐使用20字节的固定长度。 11 | 3. 服务类型(Type Of Service),8位;用于标识IP包的优先级,但现在并未使用。 12 | 4. 总长度(Total Length),16位;标识IP数据报的总长度,最大为:2^16 -1 = 65535字节。 13 | 14 | **(2)第二个四字节** 15 | 16 | 1. 标识(Identification),16位;用于标识`IP`数据报,如果因为数据链路层帧数据段长度限制(也就是`MTU`,支持的最大传输单元),`IP`数据报需要进行分片发送,则每个分片的`IP`数据报标识都是一致的。 17 | 2. 标识(Flag),3位,但目前只有2位有意义;最低位为`MF`,`MF=1`代表后面还有分片的数据报,`MF=0`代表当前数据报已是最后的数据报。次低位为`DF`,`DF=1`代表不能分片,`DF=0`代表可以分片。 18 | 3. 片偏移(Fragment Offset),13位;代表某个分片在原始数据中的相对位置。 19 | 20 | **(3)第三个四字节** 21 | 22 | 1. 生存时间(TTL),8位;以前代表`IP`数据报最大的生存时间,现在标识`IP`数据报可以经过的路由器数。 23 | 2. 协议(Protocol),8位;代表上层传输层协议的类型,1代表ICMP,2代表IGMP,6代表TCP,17代表UDP。 24 | 3. 校验和(Header Checksum),16位;用于验证数据完整性,计算方法为,首先将校验和位置零,然后将每16位二进制反码求和即为校验和,最后写入校验和位置。 25 | 26 | **(4)第四个四字节:源IP地址** 27 | 28 | **(5)第五个四字节:目的IP地址** 29 | 30 | ### TCP首部 31 | 32 | ![](IP、TCP、UDP首部详解[转]/2.png) 33 | 34 | **(1)第一个4字节** 35 | 36 | 1. 源端口,16位;发送数据的源进程端口 37 | 2. 目的端口,16位;接收数据的进程端口 38 | 39 | **(2)第二个4字节与第三个4字节** 40 | 41 | 1. 序号,32位;代表当前`TCP`数据段第一个字节占整个字节流的相对位置 42 | 2. 确认号,32位;代表接收端希望接收的数据序号,为上次接收到数据报的序号+1,当`ACK`标志位为1时才生效 43 | 44 | **(3)第四个4字节** 45 | 46 | 1. 数据偏移,4位;实际代表`TCP`首部长度,最大为60字 47 | 2. 6个标志位,每个标志位1位: 48 | * SYN,为同步标志,用于数据同步 49 | * ACK,为确认序号,ACK=1时确认号才有效 50 | * FIN,为结束序号,用于发送端提出断开连接 51 | * URG,为紧急序号,URG=1是紧急指针有效 52 | * PSH,指示接收方立即将数据提交给应用层,而不是等待缓冲区满 53 | * RST,重置连接 54 | 3. 窗口值,16位;标识接收方可接受的数据字节数 55 | 56 | **(4)第五个4字节** 57 | 58 | 1. 校验和,16位;用于检验数据完整性 59 | 2. 紧急指针,16位;只有当URG标识位为1时,紧急指针才有效。紧急指针的值与序号的相加值为紧急数据的最后一个字节位置。用于发送紧急数据 60 | 61 | ### UDP首部 62 | 63 | ![](IP、TCP、UDP首部详解[转]/3.jpg) 64 | -------------------------------------------------------------------------------- /Internet/IP、TCP、UDP首部详解[转]/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Internet/IP、TCP、UDP首部详解[转]/1.jpg -------------------------------------------------------------------------------- /Internet/IP、TCP、UDP首部详解[转]/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Internet/IP、TCP、UDP首部详解[转]/2.png -------------------------------------------------------------------------------- /Internet/IP、TCP、UDP首部详解[转]/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Internet/IP、TCP、UDP首部详解[转]/3.jpg -------------------------------------------------------------------------------- /Internet/OSI七层网络模型/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Internet/OSI七层网络模型/1.gif -------------------------------------------------------------------------------- /Internet/OSI七层网络模型/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Internet/OSI七层网络模型/2.png -------------------------------------------------------------------------------- /Internet/OSI七层网络模型/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Internet/OSI七层网络模型/3.jpg -------------------------------------------------------------------------------- /Internet/TCP协议.md: -------------------------------------------------------------------------------- 1 | ### TCP报文格式 2 | 3 | ![](TCP协议/1.png) 4 | 5 | * 16位源端口号与目的端口号:用于区别主机中的不同进程,而IP地址是用来区分不同的主机的,源端口号和目的端口号配合上IP首部中的源IP地址和目的IP地址就能唯一的确定一个TCP连接 6 | * 32位序号:用来标识从TCP发送端向TCP接收端发送的数据字节流,它表示在这个报文段中的的第一个数据字节在数据字节流中的偏移,主要用来解决网络报乱序的问题 7 | * 32位确认序号:用作对另一方发送来的TCP报文段的响应。其值是收到的TCP报文段的序号值加1。不过,只有当标志位中的ACK标志为1时该确认序列号的字段才有效。主要用来解决不丢包的问题 8 | * 4位首部长度:给出首部中32bit字的数目,这个字段占4bit,最多能表示15个32bit的的字,即4*15=60个字节的首部长度,因此TCP最多有60字节的首部,若不存在选项,则这个值为20字节 9 | * 标志位:TCP首部中有6个标志位,它们中的多个可同时被设置为1,主要是用于操控TCP的状态机的,依次为URG,ACK,PSH,RST,SYN,FIN。每个标志位的意思如下: 10 | * URG:此标志表示TCP包的16位紧急指针是否有效,用来保证TCP连接不被中断,并且督促中间层设备要尽快处理这些数据 11 | * ACK:此标志表示32位确认序号是否有效,一般称携带ACK标志的TCP报文段为“确认报文段” 12 | * PSH:这个标志位表示Push操作。所谓Push操作就是指在数据包到达接收端以后,立即传送给应用程序,而不是在缓冲区中排队 13 | * RST:这个标志表示连接复位请求。用来复位那些产生错误的连接,也被用来拒绝错误和非法的数据包,一般称携带RST标志的TCP报文段为“复位报文段” 14 | * SYN:表示同步序号,请求建立一个连接,一般称携带SYN标志的TCP报文段为“同步报文段” 15 | * FIN:表示发送端已经达到数据末尾,也就是说双方的数据传送完成,没有数据可以传送了,发送FIN标志位的TCP数据包后,连接将被断开,一般称携带FIN标志的TCP报文段为“结束报文段” 16 | * 16位窗口大小:是TCP流量控制的一个手段。这里说的窗口,指的是接收通告窗口。它告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度 17 | * 16位校验和:发送端对TCP报文段执行CRC算法计算一个数值,接收端计算结果要与发送端完全一样,从而证明数据的有效性。该校验不仅包括TCP首部,也包括数据部分。这是一个强制性的字段,一定是由发送端计算和存储,并由接收端进行验证的 18 | * 16位紧急指针:指向后面是紧急数据的字节,在URG标志设置为1时才有效。如果URG标志为0,紧急域作为填充 19 | 20 | ### TCP的连接与终止 21 | 22 | ![](TCP协议/2.png) ![](TCP协议/3.jpg) 23 | 24 | 其实,网络上的传输是没有连接的,包括TCP也是一样的。而TCP所谓的“连接”,其实只不过是在通讯的双方维护一个“连接状态”,让它看上去好像有连接一样。所以,TCP的状态变换是非常重要的。 25 | 26 | #### TCP三次握手 27 | 28 | 对于建立链接的3次握手,主要是要初始化Sequence Number的初始值。通信的双方要互相通知对方自己的初始化的Sequence Number(缩写为ISN:Inital Sequence Number),所以叫SYN,全称Synchronize Sequence Numbers。也就上图中的x和y。这个号要作为以后的数据通信的序号,以保证应用层接收到的数据不会因为网络上的传输的问题而乱序(TCP会用这个序号来拼接数据)。 29 | 30 | 简单来说,就是 31 | 32 | 1. 建立连接时,客户端发送SYN包(SYN=i)到服务器,并进入到SYN-SEND状态,等待服务器确认 33 | 2. 服务器收到SYN包,必须确认客户的SYN(ack=i+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器进入SYN-RECV状态 34 | 3. 客户端收到服务器的SYN+ACK包,向服务器发送确认报ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手 35 | 36 | **为什么要三次握手?** 37 | 38 | 为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。例如如下情况: 39 | 40 | client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。 41 | 42 | #### TCP四次挥手 43 | 44 | 由于TCP连接时是全双工的,因此每个方向都必须单独进行关闭。这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的链接。收到一个FIN只是意味着这一方向上没有数据流动,既不会再收到数据,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN,首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭。 45 | 46 | 服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN 报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送。 47 | 48 | 简单来说就是 49 | 50 | 1. 客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送 51 | 2. 服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号 52 | 3. 服务器B关闭与客户端A的连接,发送一个FIN给客户端A 53 | 4. 客户端A发回ACK报文确认,并将确认序号设置为收到序号加1 54 | 55 | A在进入到TIME-WAIT状态后,并不会马上释放TCP,必须经过时间等待计时器设置的时间2MSL(最长报文段寿命),A才进入到CLOSED状态。为什么? 56 | 57 | * 保证TCP协议的全双工连接能够可靠关闭 58 | * 保证这次连接的重复数据段从网络中消失 59 | 60 | 先说第一点,如果Client直接CLOSED了,那么由于IP协议的不可靠性或者是其它网络原因,导致Server没有收到Client最后回复的ACK。那么Server就会在超时之后继续发送FIN,此时由于Client已经CLOSED了,就找不到与重发的FIN对应的连接,最后Server就会收到RST而不是ACK,Server就会以为是连接错误把问题报告给高层。这样的情况虽然不会造成数据丢失,但是却导致TCP协议不符合可靠连接的要求。所以,Client不是直接进入CLOSED,而是要保持TIME_WAIT,当再次收到FIN的时候,能够保证对方收到ACK,最后正确的关闭连接。 61 | 62 | 再说第二点,如果Client直接CLOSED,然后又再向Server发起一个新连接,我们不能保证这个新连接与刚关闭的连接的端口号是不同的。也就是说有可能新连接和老连接的端口号是相同的。一般来说不会发生什么问题,但是还是有特殊情况出现:假设新连接和已经关闭的老连接端口号是一样的,如果前一次连接的某些数据仍然滞留在网络中,这些延迟数据在建立新连接之后才到达Server,由于新连接和老连接的端口号是一样的,又因为TCP协议判断不同连接的依据是socket pair,于是,TCP协议就认为那个延迟的数据是属于新连接的,这样就和真正的新连接的数据包发生混淆了。所以TCP连接还要在TIME_WAIT状态等待2倍MSL,这样可以保证本次连接的所有数据都从网络中消失。 63 | 64 | **为什么要四次挥手?** 65 | 66 | TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。TCP是全双工模式,这就意味着,当主机1发出FIN报文段时,只是表示主机1已经没有数据要发送了,主机1告诉主机2,它的数据已经全部发送完毕了;但是,这个时候主机1还是可以接受来自主机2的数据;当主机2返回ACK报文段时,表示它已经知道主机1没有数据发送了,但是主机2还是可以发送数据到主机1的;当主机2也发送了FIN报文段时,这个时候就表示主机2也没有数据要发送了,就会告诉主机1,我也没有数据要发送了,之后彼此就会中断这次TCP连接。 67 | 68 | **参考链接** 69 | 70 | [TCP协议深度解析](http://www.kuqin.com/shuoit/20141018/342719.html)
71 | [关于TCP协议,我想你应该懂了!](http://network.51cto.com/art/201411/456783.htm)
72 | [TCP协议详解(理论篇)](http://www.2cto.com/net/201208/149347.html)
73 | [TCP协议详解](http://www.jianshu.com/p/ef892323e68f)
74 | [TCP协议解析](http://www.cnblogs.com/Adoryn/p/4134981.html)
75 | [TCP/IP 协议详解内容总结](http://blog.jobbole.com/91841/) 76 | -------------------------------------------------------------------------------- /Internet/TCP协议/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Internet/TCP协议/1.png -------------------------------------------------------------------------------- /Internet/TCP协议/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Internet/TCP协议/2.png -------------------------------------------------------------------------------- /Internet/TCP协议/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Internet/TCP协议/3.jpg -------------------------------------------------------------------------------- /Java/Java的内省机制.md: -------------------------------------------------------------------------------- 1 | 内省(`Introspector`)是`Java`语言对`JavaBean`类属性、事件的一种缺省处理方法。`JavaBean`是一种特殊的类,遵守`JavaBean`的规范,即提供默认构造方法,同时所有的属性都是私有的,且每个属性都有公开的读取和设定的方法(`getter`和`setter`),这些方法都遵守命名的规范。例如类`Person`中有属性`name`,可以通过`getName/setName`来得到其值或者设置新的值。 2 | 3 | `Person`类: 4 | ```java 5 | package com.sigalhu; 6 | 7 | public class Person { 8 | private String name; 9 | private String sex; 10 | private int age; 11 | 12 | public Person() { 13 | this.name = "SigalHu"; 14 | this.sex = "男"; 15 | this.age = 26; 16 | } 17 | 18 | public Person(String name, String sex, int age) { 19 | this.name = name; 20 | this.sex = sex; 21 | this.age = age; 22 | } 23 | 24 | public String getName() { 25 | return name; 26 | } 27 | 28 | public void setName(String name) { 29 | this.name = name; 30 | } 31 | 32 | public void setSex(String sex) { 33 | this.sex = sex; 34 | } 35 | 36 | public String getSex() { 37 | return sex; 38 | } 39 | 40 | public int getAge() { 41 | return age; 42 | } 43 | 44 | public void setAge(int age) { 45 | this.age = age; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "Person{" + 51 | "name=" + name + 52 | ", sex=" + sex + 53 | ", age=" + age + 54 | '}'; 55 | } 56 | } 57 | ``` 58 | 59 | 首先通过`Introspector`类来获取某个对象的`BeanInfo`信息,然后通过`BeanInfo`来获取属性的描述器`PropertyDescriptor`,通过`PropertyDescriptor`就可以获取某个属性对应的`getter/setter`方法,因此就可以通过反射机制来调用这些方法。此外,还可以通过构造方法直接创建`PropertyDescriptor`对象来对某个对象的属性进行相应操作。 60 | 61 | `PropertyDescriptor`类的主要方法有: 62 | * `getPropertyType()`:获得属性的`Class`对象; 63 | * `getReadMethod()`:获得用于读取属性值的方法; 64 | * `getWriteMethod()`:获得用于写入属性值的方法; 65 | * `hashCode()`:获取对象的哈希值; 66 | * `setReadMethod(Method readMethod)`:设置用于读取属性值的方法; 67 | * `setWriteMethod(Method writeMethod)`:设置用于写入属性值的方法。 68 | 69 | `Main`类: 70 | ```java 71 | package com.sigalhu; 72 | 73 | import java.beans.BeanInfo; 74 | import java.beans.Introspector; 75 | import java.beans.PropertyDescriptor; 76 | 77 | public class Main { 78 | static public void main(String[] args) throws Exception{ 79 | Person person = new Person("a","男",25); 80 | 81 | //如果不想把父类的属性也列出来的话,getBeanInfo的第二个参数填写父类的信息 82 | BeanInfo beanInfo = Introspector.getBeanInfo(Person.class, Object.class); 83 | 84 | //读取所有属性值 85 | PropertyDescriptor[] props = beanInfo.getPropertyDescriptors(); 86 | for(PropertyDescriptor prop:props){ 87 | System.out.println(prop.getName() + ": " + prop.getReadMethod().invoke(person)); 88 | } 89 | System.out.println(); 90 | 91 | //写入指定属性值 92 | PropertyDescriptor propName = new PropertyDescriptor("name",Person.class); 93 | propName.getWriteMethod().invoke(person,"b"); 94 | System.out.println(person); 95 | System.out.println(); 96 | 97 | //设置用于写入属性值的方法 98 | PropertyDescriptor propSex = new PropertyDescriptor("sex",Person.class); 99 | propName.setWriteMethod(propSex.getWriteMethod()); 100 | propName.getWriteMethod().invoke(person,"c"); 101 | System.out.println(person); 102 | } 103 | } 104 | ``` 105 | 运行结果: 106 | ```java 107 | age: 25 108 | name: a 109 | sex: 男 110 | 111 | Person{name=b, sex=男, age=25} 112 | 113 | Person{name=b, sex=c, age=25} 114 | ``` 115 | 116 | **参考链接** 117 | 118 | [Java 内省机制](https://www.cnblogs.com/sunfie/p/4350941.html)
119 | [java的内省机制](http://geeksun.iteye.com/blog/539222)
120 | [Java内省机制小总结](http://blog.csdn.net/changqianger/article/details/45341651)
121 | [《JavaBean》-Java的内省机制](http://blog.csdn.net/zhoukun1008/article/details/52124242)
122 | [Java bean 是个什么概念?](https://www.zhihu.com/question/19773379)
123 | [Java内省机制](http://blog.csdn.net/hahalzb/article/details/5972421) 124 | 125 | -------------------------------------------------------------------------------- /Linux/Linux常用命令.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Linux/Linux常用命令.md -------------------------------------------------------------------------------- /Matlab/Matlab生成exe文件.md: -------------------------------------------------------------------------------- 1 | **第1步:** 打开matlab2013a,将目录选择为要转化的工程目录,并在指令窗口输入deploytool,弹出界面,其中Type中的Windows Standalone Application与Console Application都可以生成exe,区别在于Console Application生成的exe在打开时会弹出dos窗口,该窗口在程序出错时会显示错误信息,还有个作用就是如果需要手动输入参数时,比如每次定义a等于多少(在m文件里是 a=input('请输入一个数')),现在则可以通过dos窗口输入 2 | 3 | ![](Matlab生成exe文件/1.png) 4 | 5 | **第2步:** 先试试Console Application,OK 6 | 7 | ![](Matlab生成exe文件/2.png) 8 | 9 | **第3步:** 通过1添加主程序m文件,在2中添加工程中的其他m文件或包含m文件的文件夹,如下图所示(若跳过2直接执行3,可能会报错,如本工程报错如下,原因是主程序中调用的函数GetParameters在封装时并没有封装工程中自己编的GetParameters函数,而是封装了matlab库中的一个同名的函数) 10 | 11 | ![](Matlab生成exe文件/3.png) ![](Matlab生成exe文件/4.png) ![](Matlab生成exe文件/5.png) 12 | 13 | **第4步:** 生成exe成功后将工程中用到的文件拷贝到comSymDemotest\\distrib\\,运行成功!也可以在执行2时,将File文件夹添加进去,执行3,生成后也可以运行,不过这样会导致数据文件不可见,而且在界面上更改文件名会导致文件无法打开,故不提倡 14 | 15 | ![](Matlab生成exe文件/6.png) ![](Matlab生成exe文件/7.png) ![](Matlab生成exe文件/8.png) 16 | 17 | **第5步:** 若程序成功运行,新建工程comSymDemo,选择Windows Standalone Application,OK 18 | 19 | ![](Matlab生成exe文件/9.png) 20 | 21 | **第6步:** 下面的步骤和Console Application一样 22 | 23 | ![](Matlab生成exe文件/10.png) 24 | 25 | **第7步:** 若是想在没有安装matlab2013a的电脑上运行该exe,则选择Package中的Add MCR,OK并选择File文件夹,点击Package生成安装包 26 | 27 | ![](Matlab生成exe文件/11.png) ![](Matlab生成exe文件/12.png) ![](Matlab生成exe文件/13.png) 28 | -------------------------------------------------------------------------------- /Matlab/Matlab生成exe文件/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Matlab/Matlab生成exe文件/1.png -------------------------------------------------------------------------------- /Matlab/Matlab生成exe文件/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Matlab/Matlab生成exe文件/10.png -------------------------------------------------------------------------------- /Matlab/Matlab生成exe文件/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Matlab/Matlab生成exe文件/11.png -------------------------------------------------------------------------------- /Matlab/Matlab生成exe文件/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Matlab/Matlab生成exe文件/12.png -------------------------------------------------------------------------------- /Matlab/Matlab生成exe文件/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Matlab/Matlab生成exe文件/13.png -------------------------------------------------------------------------------- /Matlab/Matlab生成exe文件/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Matlab/Matlab生成exe文件/2.png -------------------------------------------------------------------------------- /Matlab/Matlab生成exe文件/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Matlab/Matlab生成exe文件/3.png -------------------------------------------------------------------------------- /Matlab/Matlab生成exe文件/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Matlab/Matlab生成exe文件/4.png -------------------------------------------------------------------------------- /Matlab/Matlab生成exe文件/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Matlab/Matlab生成exe文件/5.png -------------------------------------------------------------------------------- /Matlab/Matlab生成exe文件/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Matlab/Matlab生成exe文件/6.png -------------------------------------------------------------------------------- /Matlab/Matlab生成exe文件/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Matlab/Matlab生成exe文件/7.png -------------------------------------------------------------------------------- /Matlab/Matlab生成exe文件/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Matlab/Matlab生成exe文件/8.png -------------------------------------------------------------------------------- /Matlab/Matlab生成exe文件/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Matlab/Matlab生成exe文件/9.png -------------------------------------------------------------------------------- /Matlab/VC调用Matlab生成的c.md: -------------------------------------------------------------------------------- 1 | MATLAB Coder可以从MATLAB代码生成独立的、可读性强、可移植的C/C++代码。 2 | 3 | 使用MATLAB Coder产生代码的3个步骤: 4 | 1. 准备用于产生代码的MATLAB算法; 5 | 2. 检查MATLAB代码的兼容性(有些matlab代码语句并不能生成c/c++代码); 6 | 3. 产生最终使用的源代码或MEX。 7 | 8 | 利用MATLAB Coder生成c++代码,并在vs2010中验证: 9 | 10 | **第1步:** 打开Matlab2013a,新建interweava.m文件与deinterweaving.m文件 11 | ```matlab 12 | function [interweava_out,interweava_zeros] = interweava(interweava_in,mode) %#codegen 13 | interweava_zeros = 0; 14 | if strcmp(mode,'无') 15 | interweava_out = interweava_in; 16 | elseif strcmp(mode, '块交织') 17 | interweava_zeros = mod(length(interweava_in),100); 18 | if interweava_zeros 19 | interweava_zeros = 100 - interweava_zeros; 20 | interweava_in = [interweava_in,zeros(1,interweava_zeros)]; 21 | end 22 | 23 | interweava_temp = reshape(interweava_in,100,[]); 24 | interweava_out = reshape(interweava_temp',1,[]); 25 | else 26 | interweava_out = 1; 27 | end 28 | end 29 | ``` 30 | 其中,%#codegen可以防止出现警告错误 31 | ```matlab 32 | function [deinterweava_out] = deinterweaving(deinterweava_in,mode,interweava_zeros) 33 | if strcmp(mode,'无') 34 | deinterweava_out = deinterweava_in; 35 | elseif strcmp(mode, '块交织') 36 | deinterweava_temp = reshape(deinterweava_in,[],100); 37 | deinterweava_out = reshape(deinterweava_temp',1,[]); 38 | deinterweava_out = deinterweava_out(1:end-interweava_zeros); 39 | else 40 | deinterweava_out = 1; 41 | end 42 | end 43 | ``` 44 | 45 | **第2步:** 在命令窗口,输入mex -setup,选中一个存在的编译器; 46 | 47 | **第3步:** 在命令窗口输入coder(图形界面),回车,弹出MATLAB Coder Project对话框; 48 | 49 | **第4步:** 在New选项卡Name中输入一个工程名interweava.prj;点击Ok,弹出MATLAB Coder MEX Function对话框; 50 | 51 | **第5步:** 在Overview选项卡中,点击Add files,弹出对话框,选中interweava.m打开,并选择输入变量的数据类型; 52 | 53 | **第6步:** 选中Build选项卡,Output type中选择c/c++ Static Library;选中Generate code only; 54 | 55 | **第7步:** 点击Build,进行编译;点击View report,弹出Code Generation Report对话框,此时,变量interweava_in、mode、interweava_out、interweava_zeros会显示相应的变量信息; 56 | 57 | ![](VC调用Matlab生成的c/1.png) ![](VC调用Matlab生成的c/2.png) 58 | 59 | **第8步:** 打开VS2010,新建Win32 Console Application工程,并选择Empty project; 60 | 61 | ![](VC调用Matlab生成的c/3.png) ![](VC调用Matlab生成的c/4.png) 62 | 63 | **第9步:** 将Matlab生成的codegen\\lib\\deinterweaving\\下所有.c和.h文件复制到新建工程目录,并添加到工程; 64 | 65 | ![](VC调用Matlab生成的c/5.png) 66 | 67 | **第10步:** 新建一个cpp文件,代码为 68 | ```cpp 69 | #include 70 | extern"C" 71 | { 72 | #include "deinterweaving.h" 73 | #include "interweava.h" 74 | #include "deinterweaving_types.h" 75 | #include "deinterweaving_emxAPI.h" 76 | } 77 | using namespace std; 78 | void main() 79 | { 80 | double a[10] = {1,0,0,1,1,1,0,0,0,0}; 81 | emxArray_real_T * data_in = emxCreate_real_T(1,1001); 82 | emxArray_char_T * mode_in = emxCreateWrapper_char_T("块交织",1,6); 83 | emxArray_real_T * data_out = emxCreate_real_T(1,1100); 84 | emxArray_real_T * data_in1 = emxCreate_real_T(1,1001); 85 | real_T zero_num = 9; 86 | 87 | for (int ii=0;ii<100;ii++) 88 | { 89 | for (int jj=0;jj<10;jj++) 90 | { 91 | data_in->data[ii*10+jj] = a[jj]; 92 | } 93 | } 94 | data_in->data[1000] = 1; 95 | 96 | interweava(data_in,mode_in,data_out,&zero_num); 97 | deinterweaving(data_out,mode_in,zero_num,data_in1); 98 | 99 | for (int ii=0;ii<1100;ii++)//data_out->allocatedSize 100 | { 101 | cout<data[ii]<<" "<data[ii]<<" "<data[ii]<<" "<Property->Configuration Properties->Linker->Input->Additional Dependencies下添加add.lib mclmcrrt.lib 31 | 32 | ![](VC调用Matlab生成的dll/4.png) 33 | 34 | **第8步:** 在Progect->Property->Configuration Properties->VC++ Directories(在VS2005中为Tools->Options->Projects and Solutions-> VC++ Directories)->Include Directories与Library Directories下添加路径如下图所示 35 | 36 | ![](VC调用Matlab生成的dll/5.png) 37 | 38 | **第9步:** 新建文件add.cpp,代码如下,编译并运行成功 39 | ```cpp 40 | #include 41 | #include "add.h" 42 | #include "mclmcr.h" 43 | 44 | using namespace std; 45 | 46 | void main() 47 | { 48 | if(!mclInitializeApplication(NULL,0)) // hu 只用初始化一次 49 | { 50 | cout<<"初始化失败"<Import->Fragment,导入Project2文件 10 | 11 | ![](合并StarUML的多个Project文件/3.png) 12 | 13 | 可以看到此时Project2位于Project1中 14 | 15 | ![](合并StarUML的多个Project文件/4.png) 16 | 17 | 我们使用鼠标将Model2拖到Project1下面 18 | 19 | ![](合并StarUML的多个Project文件/5.png) 20 | 21 | 此时Project2中已经没有Model,但我们无法将其删除 22 | 23 | ![](合并StarUML的多个Project文件/6.png) 24 | 25 | 保存Project1文件并退出StartUML,使用Notepad打开Project1文件,可以看到文件里是一个json,删除其中`name`为`Project2`的对象,保存文件,关闭Notepad 26 | 27 | ```json 28 | { 29 | "_type": "Project", 30 | "_id": "AAAAAAFF+h6SjaM2Hec=", 31 | "name": "Project1", 32 | "ownedElements": [ 33 | { 34 | "_type": "UMLModel", 35 | "_id": "AAAAAAFF+qBWK6M3Z8Y=", 36 | "_parent": { 37 | "$ref": "AAAAAAFF+h6SjaM2Hec=" 38 | }, 39 | "name": "Model1", 40 | "ownedElements": [ 41 | ... 42 | ], 43 | "visibility": "public" 44 | }, 45 | { 46 | "_type": "Project", 47 | "_id": "AAAAAAFj1cChHdGgCAU=", 48 | "_parent": { 49 | "$ref": "AAAAAAFF+h6SjaM2Hec=" 50 | }, 51 | "name": "Project2" 52 | }, 53 | { 54 | "_type": "UMLModel", 55 | "_id": "AAAAAAFj1cChHdGfLdo=", 56 | "_parent": { 57 | "$ref": "AAAAAAFF+h6SjaM2Hec=" 58 | }, 59 | "name": "Model2", 60 | "ownedElements": [ 61 | ... 62 | ], 63 | "visibility": "public" 64 | } 65 | ] 66 | } 67 | ``` 68 | 69 | 再使用StartUML打开Project1,可以看到Project2已经被删除了 70 | 71 | ![](合并StarUML的多个Project文件/7.png) 72 | -------------------------------------------------------------------------------- /UML/合并StarUML的多个Project文件/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/UML/合并StarUML的多个Project文件/1.png -------------------------------------------------------------------------------- /UML/合并StarUML的多个Project文件/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/UML/合并StarUML的多个Project文件/2.png -------------------------------------------------------------------------------- /UML/合并StarUML的多个Project文件/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/UML/合并StarUML的多个Project文件/3.png -------------------------------------------------------------------------------- /UML/合并StarUML的多个Project文件/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/UML/合并StarUML的多个Project文件/4.png -------------------------------------------------------------------------------- /UML/合并StarUML的多个Project文件/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/UML/合并StarUML的多个Project文件/5.png -------------------------------------------------------------------------------- /UML/合并StarUML的多个Project文件/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/UML/合并StarUML的多个Project文件/6.png -------------------------------------------------------------------------------- /UML/合并StarUML的多个Project文件/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/UML/合并StarUML的多个Project文件/7.png -------------------------------------------------------------------------------- /Windows/不同进程通过共享内存实现数据共享.md: -------------------------------------------------------------------------------- 1 | **第1步:** 新建控制台工程,主进程代码如下 2 | ```cpp 3 | #include "stdafx.h" 4 | #include "windows.h" 5 | 6 | int _tmain(int argc, _TCHAR* argv[]) 7 | { 8 | wchar_t MemShareName[] = L"MemShareForTest"; 9 | LPVOID pMemShare; 10 | int data = 10; 11 | 12 | //HANDLE hMap = ::OpenFileMapping(FILE_MAP_ALL_ACCESS,0,MemShareName); 13 | HANDLE hMap = ::CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,128,MemShareName); // hu 空间大小为Bytes 14 | pMemShare = ::MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,0); 15 | memcpy((int*)pMemShare,&data,sizeof(data)); 16 | printf("%x:%d\n",(int*)pMemShare,*(int*)pMemShare); 17 | data++; 18 | memcpy((int*)pMemShare+1,&data,sizeof(data)); 19 | printf("%x:%d\n",(int*)pMemShare,*(int*)pMemShare); 20 | printf("%x:%d\n",(int*)pMemShare+1,*((int*)pMemShare+1)); 21 | 22 | getchar(); 23 | 24 | ::UnmapViewOfFile(pMemShare); 25 | ::CloseHandle(hMap); 26 | 27 | return 0; 28 | } 29 | ``` 30 | **第2步:** 新建控制台工程,从进程代码如下 31 | ```cpp 32 | #include "stdafx.h" 33 | #include "windows.h" 34 | 35 | int _tmain(int argc, _TCHAR* argv[]) 36 | { 37 | wchar_t MemShareName[] = L"MemShareForTest"; 38 | LPVOID pMemShare; 39 | 40 | HANDLE hMap = ::OpenFileMapping(FILE_MAP_ALL_ACCESS,0,MemShareName); 41 | pMemShare = ::MapViewOfFile(hMap,FILE_MAP_ALL_ACCESS,0,0,0); 42 | printf("%x:%d\n",(int*)pMemShare,*(int*)pMemShare); 43 | printf("%x:%d\n",(int*)pMemShare+1,*((int*)pMemShare+1)); 44 | 45 | getchar(); 46 | return 0; 47 | } 48 | ``` 49 | **第3步:** 依次运行主从进程,结果如下 50 | 51 | ![](不同进程通过共享内存实现数据共享/1.png) 52 | -------------------------------------------------------------------------------- /Windows/不同进程通过共享内存实现数据共享/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Windows/不同进程通过共享内存实现数据共享/1.png -------------------------------------------------------------------------------- /Windows/多线程.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Windows/多线程.md -------------------------------------------------------------------------------- /Windows/消息机制.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Windows/消息机制.md -------------------------------------------------------------------------------- /Windows/相同程序不同进程下实现数据共享.md: -------------------------------------------------------------------------------- 1 | 格式: 2 | ```cpp 3 | #pragma comment(linker,"/section:MyData,rws") 4 | #pragma data_seg("MyData") 5 | int g_iProNum = -1; 6 | #pragma data_seg() 7 | ``` 8 | 说明: 9 | 10 | 1. 以全局变量来定义,即函体之外; 11 | 2. 必需初始化,否则编译器会把未初始化的变量放到.BSS段中; 12 | 3. /SECTION:自己的数据段名,RWS,R为允许读,W为允许写,S为允许共享 13 | 14 | **第1步:** 新建控制台程序,代码如下: 15 | ```cpp 16 | #include "stdafx.h" 17 | #include 18 | 19 | #pragma comment(linker,"/section:DataShare,rws") 20 | #pragma data_seg("DataShare") 21 | int g_ValueA = 0; // 全局变量,要赋值 22 | #pragma data_seg() 23 | 24 | int _tmain(int argc, _TCHAR* argv[]) 25 | { 26 | if (!g_ValueA) 27 | { 28 | printf("%d:%d\n",&g_ValueA,g_ValueA); 29 | g_ValueA=1; 30 | } 31 | printf("%d:%d\n",&g_ValueA,g_ValueA); 32 | system("pause"); 33 | return 0; 34 | } 35 | ``` 36 | **第2步:** 运行Debug中的exe,可以看到先后运行的两个exe数据的变化 37 | 38 | ![](相同程序不同进程下实现数据共享/1.png) ![](相同程序不同进程下实现数据共享/2.png) 39 | 40 | **注意:** 进程间共享的是相同的物理地址,不同进程对应相同物理地址的虚拟地址并不相同,进程间不能共享指针,因为指针指向的是虚拟地址对应的数据,在另一进程中该虚拟地址并不指向原来的物理地址,例如 41 | ```cpp 42 | #include "stdafx.h" 43 | #include 44 | 45 | #pragma comment(linker,"/section:DataShare,rws") 46 | #pragma data_seg("DataShare") 47 | int *g_ValueA = NULL; // 全局变量,要赋值 48 | #pragma data_seg() 49 | 50 | int _tmain(int argc, _TCHAR* argv[]) 51 | { 52 | int a = 10; 53 | if (NULL == g_ValueA) 54 | { 55 | g_ValueA = &a; 56 | printf("%d:%d\n",g_ValueA,*g_ValueA); 57 | (*g_ValueA)++; 58 | } 59 | else 60 | { 61 | printf("%d:%d\n",g_ValueA,*g_ValueA); 62 | } 63 | system("pause"); 64 | return 0; 65 | } 66 | ``` 67 | 该程序运行结果如下 68 | 69 | ![](相同程序不同进程下实现数据共享/3.png) ![](相同程序不同进程下实现数据共享/4.png) 70 | 71 | 这是由于第一进程中虚拟地址对应的物理空间a变为11,而第二进程收到的该虚拟地址对应的该进程的a还是等于10 72 | -------------------------------------------------------------------------------- /Windows/相同程序不同进程下实现数据共享/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Windows/相同程序不同进程下实现数据共享/1.png -------------------------------------------------------------------------------- /Windows/相同程序不同进程下实现数据共享/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Windows/相同程序不同进程下实现数据共享/2.png -------------------------------------------------------------------------------- /Windows/相同程序不同进程下实现数据共享/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Windows/相同程序不同进程下实现数据共享/3.png -------------------------------------------------------------------------------- /Windows/相同程序不同进程下实现数据共享/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/Windows/相同程序不同进程下实现数据共享/4.png -------------------------------------------------------------------------------- /WordPress/CentOS6系统LNMP环境搭建及WordPress安装/1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/WordPress/CentOS6系统LNMP环境搭建及WordPress安装/1.PNG -------------------------------------------------------------------------------- /WordPress/CentOS6系统LNMP环境搭建及WordPress安装/10.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/WordPress/CentOS6系统LNMP环境搭建及WordPress安装/10.PNG -------------------------------------------------------------------------------- /WordPress/CentOS6系统LNMP环境搭建及WordPress安装/11.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/WordPress/CentOS6系统LNMP环境搭建及WordPress安装/11.PNG -------------------------------------------------------------------------------- /WordPress/CentOS6系统LNMP环境搭建及WordPress安装/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/WordPress/CentOS6系统LNMP环境搭建及WordPress安装/12.png -------------------------------------------------------------------------------- /WordPress/CentOS6系统LNMP环境搭建及WordPress安装/13.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/WordPress/CentOS6系统LNMP环境搭建及WordPress安装/13.PNG -------------------------------------------------------------------------------- /WordPress/CentOS6系统LNMP环境搭建及WordPress安装/14.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/WordPress/CentOS6系统LNMP环境搭建及WordPress安装/14.PNG -------------------------------------------------------------------------------- /WordPress/CentOS6系统LNMP环境搭建及WordPress安装/15.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/WordPress/CentOS6系统LNMP环境搭建及WordPress安装/15.PNG -------------------------------------------------------------------------------- /WordPress/CentOS6系统LNMP环境搭建及WordPress安装/16.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/WordPress/CentOS6系统LNMP环境搭建及WordPress安装/16.PNG -------------------------------------------------------------------------------- /WordPress/CentOS6系统LNMP环境搭建及WordPress安装/17.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/WordPress/CentOS6系统LNMP环境搭建及WordPress安装/17.PNG -------------------------------------------------------------------------------- /WordPress/CentOS6系统LNMP环境搭建及WordPress安装/2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/WordPress/CentOS6系统LNMP环境搭建及WordPress安装/2.PNG -------------------------------------------------------------------------------- /WordPress/CentOS6系统LNMP环境搭建及WordPress安装/3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/WordPress/CentOS6系统LNMP环境搭建及WordPress安装/3.PNG -------------------------------------------------------------------------------- /WordPress/CentOS6系统LNMP环境搭建及WordPress安装/4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/WordPress/CentOS6系统LNMP环境搭建及WordPress安装/4.PNG -------------------------------------------------------------------------------- /WordPress/CentOS6系统LNMP环境搭建及WordPress安装/5.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/WordPress/CentOS6系统LNMP环境搭建及WordPress安装/5.PNG -------------------------------------------------------------------------------- /WordPress/CentOS6系统LNMP环境搭建及WordPress安装/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/WordPress/CentOS6系统LNMP环境搭建及WordPress安装/6.png -------------------------------------------------------------------------------- /WordPress/CentOS6系统LNMP环境搭建及WordPress安装/7.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/WordPress/CentOS6系统LNMP环境搭建及WordPress安装/7.PNG -------------------------------------------------------------------------------- /WordPress/CentOS6系统LNMP环境搭建及WordPress安装/8.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/WordPress/CentOS6系统LNMP环境搭建及WordPress安装/8.PNG -------------------------------------------------------------------------------- /WordPress/CentOS6系统LNMP环境搭建及WordPress安装/9.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/WordPress/CentOS6系统LNMP环境搭建及WordPress安装/9.PNG -------------------------------------------------------------------------------- /git/.gitignore设置跟踪忽略文件夹中文件.md: -------------------------------------------------------------------------------- 1 | * **常用规则** 2 | 3 | | 规则 | 作用 | 4 | | ------ |----------| 5 | | /mtk/ | 过滤整个文件夹 | 6 | | *.zip | 过滤所有.zip文件 | 7 | | /mtk/do.c | 过滤某个具体文件 | 8 | | !/mtk/one.txt | 追踪(不过滤)某个具体文件 | 9 | 10 | **注意:** 如果你创建.gitignore文件之前就push了某一文件,那么即使你在.gitignore文件中写入过滤该文件的规则,该规则也不会起作用,git仍然会对该文件进行版本管理。 11 | 12 | * **配置语法** 13 | 1. 以斜杠“/”开头表示目录; 14 | 2. 以星号“\*”通配多个字符; 15 | 3. 以问号“?”通配单个字符 16 | 4. 以方括号“[]”包含单个字符的匹配列表; 17 | 5. 以叹号“!”表示不忽略(跟踪)匹配到的文件或目录。 18 | 19 | **注意:** git 对于 .gitignore配置文件是按行从上到下进行规则匹配的 20 | 21 | **示例:** 假设想要忽略根目录下所有的名为Win7Release或release的文件夹,可在.gitignore文件中添加如下规则 22 | ``` 23 | *[Rr]elease/ 24 | ``` 25 | 若想忽略类似test/bin/目录下名为Win7Release或release的文件夹,则添加规则 26 | ``` 27 | */bin/*[Rr]elease/ 28 | ``` 29 | **注意:** 当添加的规则中包含目录层级时,该目录应为相对于根目录的完整路径,上面的规则若修改为bin/\*[Rr]elease/是无效的。若想只忽略根目录下名为Win7Release或release的文件夹,可添加规则 30 | ``` 31 | /*[Rr]elease/ 32 | ``` 33 | 34 | * **跟踪忽略文件夹中文件** 35 | 36 | 假设想要忽略所有的名为Win7Release或release的文件夹下除.txt文件之外的文件,很容易写成如下**错误形式** 37 | ``` 38 | *[Rr]elease/ 39 | !*[Rr]elease/*.txt 40 | ``` 41 | **注意:** 上面已经说过git对于.gitignore配置文件是按行从上到下进行规则匹配的,由于先执行*[Rr]elease/忽略了所有符合条件的文件夹,接下来执行!\*[Rr]elease/\*.txt时找不到名为\*[Rr]elease的文件夹,也就无法追踪这些文件夹下的txt文件了。 42 | 43 | 所以,在这里,第一步我们不应该忽略这些文件夹,而应该忽略这些文件夹下的所有文件,**正确规则如下** 44 | ``` 45 | *[Rr]elease/* 46 | !*[Rr]elease/*.txt 47 | 48 | */bin/*[Rr]elease/* 49 | !*/bin/*[Rr]elease/*.txt 50 | ``` 51 | **注意:** 在这里不能交换忽略规则与追踪规则额执行顺序,因为若先执行追踪规则(如!\*[Rr]elease/\*.txt),接下来执行的忽略规则(如\*[Rr]elease/\*)会忽略所有文件,包括txt文件! 52 | 53 | 若我们想要追踪的txt文件在\*[Rr]elease文件夹下名为files的文件夹内,上面的规则是错误的,因为执行的忽略规则的时候已经忽略了files文件夹,所以在后面直接添加追踪命令(如!\*[Rr]elease/files/\*.txt)是无效的,**正确规则如下** 54 | ``` 55 | *[Rr]elease/* 56 | !*[Rr]elease/*.txt 57 | !*[Rr]elease/files/ 58 | *[Rr]elease/files/* 59 | !*[Rr]elease/files/*.txt 60 | 61 | */bin/*[Rr]elease/* 62 | !*/bin/*[Rr]elease/*.txt 63 | !*/bin/*[Rr]elease/files/ 64 | */bin/*[Rr]elease/files/* 65 | !*/bin/*[Rr]elease/files/*.txt 66 | ``` 67 | **说明:** 68 | 1. 执行\*[Rr]elease/\*,忽略根目录下符合条件的文件夹下的所有文件 69 | 2. 执行!\*[Rr]elease/\*.txt,追踪根目录下符合条件的文件夹下的txt文件 70 | 3. 执行!\*[Rr]elease/files/,追踪根目录下符合条件的文件夹下的files文件夹 71 | 4. 执行\*[Rr]elease/files/\*,忽略files文件夹下的所有文件 72 | 5. 执行!\*[Rr]elease/files/\*.txt,追踪files文件夹下的txt文件 73 | 74 | 参考链接
75 | [Git忽略规则.gitignore梳理](http://www.cnblogs.com/kevingrace/p/5690241.html)
76 | [.gitignore exclude folder but include specific subfolder 77 | ](http://stackoverflow.com/questions/5533050/gitignore-exclude-folder-but-include-specific-subfolder) 78 | -------------------------------------------------------------------------------- /git/git与svn的五个基本区别[转].md: -------------------------------------------------------------------------------- 1 | 原文:[Git 和 SVN 之间的五个基本区别](http://blog.jobbole.com/31444/) 2 | 3 | **1. GIT是分布式的,SVN不是** 4 | 5 | 这是GIT和其它非分布式的版本控制系统,例如SVN,CVS等,最核心的区别。需要做一点声明,GIT并不是目前第一个或唯一的分布式版本控制系统。还有一些系统,例如Bitkeeper, Mercurial等,也是运行在分布式模式上的。但GIT在这方面做的更好,而且有更多强大的功能特征。 6 | 7 | GIT跟SVN一样有自己的集中式版本库或服务器。但GIT更倾向于被使用于分布式模式,也就是每个开发人员从中心版本库/服务器上chect out代码后会在自己的机器上克隆一个自己的版本库。可以这样说,如果你被困在一个不能连接网络的地方时,你仍然能够提交文件,查看历史版本记录,创建项目分支等。对一些人来说,这好像没多大用处,但当你突然遇到没有网络的环境时,这个将解决你的大麻烦。 8 | 9 | 同样,这种分布式的操作模式对于开源软件社区的开发来说也是个巨大的恩赐,你不必再像以前那样做出补丁包,通过email方式发送出去,你只需要创建一个分支,向项目团队发送一个推请求。这能让你的代码保持最新,而且不会在传输过程中丢失。GitHub就是一个这样的优秀案例。 10 | 11 | **2. GIT把内容按元数据方式存储,而SVN是按文件** 12 | 13 | 所有的资源控制系统都是把文件的元信息隐藏在一个类似`.svn`,`.cvs`等的文件夹里。如果你把`.git`目录的体积大小跟`.svn`比较,你会发现它们差距很大。因为`.git`目录是处于你的机器上的一个克隆版的版本库,它拥有中心版本库上所有的东西,例如标签,分支,版本记录等。 14 | 15 | **3. GIT分支和SVN的分支不同** 16 | 17 | 分支在SVN中一点不特别,就是版本库中的另外的一个目录。如果你想知道是否合并了一个分支,你需要手工运行像这样的命令`svn propget svn:mergeinfo`,来确认代码是否被合并。所以,经常会发生有些分支被遗漏的情况。 18 | 19 | 然而,处理GIT的分支却是相当的简单和有趣。你可以从同一个工作目录下快速的在几个分支间切换。你很容易发现未被合并的分支,你能简单而快捷的合并这些文件。 20 | 21 | **4. GIT没有一个全局的版本号,而SVN有** 22 | 23 | 目前为止这是跟SVN相比GIT缺少的最大的一个特征。你也知道,SVN的版本号实际是任何一个相应时间的源代码快照。我认为它是从CVS进化到SVN的最大的一个突破。我们可以使用GIT的SHA-1来唯一的标识一个代码快照。这个并不能完全的代替SVN里容易阅读的数字版本号。但,用途应该是相同的。 24 | 25 | **5. GIT的内容完整性要优于SVN** 26 | 27 | GIT的内容存储使用的是SHA-1哈希算法。这能确保代码内容的完整性,确保在遇到磁盘故障和网络问题时降低对版本库的破坏。这里有一个很好的关于GIT内容完整性的讨论--[Git File Integrity 28 | ](http://stackoverflow.com/questions/964331/git-file-integrity)。 29 | -------------------------------------------------------------------------------- /git/git常用命令/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/git/git常用命令/1.png -------------------------------------------------------------------------------- /git/git常用命令/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/git/git常用命令/2.png -------------------------------------------------------------------------------- /git/从github下载WDK项目的坑.md: -------------------------------------------------------------------------------- 1 | 今天将自己的wdk项目上传到github,结果将项目下载到本地后却编译不通过,错误提示如下。 2 | 3 | ![这里写图片描述](从github下载WDK项目的坑/1.png) 4 | 5 | 可是从github上克隆项目到本地却编译成功,最后排错发现问题出在inf文件上,对比两个inf文件发现,直接下载的项目里的inf文件的换行符没有了!!!最坑的是下载项目的inf文件用notepad或者vs打开是换行的。。。 6 | 7 | 下载的项目: 8 | 9 | ![下载的项目](从github下载WDK项目的坑/2.png) 10 | 11 | 克隆的项目: 12 | 13 | ![克隆的项目](从github下载WDK项目的坑/3.png) 14 | 15 | 到这里,下载项目编译失败的原因很明显了,就是换行符的问题,百度找到一篇文章[《GitHub 第一坑:换行符自动转换》](http://blog.jobbole.com/46200/)。 16 | 17 | 原来,本地项目提交到github时会自动将Windows系统的换行符替换为Unix的换行符,从github上克隆项目到本地时会做相反的转换。而如果从github上直接下载项目的话,里面文件的换行符是Unix的,在Windows下以文本方式打开不能被识别。所以,这里只要关闭git的换行符自动转换功能就好了。 18 | 19 | 打开配置文件(github for windows的.gitconfig文件或者SourceTree的config文件),在[core]区段找到autocrlf,将它的值改为 false,如果在[core]区段找不到autocrlf,则在[core]区段添加 20 | ``` 21 | [core] 22 | autocrlf = false 23 | ``` 24 | 重新上传项目,问题解决! 25 | -------------------------------------------------------------------------------- /git/从github下载WDK项目的坑/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/git/从github下载WDK项目的坑/1.png -------------------------------------------------------------------------------- /git/从github下载WDK项目的坑/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/git/从github下载WDK项目的坑/2.png -------------------------------------------------------------------------------- /git/从github下载WDK项目的坑/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/git/从github下载WDK项目的坑/3.png -------------------------------------------------------------------------------- /其他/使用youtube-dl下载YouTube视频.md: -------------------------------------------------------------------------------- 1 | 本文假定使用Win10操作系统,已安装好Python和Shadowsocks,并具备翻墙能力,另外,该方法在Win7下存在问题,视频下载不下来([https://github.com/rg3/youtube-dl/issues/1901](https://github.com/rg3/youtube-dl/issues/1901)),如果有人知道解决方法,希望可以告知 2 | 3 | **第1步:** WIN+R输入cmd,确定,输入pip install youtube-dl,回车,安装成功 4 | 5 | ![这里写图片描述](使用youtube-dl下载YouTube视频/1.png) 6 | 7 | 也可输入youtube-dl验证,不过由于youtube上1080p及以上分辨率的视频和音频是分离的,所以还需要安装ffmpeg来将视频与音频进行合并,当然也可以用格式工厂,但是使用起来没有ffmpeg方便 8 | 9 | ![这里写图片描述](使用youtube-dl下载YouTube视频/2.png) 10 | 11 | **第2步:** 进入[ffmpeg下载页面](http://www.ffmpeg.org/download.html),点击Windows Builds 12 | 13 | ![这里写图片描述](使用youtube-dl下载YouTube视频/3.png) 14 | 15 | 由于我的电脑是64位,所以这里选择64-bit,Version选择3.2.4,Linking选择Static,点击Download FFmpeg下载 16 | 17 | ![这里写图片描述](使用youtube-dl下载YouTube视频/4.png) 18 | 19 | **第3步:** 下载完成后,将压缩包解压到D盘根目录 20 | 21 | ![这里写图片描述](使用youtube-dl下载YouTube视频/5.png) 22 | 23 | 添加环境变量,右击我的电脑,选择属性-高级系统设置-高级-环境变量-系统变量-path,单击编辑,在最后输入;D:\ffmpeg-3.2.4-win64-static\bin,确定,重启系统,打开cmd,输入ffmpeg -version,安装成功如下图所示 24 | 25 | ![这里写图片描述](使用youtube-dl下载YouTube视频/6.png) 26 | 27 | **第4步:** 打开Shadowsocks,启用系统代理,系统代理模式设置为全局模式,打开cmd,输入youtube-dl -F youtube视频链接,回车,会显示该视频可以下载的视频格式与分辨率,如 28 | 29 | ![这里写图片描述](使用youtube-dl下载YouTube视频/7.png) 30 | 31 | 可以看到,1080p的视频(如format code=137)显示为video only,即只有图像,没有声音,所以我们还要下载音频(fomat code=140),cmd输入d:,将当前目录切换到D盘根目录,输入youtube-dll -f 137+140 youtube视频链接,回车,下载的视频和音频会自动被ffmpeg合并保存到D盘根目录下,如果下载720p的视频(format code=22),可以看到并不需要单独下载音频文件,输入youtube-dll -f 22 youtube视频链接即可 32 | 33 | ![这里写图片描述](使用youtube-dl下载YouTube视频/8.png) 34 | 35 | **第5步:** 每次下载都要这样重复操作比较麻烦,我们可以写成bat文件,新建YouTuBe.bat 36 | ``` 37 | @echo off 38 | :start 39 | set /p dir=请输入保存路径: 40 | set dir=%dir:/=\% 41 | pushd %dir% 42 | if /i not %dir%==%cd% goto :start 43 | echo 保存路径:%cd% 44 | :download 45 | set /p input=请输入视频链接: 46 | set input=%input:&=^^^&% 47 | youtube-dl -F %input% 48 | if errorlevel 1 goto :download 49 | set /p code=请输入视频格式编号: 50 | youtube-dl -f %code% %input% 51 | goto :download 52 | ``` 53 | 双击运行,按照提示输入保存路径、视频链接和format code,即可下载 54 | 55 | ![这里写图片描述](使用youtube-dl下载YouTube视频/9.png) 56 | -------------------------------------------------------------------------------- /其他/使用youtube-dl下载YouTube视频/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/其他/使用youtube-dl下载YouTube视频/1.png -------------------------------------------------------------------------------- /其他/使用youtube-dl下载YouTube视频/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/其他/使用youtube-dl下载YouTube视频/2.png -------------------------------------------------------------------------------- /其他/使用youtube-dl下载YouTube视频/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/其他/使用youtube-dl下载YouTube视频/3.png -------------------------------------------------------------------------------- /其他/使用youtube-dl下载YouTube视频/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/其他/使用youtube-dl下载YouTube视频/4.png -------------------------------------------------------------------------------- /其他/使用youtube-dl下载YouTube视频/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/其他/使用youtube-dl下载YouTube视频/5.png -------------------------------------------------------------------------------- /其他/使用youtube-dl下载YouTube视频/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/其他/使用youtube-dl下载YouTube视频/6.png -------------------------------------------------------------------------------- /其他/使用youtube-dl下载YouTube视频/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/其他/使用youtube-dl下载YouTube视频/7.png -------------------------------------------------------------------------------- /其他/使用youtube-dl下载YouTube视频/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/其他/使用youtube-dl下载YouTube视频/8.png -------------------------------------------------------------------------------- /其他/使用youtube-dl下载YouTube视频/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/其他/使用youtube-dl下载YouTube视频/9.png -------------------------------------------------------------------------------- /其他/研究生阶段的自我总结.md: -------------------------------------------------------------------------------- 1 | 眼看着就要研三了,也在准备找工作了,感觉在自己读研阶段学到了很多,在这里进行一下简单的总结吧。 2 | 3 | 我接触的第一门编程语言是C++,它陪伴了我整个大学时期,由于专业原因,我在大学期间所做的编程工作更偏向于底层开发,大四获得保研资格之后进入实验室,接手师兄在软件方面的工作,那时我仿佛打开了新世界的大门,以前对于尝试新领域,我总是怀着一种畏惧心理,害怕自己花了大力气,最后却一无所得,所以说环境的逼迫有时真的非常重要,当你勇敢的迈出第一步,后面就会越走越快。 4 | 5 | 那段时间我真的是学的如痴如醉,用朋友的话说,就是天天中午看我在实验室啃面包,MFC、驱动、DSP,这些都是我从未接触过的。但随着自己参与项目遇到问题,我越来越无法容忍这种架构的落后与不合理性。 6 | 7 | 1)**软件方面:** MFC的开发难度大,界面美化较复杂,且部分控件无法满足项目需求,经过调研,决定采用WinForm作为替代,不过现在随着阅历的增加,当我尝试app开发的时候,突然眼前一亮,app、WPF、DirectUI的界面开发使用的是类似标记语言,很明显,界面开发与软件逻辑的更深层次的分离已经成为了未来发展的趋势,而且这样也确实更加合理,因为程序员可以把更多的精力放到软件本身,既然如此,我为什么不使用HTML来开发界面(由于做过一些爬虫,所以略懂前端的皮毛),想到此处,我非常激动,立马开始调研,原来早有大神想到这个,不过这样做需要嵌入浏览器内核,导致软件体积庞大,而且运行会相对慢一些,经过深思熟虑,决定以WinForm为载体,利用其浏览器控件来回避浏览器内核问题,减小软件体积,使用HTML+CSS3搭建软件界面,使用C#与C++来执行软件逻辑与具体操作,由于文件都在本地,软件界面的加载就少去了网络延迟的影响,肯定还是会比原生慢,但是否会影响到用户体验只有试过才知道。不过现在由于时间紧张,这一部分的学习进度缓慢,只能每天晚上睡觉之前抽空学习,但我也不会放弃,慢慢积累吧。 8 | 9 | 2)**驱动方面:** 之前驱动开发使用的是WinDriver,这个开发工具确实功能强大,将驱动相关操作的具体实现加以封装,并提供API给开发者调用,使得开发者可以在不深入了解底层驱动相关知识的前提下实现驱动的快速开发。但相应的也限制了开发者的自由发挥,在项目开发过程中,有时遇到问题,我明明知道问题的原因,解决思路也有,但我通过WinDriver的接口函数就是无法实现,只能选择回避,这是我无法容忍的,我会觉得很不舒服,因为我喜欢编程的高效、自由、优雅,我也在自己所知的范围内这么要求自己,于是我选择了WDK,也争取到了导师的支持,让我一段时间专心研究驱动开发,终于在新的项目中使用WDK编写的驱动替代了WinDriver。 10 | 11 | 3)**理论计算方面:** 项目组之前采用的是通过C#或C++调用Matlab来进行相关理论计算,在数据量很大的情况下运行速度很慢,我现在正在尝试使用GPU并行程序来计算相关理论结果,已经完成了处理的理论代码移植,计算速度的提升十分显著,保证了大数据处理的实时性要求,接下来我要进一步优化算法结构,在算法的通用性与高效性之间取得平衡。 12 | 13 | 以上算是我在读研期间为项目组所做的贡献,当然在这过程中,我也学到了很多。 14 | 15 | 1)**C++:** C++是我使用时间最长的一门语言了,我很喜欢它的高效、简洁和更接近于底层。因为这些,在我编程的过程中就需要不停的问自己为什么这么做,比如,dll与lib有什么区别,各有什么优缺点,。我很讨厌知道这么做,但不知道为什么这么做的感觉 16 | 17 | 2)C#, 18 | 19 | 3)CUDA, 20 | 21 | 4)WDK 22 | 23 | 4)Windows, 24 | 25 | 在个人兴趣方面,学得挺杂的。 26 | 27 | 1)Java,当初学了C#之后顺势就学了Java,因为两者太像了。但由于一直苦于找不到练手的机会,感觉十分痛苦,因为Java在我看来更适合用来做系统级的开发,让我开发界面程序,我肯定选择C#,每个语言都有自己的优势,最后决定试试Android应用开发,我知道Android开发方面的程序员已经接近饱和了,所以只是因为真的手痒,想把Java用起来而已,没想到学习编写第一个app的时候,灵感来了,就是上面说的,结果Android应用开发这一块就暂时放下了,但Java也没有放弃,现在准备找工作,正好在用Java编写算法还有做一些机试题目。 28 | 29 | 2)Python,学Python是因为我发现可以用它很轻松的去做一些好玩的事情,开发微信公众号啦、写一些爬虫啦,Python基础方面的资料网上很多,麻烦的是有些库的使用,网上讨论的太少,墙外可能稍微多些,但有时也不能解决问题,必须要去翻说明文档,最怕的就是有些文档在关键部分也只是寥寥几句,真的很头疼。我也尝试着用Python来替代Matlab编写理论代码,但Python对通信相关算法的支持还是太差,我现在也没有精力去做这件事,要准备找工作啊。对了,还有些我准备要做的在这里记下来,我一定要在毕业前编写一个爬虫来爬取学校图书馆的电子书资源,好多电子书资源网上都找不到的,毕业就没机会了啊,还有就是等闲下来用Python做一下数据库的高并发、大数据的搜索与存储,并尝试优化,最近忙晕了,都忘了。 30 | 31 | 3)MySQL, 32 | 33 | 4)Linux, 34 | 35 | 5)HTML+CSS3+JavaScript, 36 | 37 | 5)Unity, 38 | -------------------------------------------------------------------------------- /微信公众平台开发/使用python-aiohttp爬取今日头条/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/微信公众平台开发/使用python-aiohttp爬取今日头条/1.png -------------------------------------------------------------------------------- /微信公众平台开发/使用python-aiohttp爬取今日头条/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/微信公众平台开发/使用python-aiohttp爬取今日头条/2.png -------------------------------------------------------------------------------- /微信公众平台开发/通过Apache反向代理实现微信服务器80端口访问.md: -------------------------------------------------------------------------------- 1 | 由于微信公众平台需要的80端口已经被apache占用,所以需要基于apache配置反向代理。 2 | 3 | 代理服务器就相当于一个中介,有正向代理与反向代理两种。在正向代理中,客户端通过代理服务器访问目标服务器,代理服务器扮演着客户端的角色,真正的客户端对目标服务器不可见,比如科学上网。在反向代理中,代理服务器扮演着目标服务器的角色,例如,当客户端通过某个域名获取资源时,这些资源可能并不是从该域名绑定的服务器获取,该服务器也许只是作为代理服务器将收到的客户端请求转发给特定服务器。 4 | 5 | 本人在阿里云ECS上搭建微信公众平台服务器,由于80端口已经被apache占用,所以需要配置apache作为代理服务器接收来自微信服务器的请求,并将该请求转发给微信公众平台服务器(使用6670端口)。具体步骤如下: 6 | 7 | **第1步:** 创建sites-available与sites-enabled目录,sites-available目录将会存放所有的虚拟主机文件,而sites-enabled目录将会存放我们想对外提供服务的主机的符号链接 8 | ```shell 9 | mkdir /usr/local/apache/sites-available 10 | mkdir /usr/local/apache/sites-enabled 11 | ``` 12 | **第2步:** 编辑apache的配置文件 13 | ```shell 14 | vi /usr/local/apache/conf/httpd.conf 15 | ``` 16 | 找到以下两条,把#号去掉 17 | ``` 18 | #LoadModule proxy_module modules/mod_proxy.so 19 | #LoadModule proxy_http_module modules/mod_proxy_http.so 20 | ``` 21 | 在文件末尾添加一行用以声明额外配置文件所在的可选目录 22 | ``` 23 | IncludeOptional sites-enabled/*.conf 24 | ``` 25 | **第3步:** 在sites-available目录下创建文件 26 | ```shell 27 | vi /usr/local/apache/sites-available/web.conf 28 | ``` 29 | 并添加如下内容,当微信服务器访问ServerName的80端口时,将会指向6670端口 30 | ```xml 31 | 32 | ServerName 此处填写你在微信公众平台上绑定的域名或IP 33 | ServerAlias 此处填写你在微信公众平台上绑定的域名或IP 34 | ProxyPass / http://127.0.0.1:6670/ 35 | ProxyPassReverse / http://127.0.0.1:6670/ 36 | 37 | ``` 38 | **第4步:** 在sites-enabled目录下创建符号链接,**注意:此处必须使用完整路径** 39 | ```shell 40 | ln -s /usr/local/apache/sites-available/web.conf /usr/local/apache/sites-enabled/web.conf 41 | ``` 42 | **第5步:** 重启apache 43 | ```shell 44 | service httpd restart 45 | ``` 46 | #### 参考链接 47 | [CentOS 7 Apache 多端口部署 Web Apps 指南](http://www.jianshu.com/p/b34c78bf9bf0)
48 | [Apache启动报错:Invalid command 'ProxyPass', perhaps misspelled or defined by a module not inclu ded in t](http://blog.csdn.net/zhouyingge1104/article/details/44459655) 49 | -------------------------------------------------------------------------------- /数据库/MySQL高并发.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/数据库/MySQL高并发.md -------------------------------------------------------------------------------- /数据结构与算法/字典序算法[转].md: -------------------------------------------------------------------------------- 1 | 原文:[字典序算法](http://www.cnblogs.com/darklights/p/5285598.html) 2 | 3 | 我们先看一个例子。 4 | 5 | 示例:`1 2 3`的全排列如下: 6 | ```cpp 7 | 1 2 3 | 1 3 2 | 2 1 3 | 2 3 1 | 3 1 2 | 3 2 1 8 | ``` 9 | 我们这里是通过字典序法找出来的。 10 | 11 | 那么什么是字典序法呢? 12 | 13 | 从上面的全排列也可以看出来了,从左往右依次增大,对这就是字典序法。可是如何用算法来实现字典序法全排列呢? 14 | 15 | 我们再来看一段文字描述:(用字典序法找`124653`的下一个排列) 16 | 17 | * 如果当前排列是`124653`,找它的下一个排列的方法是,从这个序列中从右至左找第一个左邻小于右邻的数 18 | * 如果找不到,则所有排列求解完成,如果找得到则说明排列未完成 19 | * 本例中将找到`46`,计`4`所在的位置为`i`,找到后不能直接将`46`位置互换,而又要从右到左到第一个比`4`大的数 20 | * 本例找到的数是`5`,其位置计为`j`,将`i`与`j`所在元素交换`125643` 21 | * 然后将`i+1`至最后一个元素从小到大排序得到`125346`,这就是`124653`的下一个排列 22 | 23 | 下图是用字典序法找`1 2 3`的全排列(全过程): 24 | 25 | ![](字典序算法[转]/1.png) 26 | 27 | 代码实现(C语言): 28 | ```c 29 | #include 30 | //交换list[a],list[b] 31 | void Swap(int list[], int a, int b) { 32 | int temp = 0; 33 | temp = list[a]; 34 | list[a] = list[b]; 35 | list[b] = temp; 36 | return; 37 | } 38 | //将list区间[a,n]之间的数据由小到大排序 39 | void Sort(int list[], int a, int n) { 40 | int temp = 0; 41 | for (int i = 1; i < n-a; ++i) 42 | for (int j = a+1; j < n-1; ++j) 43 | if (list[j] > list[j+1]) { 44 | temp = list[j]; 45 | list[j] = list[j+1]; 46 | list[j+1] = temp; 47 | } 48 | return; 49 | } 50 | //全排列 51 | void Prim(int list[], int n) { 52 | int num = 1, a = 0, b = 0; 53 | for (int i = n; i > 0; --i) //计算有多少种情况,就循环多少次 54 | num *= i; 55 | while (num--) { 56 | for (int i = 0; i < n; ++i) //打印情况 57 | printf("%d ",list[i]); 58 | printf("\n"); 59 | 60 | for (int i = n-1; i > 0; --i) //从右往左,找出第一个左边小于右边的数,设为list[a] 61 | if (list[i-1] < list[i]) { 62 | a = i-1; 63 | break; 64 | } 65 | for (int j = n-1; j > a; --j) //从右往左,找出第一个大于list[a]的数,设为list[b] 66 | if (list[j] > list[a]) { 67 | b = j; 68 | break; 69 | } 70 | Swap(list, a, b); //交换list[a],list[b] 71 | Sort(list, a, n); //将list[a]后面的数据,由小往大排列 72 | } 73 | return; 74 | } 75 | //主函数 76 | int main() { 77 | int list[] = {1,2,3,4}; 78 | Prim(list,3); 79 | return 0; 80 | } 81 | ``` 82 | -------------------------------------------------------------------------------- /数据结构与算法/字典序算法[转]/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/数据结构与算法/字典序算法[转]/1.png -------------------------------------------------------------------------------- /数据结构与算法/散列(hash)表算法[转]/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/数据结构与算法/散列(hash)表算法[转]/1.jpg -------------------------------------------------------------------------------- /数据结构与算法/散列(hash)表算法[转]/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/数据结构与算法/散列(hash)表算法[转]/2.jpg -------------------------------------------------------------------------------- /数据结构与算法/最长回文子串:Manacher算法[转].md: -------------------------------------------------------------------------------- 1 | 原文:[Manacher 算法](http://www.61mon.com/index.php/archives/181/) 2 | 3 | ### 背景 4 | 5 | 给定一个字符串,求出其最长回文子串。例如: 6 | ```cpp 7 | s="abcd"; //最长回文长度为 1 8 | s="ababa"; //最长回文长度为 5 9 | s="abccb"; //最长回文长度为 4,即 bccb 10 | ``` 11 | 以上问题的传统思路大概是,遍历每一个字符,以该字符为中点向两边查找。其时间复杂度为$O(n^2)$,很不高效。而`Manacher`算法可以把时间复杂度提升到$O(n)$。 12 | 13 | ### 算法过程分析 14 | 15 | 由于回文分为偶回文(比如`bccb`)和奇回文(比如`bcacb`),而在处理奇偶问题上会比较繁琐,所以这里我们使用一个技巧,具体做法是,在字符串首尾,及字符间各插入一个字符(前提这个字符未出现在串里)。 16 | 17 | 举个例子:`s="abbahopxpo"`,转换为`s_new="#a#b#b#a#h#o#p#x#p#o#"`。如此,`s`里起初有一个偶回文`abba`和一个奇回文`opxpo`,被转换为`#a#b#b#a#`和`#o#p#x#p#o#`,长度都转换成了奇数。 18 | 19 | 定义一个辅助数组`int p[]`,其中`p[i]`表示以`i`为中心的最长回文的半径,例如: 20 | | `i` | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | | :--: | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | 22 | | `s_new[i]` | # | a | # | b | # | b | # | a | # | h | # | o | # | p | # | x | # | p | # | 23 | | `p[i]` | 1 | 2 | 1 | 2 | 5 | 2 | 1 | 2 | 1 | 2 | 1 | 2 | 1 | 2 | 1 | 4 | 1 | 2 | 1 | 24 | 可以看出,**`p[i] - 1`正好是原字符串中最长回文串的长度。** 25 | 26 | 接下来的重点就是求解`p`数组,如下图: 27 | 28 | ![](最长回文子串:Manacher算法[转]/1.png) 29 | 30 | 设置两个变量`mx,id`。`mx`代表以`id`为中心的最长回文的右边界,也就是`mx = id + p[id]`。假设我们现在求`p[i]`,也就是以`i`为中心的最长回文半径,如果`i < mx`,则`p[i]`的值可基于以下三种情况得出: 31 | 32 | #### 1. `j`的回文串有一部分在`id`的之外 33 | 34 | ![](最长回文子串:Manacher算法[转]/2.png) 35 | 36 | 上图中,黑线为`id`的回文,`i,j`关于`id`对称,红线为`j`的回文。此时`p[i] = mx - i`,即紫线。那么`p[i]`还可以更大么?答案是不可能!见下图: 37 | 38 | ![](最长回文子串:Manacher算法[转]/3.png) 39 | 40 | 假设右侧新增的紫色部分是`p[i]`可以增加的部分,那么根据回文的性质,`a == d` ,也就是说`id`的回文不仅仅是黑线,而是黑线+两条紫线,矛盾,所以假设不成立,故`p[i] = mx - i`,不可以再增加一分。 41 | 42 | #### 2. `j`回文串全部在`id`的内部 43 | 44 | ![](最长回文子串:Manacher算法[转]/4.png) 45 | 46 | 此时`p[i] = p[j]`,那么`p[i]`还可以更大么?答案亦是不可能!见下图: 47 | 48 | ![](最长回文子串:Manacher算法[转]/5.png) 49 | 50 | 假设右侧新增的红色部分是`p[i]`可以增加的部分,那么根据回文的性质,`a == b`,也就是说`j`的回文应该再加上`a,b`,矛盾,所以假设不成立,故`p[i] = p[j]`,也不可以再增加一分。 51 | 52 | #### 3. `j`回文串左端正好与`id`的回文串左端重合 53 | 54 | ![](最长回文子串:Manacher算法[转]/6.png) 55 | 56 | 根据代码,此时`p[i] = p[j]`或`p[i] = mx - i`,并且`p[i]`还可以继续增加。 57 | 58 | ### 代码实现 59 | ```cpp 60 | #include 61 | #include 62 | using namespace std; 63 | 64 | // Manacher 算法 65 | // 功能:给定一个字符串,求它的最长回文子串长度 66 | int manacher(string str) { 67 | int max_loc = str.size() * 2; 68 | 69 | // 初始化字符串 70 | string s_new(max_loc + 1, '#'); 71 | for (int ii = str.size() - 1; ii >= 0; --ii) { 72 | s_new[2 * ii + 1] = str[ii]; 73 | } 74 | 75 | // 遍历字符串 76 | vector p(max_loc + 1, 1); 77 | int max_len = 1; 78 | for (int ii = 1, jj, id = 0, mx = 1; ii < s_new.size(); ++ii) { 79 | if (ii - id >= mx) { 80 | for (; ii + p[ii] < s_new.size() && ii - p[ii] >= 0; ++p[ii]) { 81 | if (s_new[ii + p[ii]] != s_new[ii - p[ii]]) 82 | break; 83 | } 84 | id = ii; 85 | mx = p[ii]; 86 | max_len = max(mx, max_len); 87 | } else { 88 | jj = id - (ii - id); 89 | if (jj - p[jj] == id - mx) { 90 | for (p[ii] = id + mx - ii; ii + p[ii] < s_new.size() && ii - p[ii] >= 0; ++p[ii]) { 91 | if (s_new[ii + p[ii]] != s_new[ii - p[ii]]) 92 | break; 93 | } 94 | id = ii; 95 | mx = p[ii]; 96 | max_len = max(mx, max_len); 97 | } else if (jj - p[jj] < id - mx) { 98 | p[ii] = id + mx - ii; 99 | } else { 100 | p[ii] = p[jj]; 101 | } 102 | } 103 | } 104 | return max_len - 1; 105 | } 106 | ``` 107 | -------------------------------------------------------------------------------- /数据结构与算法/最长回文子串:Manacher算法[转]/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/数据结构与算法/最长回文子串:Manacher算法[转]/1.png -------------------------------------------------------------------------------- /数据结构与算法/最长回文子串:Manacher算法[转]/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/数据结构与算法/最长回文子串:Manacher算法[转]/2.png -------------------------------------------------------------------------------- /数据结构与算法/最长回文子串:Manacher算法[转]/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/数据结构与算法/最长回文子串:Manacher算法[转]/3.png -------------------------------------------------------------------------------- /数据结构与算法/最长回文子串:Manacher算法[转]/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/数据结构与算法/最长回文子串:Manacher算法[转]/4.png -------------------------------------------------------------------------------- /数据结构与算法/最长回文子串:Manacher算法[转]/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/数据结构与算法/最长回文子串:Manacher算法[转]/5.png -------------------------------------------------------------------------------- /数据结构与算法/最长回文子串:Manacher算法[转]/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/数据结构与算法/最长回文子串:Manacher算法[转]/6.png -------------------------------------------------------------------------------- /数据结构与算法/海量数据处理算法:Bloom Filter[转]/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/数据结构与算法/海量数据处理算法:Bloom Filter[转]/1.jpg -------------------------------------------------------------------------------- /数据结构与算法/海量数据处理算法:Bloom Filter[转]/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/数据结构与算法/海量数据处理算法:Bloom Filter[转]/2.jpg -------------------------------------------------------------------------------- /数据结构与算法/海量数据处理算法:Bloom Filter[转]/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/数据结构与算法/海量数据处理算法:Bloom Filter[转]/3.jpg -------------------------------------------------------------------------------- /数据结构与算法/海量数据处理算法:Bloom Filter[转]/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/数据结构与算法/海量数据处理算法:Bloom Filter[转]/4.png -------------------------------------------------------------------------------- /数据结构与算法/海量数据处理算法:Bloom Filter[转]/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/数据结构与算法/海量数据处理算法:Bloom Filter[转]/5.png -------------------------------------------------------------------------------- /数据结构与算法/海量数据处理算法:Bloom Filter[转]/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/数据结构与算法/海量数据处理算法:Bloom Filter[转]/6.png -------------------------------------------------------------------------------- /数据结构与算法/海量数据处理算法:Bloom Filter[转]/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/数据结构与算法/海量数据处理算法:Bloom Filter[转]/7.png -------------------------------------------------------------------------------- /数据结构与算法/红黑树.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/数据结构与算法/红黑树.md -------------------------------------------------------------------------------- /读书笔记/《C++面向对象程序设计-基于Visual C++ 2010》读书笔记/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《C++面向对象程序设计-基于Visual C++ 2010》读书笔记/1.png -------------------------------------------------------------------------------- /读书笔记/《C++面向对象程序设计-基于Visual C++ 2010》读书笔记/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《C++面向对象程序设计-基于Visual C++ 2010》读书笔记/2.png -------------------------------------------------------------------------------- /读书笔记/《C++面向对象程序设计-基于Visual C++ 2010》读书笔记/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《C++面向对象程序设计-基于Visual C++ 2010》读书笔记/3.png -------------------------------------------------------------------------------- /读书笔记/《C++面向对象程序设计-基于Visual C++ 2010》读书笔记/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《C++面向对象程序设计-基于Visual C++ 2010》读书笔记/4.png -------------------------------------------------------------------------------- /读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/1. 线程网格、线程块以及线程/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/1. 线程网格、线程块以及线程/1.png -------------------------------------------------------------------------------- /读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/1. 线程网格、线程块以及线程/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/1. 线程网格、线程块以及线程/2.png -------------------------------------------------------------------------------- /读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/1. 线程网格、线程块以及线程/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/1. 线程网格、线程块以及线程/3.png -------------------------------------------------------------------------------- /读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/1. 线程网格、线程块以及线程/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/1. 线程网格、线程块以及线程/4.png -------------------------------------------------------------------------------- /读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/1. 线程网格、线程块以及线程/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/1. 线程网格、线程块以及线程/5.png -------------------------------------------------------------------------------- /读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/1. 线程网格、线程块以及线程/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/1. 线程网格、线程块以及线程/6.png -------------------------------------------------------------------------------- /读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/1. 线程网格、线程块以及线程/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/1. 线程网格、线程块以及线程/7.png -------------------------------------------------------------------------------- /读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/2. CUDA内存处理/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/2. CUDA内存处理/1.png -------------------------------------------------------------------------------- /读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/2. CUDA内存处理/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/2. CUDA内存处理/2.png -------------------------------------------------------------------------------- /读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/2. CUDA内存处理/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/2. CUDA内存处理/3.png -------------------------------------------------------------------------------- /读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/2. CUDA内存处理/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《CUDA并行程序设计-GPU编程指南》读书笔记/2. CUDA内存处理/4.png -------------------------------------------------------------------------------- /读书笔记/《C陷阱与缺陷》读书笔记.md: -------------------------------------------------------------------------------- 1 | 如果一个整型常数的第一个字符是数字0,那么该常量将被视作八进制数。如0195相当于十进制数141 2 | 3 | --- 4 | 5 | C语言中,else始终与同一对括号内最近的未匹配的if结合。 6 | 7 | --- 8 | 9 | Switch语句中的case语句若在结尾处无break,程序将会继续执行下一条case语句 10 | 11 | --- 12 | 13 | 非数组的指针 14 | ```cpp 15 | #include 16 | char *r,*s = "hhh",*t = "sigalhu"; 17 | r = (char*)malloc(strlen(s) + strlen(t) + 1); 18 | if(!r) { 19 | exit(1); 20 | } 21 | strcpy(r, s); 22 | strcat(r, t); 23 | free(r); 24 | ``` 25 | 26 | --- 27 | 28 | extern修饰变量的声明。举例来说,**如果文件a.c需要引用b.c中变量int v,就可以在a.c中声明extern int v,然后就可以引用变量v。这里需要注意的是,被引用的变量v的链接属性必须是外链接(external)的,也就是说a.c要引用到v,不只是取决于在a.c中声明extern int v,还取决于变量v本身是能够被引用到的。** 这涉及到c语言的另外一个话题--变量的作用域。能够被其他模块以extern修饰符引用到的变量通常是全局变量。还有很重要的一点是,extern int v可以放在a.c中的任何地方,比如你可以在a.c中的函数fun定义的开头处声明extern int v,然后就可以引用到变量v了,只不过这样只能在函数fun作用域中引用v罢了,这还是变量作用域的问题。对于这一点来说,很多人使用的时候都心存顾虑。好像extern声明只能用于文件作用域似的。 29 | 30 | --- 31 | 32 | **static可以把变量和函数的作用域限制在一个源文件中,避免命名冲突。** 33 | -------------------------------------------------------------------------------- /读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/1.png -------------------------------------------------------------------------------- /读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/10.png -------------------------------------------------------------------------------- /读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/11.png -------------------------------------------------------------------------------- /读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/12.png -------------------------------------------------------------------------------- /读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/13.png -------------------------------------------------------------------------------- /读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/14.png -------------------------------------------------------------------------------- /读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/15.png -------------------------------------------------------------------------------- /读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/2.png -------------------------------------------------------------------------------- /读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/3.png -------------------------------------------------------------------------------- /读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/4.png -------------------------------------------------------------------------------- /读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/5.png -------------------------------------------------------------------------------- /读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/6.png -------------------------------------------------------------------------------- /读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/7.png -------------------------------------------------------------------------------- /读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/8.png -------------------------------------------------------------------------------- /读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《Maven实战》读书笔记/1. 使用intellij idea搭建并配置maven多模块项目/9.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/1. 设计原则.md: -------------------------------------------------------------------------------- 1 | #### 单一职责原则 2 | 3 | 单一职责原则要求在设计类、接口或方法时要保证功能的单一。例如不要将描述用户信息与行为的方法放到一个类或者接口中,而应该将按职责进行划分。其中,将用户的信息抽取为`BO`(`Business Object`,业务对象);将用户的行为抽取为`Biz`(`Business Logic`,业务逻辑)。但在这里最好还将`BO`与`Biz`抽象为接口,然后在用一个类去实现这些单一职责的接口,而不应该针对每个接口都设计一个相应的职责单一的类,因为这样会造成类之间的耦合过重与类数量的增加,增加设计的复杂性。 4 | 5 | 因此,类的设计尽量做到只有一个原因引起变化就可以了。同时,方法也应该做到职责单一。例如,有一个方法要实现修改用户信息,方法内部需要实现修改用户密码,此时应该将修改用户密码相关实现抽取为另一个方法,并在修改用户信息的方法中进行调用。比较典型的应用就是`set/get`方法,对每个字段的操作都应该实现相应的函数,并在函数命名上加以体现,如`void setName(String name)`,也不要想着实现一个通用的方法去根据输入参数的不同在方法内部进行判断,如`void set(Object obj, Menu options)`,不要让别人猜测这个方法可能是用来实现什么逻辑。 6 | 7 | #### 里式替换原则 8 | 9 | 里式替换原则要求只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常。该原则有几点需要注意: 10 | 11 | * 如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,此时子类对应方法已经不能完成父类方法要求实现的业务,则建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承。 12 | * 子类可以有自己的个性,但应该尽量避免,因为这要会导致耦合关系更加复杂。 13 | * 覆盖或实现父类的方法时输入参数可以被放大,但不能被缩小。若子类在实现父类方法时,将输入参数缩小,即子类重载父类方法。此时按照里式替换原则将相关业务中的父类替换为子类,我们期望被调用的还是父类的方法,因为我们并没有覆写父类方法,但很可能此处因为输入参数类型的问题而执行子类的方法,而父类方法被隐藏了,这样就造成了逻辑上的混乱。 14 | 15 | #### 依赖倒置原则 16 | 17 | 依赖倒置原则主要表现为以下三点: 18 | 19 | * 实现类之间通过接口或抽象类产生依赖关系,而不发生直接的依赖关系。 20 | * 接口或抽象类不依赖于实现类。 21 | * 实现类依赖于接口或抽象类。 22 | 23 | 采用该原则可以降低类间耦合,增加协同开发的效率。 24 | 25 | #### 接口隔离原则 26 | 27 | 接口隔离原则要求客户端不应该依赖不需要的接口,这就需要在满足单一职责原则的前提下,对接口进行细化,同时接口中的方法尽量少,而不要建立一个庞大臃肿的接口,容纳所有的客户端访问。 28 | 29 | 因此,开发者需要根据经验和常识决定接口的粒度大小,接口粒度太小,导致接口数据剧增;接口粒度太大,灵活性降低,给整体项目带来无法预料的风险。 30 | 31 | #### 最少知识原则 32 | 33 | 最少知识原则要求一个类应该对自己需要耦合或调用的类知道的最少。例如,安装软件时有步骤一、步骤二、步骤三,这些方法都封装在导向类中,在安装软件时,需要依次调用这些方法。此时,若导向类将这三个方法都声明为`public`,会导致业务场景类与导向类的关系太过紧密,修改时涉及的面也更大,例如在修改导向类中的这些方法时,还需要对业务场景类中相关调用进行修改。因此,更好的做法是将这三个方法都声明为`private`,而导向类只公布一个包含这三个方法调用的`public`方法。 34 | 35 | 同时,需要注意的是,类与类之间的关系应该建立在类间,而不是方法间,即一个方法中尽量不引入一个类中不存在的对象。 36 | 37 | #### 开闭原则 38 | 39 | 开闭原则的含义是说一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化。该原则主要体现为: 40 | 41 | * 抽象约束。通过接口或抽象类可以约束一组可能变化的行为,并且能够实现对扩展开放,即通过接口或抽象类约束扩展,对扩展进行边界限定,不允许出现在接口或抽象类中不存在的`public`方法,同时参数类型、引用对象尽量使用接口或抽象类,而不是实现类,而抽象层则尽量保持稳定,一旦确定即不允许修改。 42 | * 元数据控制模块行为。尽量使用元数据,即配置参数,来控制程序的行为,减少重复开发,元数据可以从文件中获得,也可以从数据库中获得。 43 | * 制定项目章程。 44 | * 封装变化。将相同的变化封装到一个接口或抽象类中;将不同的变化封装到不同的接口或抽象类中,不应该有两个不同的变化出现在同一个接口或抽象类中。 -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/10. 命令模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 命令模式将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。 4 | 5 | #### 类图 6 | 7 | ![](10.%20命令模式/1.png) 8 | 9 | **`Receiver` 接收者角色** 10 | 11 | 该角色就是干活的角色,命令传递到这里是应该被执行的。 12 | 13 | **`Command` 命令角色** 14 | 15 | 需要执行的所有命令都在这里声明。 16 | 17 | **`Invoker` 调用者角色** 18 | 19 | 接收到命令,并执行命令。 20 | 21 | #### 实现 22 | 23 | 通用`Receiver`类: 24 | ```java 25 | package com.sigalhu.commandpattern.impl; 26 | 27 | public abstract class Receiver { 28 | //抽象接受者,定义每个接受者都必须完成的业务 29 | public abstract void doSomething(); 30 | } 31 | ``` 32 | 具体的`Receiver`类: 33 | ```java 34 | package com.sigalhu.commandpattern.impl; 35 | 36 | public class ConcreteReceiver1 extends Receiver{ 37 | //每个接受者都必须处理一定的业务逻辑 38 | @Override 39 | public void doSomething() { 40 | } 41 | } 42 | ``` 43 | ```java 44 | package com.sigalhu.commandpattern.impl; 45 | 46 | public class ConcreteReceiver2 extends Receiver{ 47 | //每个接受者都必须处理一定的业务逻辑 48 | @Override 49 | public void doSomething() { 50 | } 51 | } 52 | ``` 53 | 抽象的`Command`类: 54 | ```java 55 | package com.sigalhu.commandpattern.impl; 56 | 57 | public abstract class Command { 58 | //每个命令类都必须有一个执行命令的方法 59 | public abstract void execute(); 60 | } 61 | ``` 62 | 具体的`Command`类: 63 | ```java 64 | package com.sigalhu.commandpattern.impl; 65 | 66 | public class ConcreteCommand1 extends Command { 67 | //对哪个Receiver类进行命令处理 68 | public Receiver receiver; 69 | 70 | //构造函数传递接受者 71 | public ConcreteCommand1(Receiver _receiver){ 72 | this.receiver = _receiver; 73 | } 74 | 75 | //必须实现一个命令 76 | @Override 77 | public void execute() { 78 | //业务处理 79 | this.receiver.doSomething(); 80 | } 81 | } 82 | ``` 83 | ```java 84 | package com.sigalhu.commandpattern.impl; 85 | 86 | public class ConcreteCommand2 extends Command { 87 | //对哪个Receiver类进行命令处理 88 | public Receiver receiver; 89 | 90 | //构造函数传递接受者 91 | public ConcreteCommand2(Receiver _receiver){ 92 | this.receiver = _receiver; 93 | } 94 | 95 | //必须实现一个命令 96 | @Override 97 | public void execute() { 98 | //业务处理 99 | this.receiver.doSomething(); 100 | } 101 | } 102 | ``` 103 | 调用者`Invoker`类: 104 | ```java 105 | package com.sigalhu.commandpattern.impl; 106 | 107 | public class Invoker { 108 | private Command command; 109 | 110 | //接受命令 111 | public void setCommand(Command _command){ 112 | this.command = _command; 113 | } 114 | 115 | //执行命令 116 | public void action(){ 117 | this.command.execute(); 118 | } 119 | } 120 | ``` 121 | 场景类: 122 | ```java 123 | package com.sigalhu.commandpattern.impl; 124 | 125 | public class Client { 126 | public static void main(String[] args){ 127 | //首先声明调用者Invoker 128 | Invoker invoker = new Invoker(); 129 | //定义接收者 130 | Receiver receiver = new ConcreteReceiver1(); 131 | //定义一个发送给接收者的命令 132 | Command command = new ConcreteCommand1(receiver); 133 | //把命令交给调用者去执行 134 | invoker.setCommand(command); 135 | invoker.action(); 136 | } 137 | } 138 | ``` 139 | 140 | #### 优点 141 | 142 | * 类间解耦。调用者角色与接收者角色之间没有任何依赖关系; 143 | * 可扩展性。`Command`的子类可以非常容易地扩展; 144 | * 命令模式结合其他模式会更优秀。例如可以结合责任链模式,实现命令族解析任务。 145 | 146 | #### 缺点 147 | 148 | 如果有`N`个命令,`Command`的子类就要有`N`个,会导致类膨胀。 149 | 150 | #### 注意 151 | 152 | 在项目中,约定的优先级最高,每一个命令是对一个或多个`Receiver`的封装,我们可以通过有意义的类名或命令名处理命令角色和接收者角色的耦合关系,减少高层模块对低层模块的依赖关系,提高系统整体的稳定性。因此,建议大家在实际项目开发时采用封闭`Receiver`的方式,减少`Client`对`Receiver`的依赖。 153 | 154 | #### 源码:https://github.com/SigalHu/DesignPatterns -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/10. 命令模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/10. 命令模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/11. 责任链模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 责任链模式通过将多个对象连成一条链,并沿着这条链传递请求,直到有对象处理它为止,这样可以避免请求的发送者与接受者之间的耦合关系。 4 | 5 | #### 类图 6 | 7 | ![](11.%20责任链模式/1.png) 8 | 9 | 抽象处理者实现三个职责:一是定义一个请求的处理方法`handleMessage`,唯一对外开放;二是定义一个链的编排方法`setNext`,设置下一个处理者;三是定义具体请求者必须实现的两个方法:定义自己能够处理的级别`getHandlerLevel`和具体的处理任务`echo`。 10 | 11 | 在处理者中涉及三个类:`Level`类负责定义请求和处理级别,`Request`类负责封装请求,`Response`负责封装链中的返回结果。 12 | 13 | #### 实现 14 | 15 | 抽象处理者: 16 | ```java 17 | package com.sigalhu.responsibilitychain.impl; 18 | 19 | public abstract class Handler { 20 | private Handler nextHandler; 21 | 22 | //每个处理者都必须对请求做出处理 23 | public final Response handleMessage(Request request){ 24 | Response response = null; 25 | //判断是否是自己的处理级别 26 | if(this.getHandlerLevel().equals(request.getRequestLevel())){ 27 | response = this.echo(request); 28 | } else { 29 | //判断是否有下一个处理者 30 | if(this.nextHandler != null){ 31 | response = this.nextHandler.handleMessage(request); 32 | } else { 33 | //没有适当的处理者,业务自行处理 34 | } 35 | } 36 | return response; 37 | } 38 | 39 | //设置下一个处理者是谁 40 | public void setNext(Handler _handler){ 41 | this.nextHandler = _handler; 42 | } 43 | 44 | //每个处理者都有一个处理级别 45 | protected abstract Level getHandlerLevel(); 46 | 47 | //每个处理者都必须实现处理任务 48 | protected abstract Response echo(Request request); 49 | } 50 | ``` 51 | 具体处理者: 52 | ```java 53 | package com.sigalhu.responsibilitychain.impl; 54 | 55 | public class ConcreteHandler1 extends Handler { 56 | //定义自己的处理逻辑 57 | @Override 58 | protected Response echo(Request request) { 59 | return null; 60 | } 61 | 62 | //设置自己的处理级别 63 | @Override 64 | protected Level getHandlerLevel() { 65 | return null; 66 | } 67 | } 68 | ``` 69 | ```java 70 | package com.sigalhu.responsibilitychain.impl; 71 | 72 | public class ConcreteHandler2 extends Handler{ 73 | //定义自己的处理逻辑 74 | @Override 75 | protected Response echo(Request request) { 76 | return null; 77 | } 78 | 79 | //设置自己的处理级别 80 | @Override 81 | protected Level getHandlerLevel() { 82 | return null; 83 | } 84 | } 85 | ``` 86 | ```java 87 | package com.sigalhu.responsibilitychain.impl; 88 | 89 | public class ConcreteHandler3 extends Handler { 90 | //定义自己的处理逻辑 91 | @Override 92 | protected Response echo(Request request) { 93 | return null; 94 | } 95 | 96 | //设置自己的处理级别 97 | @Override 98 | protected Level getHandlerLevel() { 99 | return null; 100 | } 101 | } 102 | ``` 103 | 模块中有关框架代码: 104 | ```java 105 | package com.sigalhu.responsibilitychain.impl; 106 | 107 | public class Level { 108 | //定义一个请求和处理等级 109 | } 110 | ``` 111 | ```java 112 | package com.sigalhu.responsibilitychain.impl; 113 | 114 | public class Request { 115 | //请求的等级 116 | public Level getRequestLevel(){ 117 | return null; 118 | } 119 | } 120 | ``` 121 | ```java 122 | package com.sigalhu.responsibilitychain.impl; 123 | 124 | public class Response { 125 | //处理者返回的数据 126 | } 127 | ``` 128 | 场景类: 129 | ```java 130 | package com.sigalhu.responsibilitychain.impl; 131 | 132 | public class Client { 133 | public static void main(String[] args){ 134 | //声明所有的处理节点 135 | Handler handler1 = new ConcreteHandler1(); 136 | Handler handler2 = new ConcreteHandler2(); 137 | Handler handler3 = new ConcreteHandler3(); 138 | //设置链中的阶段顺序1-->2-->3 139 | handler1.setNext(handler2); 140 | handler2.setNext(handler3); 141 | //提交请求,返回结果 142 | Response response = handler1.handleMessage(new Request()); 143 | } 144 | } 145 | ``` 146 | 147 | #### 优点 148 | 149 | 将请求与处理分开,请求者可以不用知道是谁处理的,处理者可以不用知道请求的全貌,两者解耦,提高系统的灵活性。 150 | 151 | #### 缺点 152 | 153 | * 性能问题。每个请求都是从链头遍历到链尾,特别是在链比较长的时候,性能是一个非常大的问题; 154 | * 调试不方便。特别是链条比较长,环节比较多的时候,由于采用了类似递归的方式,调试的时候逻辑可能比较复杂。 155 | 156 | #### 注意 157 | 158 | 链中节点数量需要控制,避免出现超长链的情况,一般的做法是在`Handler`中设置一个最大节点数量,在`setNext`方法中判断是否已经超过其阈值,超过则不允许该链建立,避免无意识地破坏系统性能。 159 | 160 | #### 源码:https://github.com/SigalHu/DesignPatterns -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/11. 责任链模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/11. 责任链模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/12. 装饰模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 装饰模式可以动态地给一个对象添加一些额外的职责。 4 | 5 | #### 类图 6 | 7 | ![](12.%20装饰模式/1.png) 8 | 9 | **`Component` 抽象构件** 10 | 11 | `Component`是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象。 12 | 13 | **`ConcreteComponent` 具体构件** 14 | 15 | `ConcreteComponent`是最核心、最原始、最基本的接口或抽象类的实现。 16 | 17 | **`Decorator` 装饰角色** 18 | 19 | 一般是一个抽象类,实现接口或者抽象方法,且属性里有一个`private`变量指向`Component`抽象构件。 20 | 21 | **`ConcreteDecorator` 具体装饰角色** 22 | 23 | `ConcreteDecorator`把最核心的、最原始的、最基本的东西装饰成其他东西。 24 | 25 | #### 实现 26 | 27 | 抽象构件: 28 | ```java 29 | package com.sigalhu.decoratorpattern.impl; 30 | 31 | public abstract class Component { 32 | //抽象的方法 33 | public abstract void operate(); 34 | } 35 | ``` 36 | 具体构件: 37 | ```java 38 | package com.sigalhu.decoratorpattern.impl; 39 | 40 | public class ConcreteComponent extends Component { 41 | //具体实现 42 | @Override 43 | public void operate() { 44 | System.out.println("do something"); 45 | } 46 | } 47 | ``` 48 | 抽象装饰者: 49 | ```java 50 | package com.sigalhu.decoratorpattern.impl; 51 | 52 | public abstract class Decorator extends Component { 53 | private Component component = null; 54 | 55 | //通过构造函数传递被装饰者 56 | public Decorator(Component _component){ 57 | this.component = _component; 58 | } 59 | 60 | //委托给被修饰者执行 61 | @Override 62 | public void operate() { 63 | this.component.operate(); 64 | } 65 | } 66 | ``` 67 | 具体的装饰类: 68 | ```java 69 | package com.sigalhu.decoratorpattern.impl; 70 | 71 | public class ConcreteDecorator1 extends Decorator{ 72 | //定义被修饰者 73 | public ConcreteDecorator1(Component _component){ 74 | super(_component); 75 | } 76 | 77 | //定义自己的修饰方法 78 | private void method1(){ 79 | System.out.println("method1 修饰"); 80 | } 81 | 82 | //重写父类的方法 83 | @Override 84 | public void operate() { 85 | this.method1(); 86 | super.operate(); 87 | } 88 | } 89 | ``` 90 | ```java 91 | package com.sigalhu.decoratorpattern.impl; 92 | 93 | public class ConcreteDecorator2 extends Decorator { 94 | //定义被修饰者 95 | public ConcreteDecorator2(Component _component){ 96 | super(_component); 97 | } 98 | 99 | //定义自己的修饰方法 100 | private void method2(){ 101 | System.out.println("method2 修饰"); 102 | } 103 | 104 | //重写父类的方法 105 | @Override 106 | public void operate() { 107 | super.operate(); 108 | this.method2(); 109 | } 110 | } 111 | ``` 112 | 场景类: 113 | ```java 114 | package com.sigalhu.decoratorpattern.impl; 115 | 116 | public class Client { 117 | public static void main(String[] args){ 118 | Component component = new ConcreteComponent(); 119 | //第一次修饰 120 | component = new ConcreteDecorator1(component); 121 | //第二次修饰 122 | component = new ConcreteDecorator2(component); 123 | //修饰后运行 124 | component.operate(); 125 | } 126 | } 127 | ``` 128 | 129 | #### 优点 130 | 131 | * 装饰类和被装饰类可以独立发展,而不会互相耦合; 132 | * 装饰模式作为继承关系的替代方案,可以解决子类膨胀的问题; 133 | * 装饰模式可以动态地扩展一个类的功能。 134 | 135 | #### 缺点 136 | 137 | 多层的装饰比较复杂,因此,尽量减少装饰类的数量,以便降低系统的复杂度。 138 | 139 | #### 源码:https://github.com/SigalHu/DesignPatterns -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/12. 装饰模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/12. 装饰模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/13. 策略模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。 4 | 5 | #### 类图 6 | 7 | ![](13.%20策略模式/1.png) 8 | 9 | **`Context` 封装角色** 10 | 11 | 也叫上下文角色,起承上启下封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。 12 | 13 | **`Strategy` 抽象策略角色** 14 | 15 | 策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。 16 | 17 | **`ConcreteStrategy` 具体策略角色** 18 | 19 | 实现抽象策略中的操作,包含具体的算法。 20 | 21 | #### 实现 22 | 23 | 抽象的策略角色: 24 | ```java 25 | package com.sigalhu.strategypattern.impl; 26 | 27 | public interface Strategy { 28 | //策略模式的运算法则 29 | public void doSomething(); 30 | } 31 | ``` 32 | 具体的策略角色: 33 | ```java 34 | package com.sigalhu.strategypattern.impl; 35 | 36 | public class ConcreteStrategy1 implements Strategy { 37 | @Override 38 | public void doSomething() { 39 | System.out.println("具体策略1的运算法则"); 40 | } 41 | } 42 | ``` 43 | ```java 44 | package com.sigalhu.strategypattern.impl; 45 | 46 | public class ConcreteStrategy2 implements Strategy { 47 | @Override 48 | public void doSomething() { 49 | System.out.println("具体策略2的运算法则"); 50 | } 51 | } 52 | ``` 53 | 封装角色: 54 | ```java 55 | package com.sigalhu.strategypattern.impl; 56 | 57 | public class Context { 58 | //抽象策略 59 | private Strategy strategy = null; 60 | 61 | //构造函数设置具体策略 62 | public Context(Strategy _strategy){ 63 | this.strategy = _strategy; 64 | } 65 | 66 | //封装后的策略方法 67 | public void doAnything(){ 68 | this.strategy.doSomething(); 69 | } 70 | } 71 | ``` 72 | 高层模块: 73 | ```java 74 | package com.sigalhu.strategypattern.impl; 75 | 76 | public class Client { 77 | public static void main(String[] args){ 78 | //声明一个具体的策略 79 | Strategy strategy = new ConcreteStrategy1(); 80 | //声明上下文对象 81 | Context context = new Context(strategy); 82 | //执行封装后的方法 83 | context.doAnything(); 84 | } 85 | } 86 | ``` 87 | 88 | #### 优点 89 | 90 | * 算法可以自由切换。只要实现抽象策略,它就成为策略家族的一员,通过封装角色对其进行封装,保证对外提供可自由切换的策略; 91 | * 避免使用多重条件判断。使用策略模式后,可以由其他模块决定采用何种策略,策略家族对外提供的访问接口就是封装类,简化了操作,同时避免了条件语句判断; 92 | * 扩展性良好。在现有的系统中增加一个策略,只要实现接口就可以,其他都不用修改,类似于一个可反复拆卸的插件。 93 | 94 | #### 缺点 95 | 96 | * 策略类数量增多。每一个策略都是一个类,复用的可能性很小,类数量增多; 97 | * 所有的策略类都需要对外暴露。上层模块必须知道有哪些策略,然后才能决定使用哪一个策略,与迪米特法则违背,但可以使用其他模式来修正这个缺陷,如工厂方法模式、代理模式或享元模式。 98 | 99 | #### 使用场景 100 | 101 | * 多个类只有在算法或者行为上稍有不同的场景; 102 | * 算法需要自由切换的场景; 103 | * 需要屏蔽算法规则的场景。 104 | 105 | #### 注意 106 | 107 | 如果系统中的一个策略家族的具体策略数量超过4个,则需要考虑使用混合模式,解决策略类膨胀和对外暴露的问题,否则会对系统维护造成困难。 108 | 109 | #### 源码:https://github.com/SigalHu/DesignPatterns -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/13. 策略模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/13. 策略模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/14. 适配器模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 适配器模式将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作,通常用来解决接口不相容的问题。 4 | 5 | #### 类图 6 | 7 | ![](14.%20适配器模式\1.png) 8 | 9 | **`Target` 目标角色** 10 | 11 | 该角色定义把其他类转换为何种接口,也就是我们期望接口。 12 | 13 | **`Adaptee` 源角色** 14 | 15 | 它是已经存在的、运行良好的类或对象,经过适配器角色的包装,从而转换成目标角色。 16 | 17 | **`Adapter` 适配器角色** 18 | 19 | 适配器模式的核心角色,其他两个角色都是已经存在的角色,而适配器角色是需要新建立的,它的职责非常简单:通过继承或是类关联的方式,把源角色转换为目标角色。 20 | 21 | #### 实现 22 | 23 | 目标角色: 24 | ```java 25 | package com.sigalhu.adapterpattern.impl; 26 | 27 | public interface Target { 28 | public void request(); 29 | } 30 | ``` 31 | 目标角色的实现类: 32 | ```java 33 | package com.sigalhu.adapterpattern.impl; 34 | 35 | public class ConcreteTarget implements Target { 36 | @Override 37 | public void request() { 38 | System.out.println("if you need any help, pls call me!"); 39 | } 40 | } 41 | ``` 42 | 源角色: 43 | ```java 44 | package com.sigalhu.adapterpattern.impl; 45 | 46 | public class Adaptee { 47 | //原有的业务逻辑 48 | public void doSomething(){ 49 | System.out.println("I'm kind of busy, leave me alone, pls!"); 50 | } 51 | } 52 | ``` 53 | 适配器角色: 54 | ```java 55 | package com.sigalhu.adapterpattern.impl; 56 | 57 | public class Adapter extends Adaptee implements Target { 58 | @Override 59 | public void request() { 60 | super.doSomething(); 61 | } 62 | } 63 | ``` 64 | 场景类: 65 | ```java 66 | package com.sigalhu.adapterpattern.impl; 67 | 68 | public class Client { 69 | public static void main(String[] args){ 70 | //原有的业务逻辑 71 | Target target = new ConcreteTarget(); 72 | target.request(); 73 | //现在增加了适配器角色后的业务逻辑 74 | Target target1 = new Adapter(); 75 | target1.request(); 76 | } 77 | } 78 | ``` 79 | 80 | #### 优点 81 | 82 | * 可以让两个没有任何关系的类一起运行。通过把非本系统接口的对象包装成本系统可以接受的对象,降低了系统大规模变更的风险; 83 | * 增加了类的透明度。访问的`Target`目标角色的具体实现都委托给了源角色,这些对高层次模块是透明的,也是它不需要关心的; 84 | * 提高了类的复用度。源角色在原有系统中还是可以正常使用,而在目标角色中也可以充当新的演员; 85 | * 灵活性好。适配器基本上类似一个灵活的构件,想用就用,不想用就卸载。 86 | 87 | #### 注意 88 | 89 | 适配器模式不是为了解决还处在开发阶段的问题,而是解决正在服役的项目问题,该模式主要用于应用扩展。 90 | 91 | #### 源码:https://github.com/SigalHu/DesignPatterns -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/14. 适配器模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/14. 适配器模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/15. 迭代器模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 迭代器模式提供一种方法访问一个容器对象中各个元素,而又不需要暴露该对象的内部细节。 4 | 5 | #### 类图 6 | 7 | ![](15.%20迭代器模式\1.png) 8 | 9 | **`Iterator` 抽象迭代器** 10 | 11 | 抽象迭代器负责定义访问和遍历元素的接口,基本上是有固定的3个方法:`first()`获得第一个元素,`next()`访问下一个元素,`isDone()`是否已经访问到底部。 12 | 13 | **`ConcreteIterator` 具体迭代器** 14 | 15 | 具体迭代器实现迭代器接口,完成容器元素的遍历。 16 | 17 | **`Aggregate` 抽象容器** 18 | 19 | 抽象容器负责提供创建具体迭代器的接口。 20 | 21 | **`ConcreteAggregate` 具体容器** 22 | 23 | 具体容器实现抽象容器定义的方法,创建出容纳迭代器的对象。 24 | 25 | #### 实现 26 | 27 | 抽象迭代器: 28 | ```java 29 | package com.sigalhu.iteratorpattern.impl; 30 | 31 | public interface Iterator { 32 | //遍历到下一个元素 33 | public Object next(); 34 | //是否已经遍历到尾部 35 | public boolean hasNext(); 36 | //删除当前指向的元素 37 | public boolean remove(); 38 | } 39 | ``` 40 | 具体迭代器: 41 | ```java 42 | package com.sigalhu.iteratorpattern.impl; 43 | 44 | import java.util.Vector; 45 | 46 | public class ConcreteIterator implements Iterator { 47 | private Vector vector = new Vector(); 48 | //定义当前游标 49 | public int cursor = 0; 50 | 51 | @SuppressWarnings("unchecked") 52 | public ConcreteIterator(Vector _vector){ 53 | this.vector = _vector; 54 | } 55 | 56 | //判断是否到达尾部 57 | @Override 58 | public boolean hasNext() { 59 | if(this.cursor == this.vector.size()){ 60 | return false; 61 | } else { 62 | return true; 63 | } 64 | } 65 | 66 | //返回下一个元素 67 | @Override 68 | public Object next() { 69 | Object result = null; 70 | if(this.hasNext()){ 71 | result = this.vector.get(this.cursor++); 72 | }else{ 73 | result = null; 74 | } 75 | return result; 76 | } 77 | 78 | //删除当前元素 79 | @Override 80 | public boolean remove() { 81 | this.vector.remove(this.cursor); 82 | return true; 83 | } 84 | } 85 | ``` 86 | 抽象容器: 87 | ```java 88 | package com.sigalhu.iteratorpattern.impl; 89 | 90 | public interface Aggregate { 91 | //增加元素 92 | public void add(Object object); 93 | //减少元素 94 | public void remove(Object object); 95 | //由迭代器来遍历所有元素 96 | public Iterator iterator(); 97 | } 98 | ``` 99 | 具体容器: 100 | ```java 101 | package com.sigalhu.iteratorpattern.impl; 102 | 103 | import java.util.Vector; 104 | 105 | public class ConcreteAggregate implements Aggregate { 106 | //容纳对象的容器 107 | private Vector vector = new Vector(); 108 | 109 | //增加一个元素 110 | @Override 111 | public void add(Object object) { 112 | this.vector.add(object); 113 | } 114 | 115 | //删除一个元素 116 | @Override 117 | public void remove(Object object) { 118 | this.vector.remove(object); 119 | } 120 | 121 | //返回迭代器对象 122 | @Override 123 | public Iterator iterator() { 124 | return new ConcreteIterator(this.vector); 125 | } 126 | } 127 | ``` 128 | 场景类: 129 | ```java 130 | package com.sigalhu.iteratorpattern.impl; 131 | 132 | public class Client { 133 | public static void main(String[] args) { 134 | //声明出容器 135 | Aggregate agg = new ConcreteAggregate(); 136 | //产生对象数据放进去 137 | agg.add("abc"); 138 | agg.add("aaa"); 139 | agg.add("1234"); 140 | //遍历 141 | Iterator iterator = agg.iterator(); 142 | while (iterator.hasNext()){ 143 | System.out.println(iterator.next()); 144 | } 145 | } 146 | } 147 | ``` 148 | 149 | #### 优点 150 | 151 | 迭代器模式提供了遍历容器的方便性,容器只要管理增减元素就可以了,需要遍历时交由迭代器进行。 152 | 153 | #### 源码:https://github.com/SigalHu/DesignPatterns -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/15. 迭代器模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/15. 迭代器模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/16. 组合模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 组合模式将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。 4 | 5 | #### 类图 6 | 7 | ![](16.%20组合模式\1.png) 8 | 9 | **`Component` 抽象构件角色** 10 | 11 | 定义参加组合对象的共有方法与属性,可以定义一些默认的行为或属性。 12 | 13 | **`Leaf` 叶子构件** 14 | 15 | 叶子对象其下没有分支,是遍历的最小单位。 16 | 17 | **`Composite` 树枝构件** 18 | 19 | 树枝对象的作用是组合树枝节点和叶子节点形成一个树形结构。 20 | 21 | #### 实现 22 | 23 | 抽象构件: 24 | ```java 25 | package com.sigalhu.compositepattern.impl; 26 | 27 | public abstract class Component { 28 | //个体和整体都具有的共享 29 | public void doSomething(){ 30 | //业务逻辑 31 | } 32 | } 33 | ``` 34 | 树枝构件: 35 | ```java 36 | package com.sigalhu.compositepattern.impl; 37 | 38 | import java.util.ArrayList; 39 | 40 | public class Composite extends Component { 41 | //构件容器 42 | private ArrayList componentArrayList = new ArrayList(); 43 | 44 | //增加一个叶子构件或树枝构件 45 | public void add(Component component){ 46 | this.componentArrayList.add(component); 47 | } 48 | 49 | //删除一个叶子构件或树枝构件 50 | public void remove(Component component){ 51 | this.componentArrayList.remove(component); 52 | } 53 | 54 | //获得分支下的所有叶子构件和树枝构件 55 | public ArrayList getChildren() { 56 | return this.componentArrayList; 57 | } 58 | } 59 | ``` 60 | 叶子构件: 61 | ```java 62 | package com.sigalhu.compositepattern.impl; 63 | 64 | public class Leaf extends Component { 65 | //可以覆写父类方法 66 | @Override 67 | public void doSomething() { 68 | super.doSomething(); 69 | } 70 | } 71 | ``` 72 | 场景类: 73 | ```java 74 | package com.sigalhu.compositepattern.impl; 75 | 76 | public class Client { 77 | public static void main(String[] args) { 78 | //创建一个根节点 79 | Composite root = new Composite(); 80 | root.doSomething(); 81 | //创建一个树枝构件 82 | Composite branch = new Composite(); 83 | //创建一个叶子节点 84 | Leaf leaf = new Leaf(); 85 | //建立整体 86 | root.add(branch); 87 | branch.add(leaf); 88 | } 89 | 90 | //通过递归遍历树 91 | public static void display(Composite root) { 92 | for (Component c : root.getChildren()) { 93 | if (c instanceof Leaf) { //叶子节点 94 | c.doSomething(); 95 | } else { //树枝节点 96 | display((Composite) c); 97 | } 98 | } 99 | } 100 | } 101 | ``` 102 | 103 | #### 优点 104 | 105 | * 高层模块调用简单,不必担心自己处理的是单个对象还是整个组合结构,简化了高层模块的代码; 106 | * 节点自由增加,符合开闭原则,有利于后续维护。 107 | 108 | #### 缺点 109 | 110 | 与依赖倒置原则冲突,限制了接口的影响范围。 111 | 112 | #### 源码:https://github.com/SigalHu/DesignPatterns -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/16. 组合模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/16. 组合模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/17. 观察者模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 观察者模式定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。 4 | 5 | #### 类图 6 | 7 | ![](17.%20观察者模式\1.png) 8 | 9 | **`Subject` 被观察者** 10 | 11 | 被观察者必须能够动态地增加、取消观察者,一般是抽象类或实现类,仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者。 12 | 13 | **`Observer` 观察者** 14 | 15 | 观察者接收到消息后,即进行update操作,对接收到的信息进行处理。 16 | 17 | **`ConcreteSubject` 具体的被观察者** 18 | 19 | 具体的被观察者定义被观察者的业务逻辑,同时定义对哪些事件进行通知。 20 | 21 | **`ConcreteObserver` 具体的观察者** 22 | 23 | 具体的观察者在接收到消息后进行相应的处理。 24 | 25 | #### 实现 26 | 27 | 被观察者: 28 | ```java 29 | package com.sigalhu.observerpattern.impl; 30 | 31 | import java.util.Vector; 32 | 33 | public abstract class Subject { 34 | //定义一个观察者数组 35 | private Vector obsVector = new Vector<>(); 36 | 37 | //增加一个观察者 38 | public void addObserver(Observer o){ 39 | this.obsVector.add(o); 40 | } 41 | 42 | //删除一个观察者 43 | public void delObserver(Observer o){ 44 | this.obsVector.remove(o); 45 | } 46 | 47 | //通知所有观察者 48 | public void notifyObservers(){ 49 | for(Observer o:this.obsVector){ 50 | o.update(); 51 | } 52 | } 53 | } 54 | ``` 55 | 具体的被观察者: 56 | ```java 57 | package com.sigalhu.observerpattern.impl; 58 | 59 | public class ConcreteSubject extends Subject { 60 | //具体的业务 61 | public void doSomething(){ 62 | //do something 63 | super.notifyObservers(); 64 | } 65 | } 66 | ``` 67 | 观察者: 68 | ```java 69 | package com.sigalhu.observerpattern.impl; 70 | 71 | public interface Observer { 72 | //更新方法 73 | public void update(); 74 | } 75 | ``` 76 | 具体的观察者: 77 | ```java 78 | package com.sigalhu.observerpattern.impl; 79 | 80 | public class ConcreteObserver implements Observer { 81 | //实现更新方法 82 | @Override 83 | public void update() { 84 | System.out.println("接收到消息,并进行处理!"); 85 | } 86 | } 87 | ``` 88 | 场景类: 89 | ```java 90 | package com.sigalhu.observerpattern.impl; 91 | 92 | public class Client { 93 | public static void main(String[] args) { 94 | //创建一个被观察者 95 | ConcreteSubject subject = new ConcreteSubject(); 96 | //定义一个观察者 97 | Observer obs = new ConcreteObserver(); 98 | //观察者观察被观察者 99 | subject.addObserver(obs); 100 | //观察者开始活动了 101 | subject.doSomething(); 102 | } 103 | } 104 | ``` 105 | 106 | #### 优点 107 | 108 | * 观察者与被观察者之间是抽象耦合,不管是增加观察者还是被观察者都非常容易扩展; 109 | * 建立了一套触发机制。 110 | 111 | #### 缺点 112 | 113 | 开发和调试比较复杂,而且在`Java`中消息的通知默认是顺序执行,一个观察者卡壳,会影响整体的执行效率,因此一般采用异步方式处理。 114 | 115 | #### 建议 116 | 117 | 在一个观察者模式中最多出现一个对象既是观察者也是被观察者,也就是说消息最多转发一次(传递两次)。 118 | 119 | #### 源码:https://github.com/SigalHu/DesignPatterns -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/17. 观察者模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/17. 观察者模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/18. 门面模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 门面模式要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行,门面模式提供一个高层次的接口,使得子系统更易于使用。 4 | 5 | #### 类图 6 | 7 | ![](18.%20门面模式\1.png) 8 | 9 | **`Facade` 门面角色** 10 | 11 | 客户端可以调用这个角色的方法,此角色知晓子系统的所有功能和责任,一般情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去,也就是说该角色没有实际的业务逻辑,只是一个委托类。 12 | 13 | **`Subsystem` 子系统角色** 14 | 15 | 可以同时有一个或者多个子系统,每个子系统都是一个类的集合,且其并不知道门面的存在,对于子系统而言,门面仅仅是另外一个客户端而已。 16 | 17 | #### 实现 18 | 19 | 子系统: 20 | ```java 21 | package com.sigalhu.facadepattern.impl; 22 | 23 | public class ClassA { 24 | public void doSomethingA(){ 25 | //业务逻辑 26 | } 27 | } 28 | ``` 29 | ```java 30 | package com.sigalhu.facadepattern.impl; 31 | 32 | public class ClassB { 33 | public void doSomethingB(){ 34 | //业务逻辑 35 | } 36 | } 37 | ``` 38 | ```java 39 | package com.sigalhu.facadepattern.impl; 40 | 41 | public class ClassC { 42 | public void doSomethingC(){ 43 | //业务逻辑 44 | } 45 | } 46 | ``` 47 | 门面对象: 48 | ```java 49 | package com.sigalhu.facadepattern.impl; 50 | 51 | public class Facade { 52 | //被委托的对象 53 | private ClassA a = new ClassA(); 54 | private ClassB b = new ClassB(); 55 | private ClassC c = new ClassC(); 56 | 57 | //提供给外部访问的方法 58 | public void methodA(){ 59 | this.a.doSomethingA(); 60 | } 61 | public void methodB(){ 62 | this.b.doSomethingB(); 63 | } 64 | public void methodC(){ 65 | this.c.doSomethingC(); 66 | } 67 | } 68 | ``` 69 | 70 | #### 优点 71 | 72 | * 减少系统的相互依赖,如果不使用门面模式,外界访问直接深入到子系统内部,相互之间是一种强耦合的关系; 73 | * 提高了灵活性,子系统内部的改变只要不影响到门面对象,就不会对客户端代码造成影响; 74 | * 提高了安全性,可以通过门面对象来控制对子系统的访问。 75 | 76 | #### 缺点 77 | 78 | 不符合开闭原则,对修改关闭,对扩展开放。 79 | 80 | #### 注意 81 | 82 | 门面对象只是提供一个访问子系统的路径而已,它不应该也不能参与具体的业务逻辑,否则就会产生一个倒依赖的问题:子系统必须依赖门面才能被访问。 83 | 84 | #### 源码:https://github.com/SigalHu/DesignPatterns -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/18. 门面模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/18. 门面模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/19. 备忘录模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 备忘录模式在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态。 4 | 5 | #### 类图 6 | 7 | ![](19.%20备忘录模式\1.png) 8 | 9 | **`Originator` 发起人角色** 10 | 11 | 记录当前时刻的状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。 12 | 13 | **`Memento` 备忘录角色** 14 | 15 | 负责存储`Originator`发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。 16 | 17 | **`Caretaker` 备忘录管理员角色** 18 | 19 | 对备忘录进行管理、保存和提供备忘录。 20 | 21 | 22 | #### 实现 23 | 24 | 发起人角色: 25 | ```java 26 | package com.sigalhu.mementopattern.impl; 27 | 28 | public class Originator { 29 | //内部状态 30 | private String state = ""; 31 | 32 | public String getState(){ 33 | return state; 34 | } 35 | 36 | public void setState(String state) { 37 | this.state = state; 38 | } 39 | 40 | //创建一个备忘录 41 | public Memento createMemento(){ 42 | return new Memento(this.state); 43 | } 44 | 45 | //恢复一个备忘录 46 | public void restoreMemento(Memento memento){ 47 | this.setState(memento.getState()); 48 | } 49 | } 50 | ``` 51 | 备忘录角色: 52 | ```java 53 | package com.sigalhu.mementopattern.impl; 54 | 55 | public class Memento { 56 | //发起人的内部状态 57 | private String state = ""; 58 | 59 | public Memento(String state){ 60 | this.state = state; 61 | } 62 | 63 | public String getState() { 64 | return state; 65 | } 66 | 67 | public void setState(String state) { 68 | this.state = state; 69 | } 70 | } 71 | ``` 72 | 备忘录管理员角色: 73 | ```java 74 | package com.sigalhu.mementopattern.impl; 75 | 76 | public class Caretaker { 77 | //备忘录对象 78 | private Memento memento; 79 | 80 | public Memento getMemento() { 81 | return memento; 82 | } 83 | 84 | public void setMemento(Memento memento) { 85 | this.memento = memento; 86 | } 87 | } 88 | ``` 89 | 场景类: 90 | ```java 91 | package com.sigalhu.mementopattern.impl; 92 | 93 | public class Client { 94 | public static void main(String[] args) { 95 | //定义发起人 96 | Originator originator = new Originator(); 97 | //定义备忘录管理员 98 | Caretaker caretaker = new Caretaker(); 99 | //创建一个备忘录 100 | caretaker.setMemento(originator.createMemento()); 101 | //恢复一个备忘录 102 | originator.restoreMemento(caretaker.getMemento()); 103 | } 104 | } 105 | ``` 106 | 107 | #### 注意 108 | 109 | * 备忘录创建出来就要在“最近”的代码中使用,要主动管理它的生命周期,建立就要使用,不使用就要立刻删除其引用,等待垃圾回收器对它的回收处理; 110 | * 不要在频繁建立备份的场景中使用备忘录模式,原因有二:一是控制不了备忘录建立的对象数量,二是大对象的建立是要消耗资源的,系统性能需要考虑。 111 | 112 | #### 源码:https://github.com/SigalHu/DesignPatterns -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/19. 备忘录模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/19. 备忘录模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/2. 单例模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 单例模式要求一个类只能生成一个对象。开发者可以通过定义一个私有访问权限的构造函数,来避免被其他类`new`出一个对象,其他类对该类的访问可以通过`getInstance`获得同一个对象。 4 | 5 | #### 类图 6 | 7 | ![](2.%20单例模式/1.png) 8 | 9 | #### 实现 10 | 11 | 单例模式根据申请对象的时机可分为饿汉式单例与懒汉式单例,其中,饿汉式单例在程序启动就申请对象;懒汉式单例在其他类获取单例时申请对象。需要注意的是,懒汉式单例还需要考虑多线程同时调用`getInstance`方法的影响。 12 | 13 | 饿汉式单例: 14 | ```java 15 | package com.sigalhu.singleton.impl; 16 | 17 | public class Singleton { 18 | private static final Singleton singleton = new Singleton(); 19 | 20 | //限制产生多个对象 21 | private Singleton(){} 22 | 23 | //通过该方法获得实例对象 24 | public static Singleton getSingleton(){ 25 | return singleton; 26 | } 27 | 28 | //类中其他方法,尽量是static 29 | public static void daSomething(){ 30 | } 31 | } 32 | ``` 33 | 线程不安全的懒汉式单例: 34 | ```java 35 | package com.sigalhu.singleton.impl; 36 | 37 | public class Singleton { 38 | private static Singleton singleton = null; 39 | 40 | //限制产生多个对象 41 | private Singleton(){} 42 | 43 | //通过该方法获得实例对象 44 | public static Singleton getSingleton(){ 45 | if(singleton == null){ 46 | singleton = new Singleton(); 47 | } 48 | return singleton; 49 | } 50 | } 51 | ``` 52 | 线程安全的懒汉式单例: 53 | ```java 54 | package com.sigalhu.singleton.impl; 55 | 56 | public class Singleton { 57 | private static Singleton singleton = null; 58 | 59 | //限制产生多个对象 60 | private Singleton(){} 61 | 62 | //通过该方法获得实例对象 63 | public static Singleton getSingleton(){ 64 | if(singleton == null){ 65 | synchronized (Singleton.class){ 66 | if(singleton == null){ 67 | singleton = new Singleton(); 68 | } 69 | } 70 | } 71 | return singleton; 72 | } 73 | } 74 | ``` 75 | 76 | #### 优点 77 | 78 | * 内存中只有一个实例,减少了内存开支; 79 | * 只生成一个实例,减少了系统的性能开销; 80 | * 可以避免对资源的多重占用; 81 | * 可以在系统设置全局的访问点,优化和共享资源访问。 82 | 83 | #### 缺点 84 | 85 | * 单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现; 86 | * 若单例模式没有完成,是不能进行测试的,没有接口也不能使用`mock`的方法虚拟一个对象; 87 | * 单例模式与单一职责原则冲突。 88 | 89 | #### 源码:https://github.com/SigalHu/DesignPatterns 90 | -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/2. 单例模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/2. 单例模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/20. 访问者模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 封装一些作用于某种数据结果中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。 4 | 5 | #### 类图 6 | 7 | ![](20.%20访问者模式/1.png) 8 | 9 | **`Visitor` 抽象访问者** 10 | 11 | 抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是`visit`方法的参数定义哪些对象是可以被访问的。 12 | 13 | **`ConcreteVisitor` 具体访问者** 14 | 15 | 它影响访问者访问到一个类后该怎么干,要做什么事。 16 | 17 | **`Element` 抽象元素** 18 | 19 | 接口或者抽象类,声明接受哪一类访问者访问,程序上是通过`accept`方法中的参数来定义的。 20 | 21 | **`ConcreteElement` 具体元素** 22 | 23 | 实现`accept`方法。通常是`visitor.visit(this)`。 24 | 25 | **`ObjectStruture` 结构对象** 26 | 27 | 元素生产者,一般容纳在多个不同类、不同接口的容器,如`List`、`Set`、`Map`等,在项目中,一般很少抽象出这个角色。 28 | 29 | #### 实现 30 | 31 | 抽象元素: 32 | ```java 33 | package com.sigalhu.visitorpattern.impl; 34 | 35 | public abstract class Element { 36 | //定义业务逻辑 37 | public abstract void doSomething(); 38 | //允许谁来访问 39 | public abstract void accept(IVisitor visitor); 40 | } 41 | ``` 42 | 具体元素: 43 | ```java 44 | package com.sigalhu.visitorpattern.impl; 45 | 46 | public class ConcreteElement1 extends Element { 47 | //完善业务逻辑 48 | @Override 49 | public void doSomething() { 50 | //业务处理 51 | } 52 | 53 | //允许那个访问者访问 54 | @Override 55 | public void accept(IVisitor visitor) { 56 | visitor.visit(this); 57 | } 58 | } 59 | ``` 60 | ```java 61 | package com.sigalhu.visitorpattern.impl; 62 | 63 | public class ConcreteElement2 extends Element { 64 | //完善业务逻辑 65 | @Override 66 | public void doSomething() { 67 | //业务处理 68 | } 69 | 70 | //允许那个访问者访问 71 | @Override 72 | public void accept(IVisitor visitor) { 73 | visitor.visit(this); 74 | } 75 | } 76 | ``` 77 | 抽象访问者: 78 | ```java 79 | package com.sigalhu.visitorpattern.impl; 80 | 81 | public interface IVisitor { 82 | //可以访问哪些对象 83 | public void visit(ConcreteElement1 el1); 84 | public void visit(ConcreteElement2 el2); 85 | } 86 | ``` 87 | 具体访问者: 88 | ```java 89 | package com.sigalhu.visitorpattern.impl; 90 | 91 | public class Visitor implements IVisitor { 92 | //访问el1元素 93 | @Override 94 | public void visit(ConcreteElement1 el1) { 95 | el1.doSomething(); 96 | } 97 | 98 | //访问el2元素 99 | @Override 100 | public void visit(ConcreteElement2 el2) { 101 | el2.doSomething(); 102 | } 103 | } 104 | ``` 105 | 结构对象: 106 | ```java 107 | package com.sigalhu.visitorpattern.impl; 108 | 109 | import java.util.Random; 110 | 111 | public class ObjectStruture { 112 | //对象生成器,这里通过一个工厂方法模式模拟 113 | public static Element createElement(){ 114 | Random rand = new Random(); 115 | if(rand.nextInt(100) > 50){ 116 | return new ConcreteElement1(); 117 | } else { 118 | return new ConcreteElement2(); 119 | } 120 | } 121 | } 122 | ``` 123 | 场景类: 124 | ```java 125 | package com.sigalhu.visitorpattern.impl; 126 | 127 | public class Client { 128 | public static void main(String[] args){ 129 | for(int i=0;i<10;i++){ 130 | //获得元素对象 131 | Element el = ObjectStruture.createElement(); 132 | //接受访问者访问 133 | el.accept(new Visitor()); 134 | } 135 | } 136 | } 137 | ``` 138 | 139 | #### 优点 140 | 141 | * 符合单一职责原则。`Element`抽象类的两个子类负责数据的加载,而`Visitor`类则负责报表的展现,两个不同的职责非常明确地分离开来,各自演绎变化; 142 | * 优秀的扩展性。由于职责分开,继续增加对数据的操作是非常快捷的; 143 | * 灵活性非常高。 144 | 145 | #### 缺点 146 | 147 | * 具体元素对访问者公布细节; 148 | * 具体元素变更比较困难; 149 | * 访问者依赖的是具体元素,而不是抽象元素,违背了依赖倒置原则。 150 | 151 | #### 源码:https://github.com/SigalHu/DesignPatterns -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/20. 访问者模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/20. 访问者模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/21. 状态模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。 4 | 5 | #### 类图 6 | 7 | ![](21.%20状态模式/1.png) 8 | 9 | **`State` 抽象状态角色** 10 | 11 | 接口或抽象类,负责对象状态定义,并且封装环境角色以实现状态切换。 12 | 13 | **`ConcreteState` 具体状态角色** 14 | 15 | 每一个具体状态必须完成两个职责:本状态的行为管理以及趋向状态处理,通俗地说,就是本状态下要做的事情,以及本状态如何过渡到其他状态。 16 | 17 | **`Context` 环境角色** 18 | 19 | 定义客户端需要的接口,并且负责具体状态的切换。 20 | 21 | #### 实现 22 | 23 | 抽象状态角色: 24 | ```java 25 | package com.sigalhu.statepattern.impl; 26 | 27 | public abstract class State { 28 | //定义一个环境角色,提供子类访问 29 | protected Context context; 30 | 31 | //设置环境角色 32 | public void setContext(Context context){ 33 | this.context = context; 34 | } 35 | 36 | //行为1 37 | public abstract void handle1(); 38 | //行为2 39 | public abstract void handle2(); 40 | } 41 | ``` 42 | 具体状态角色: 43 | ```java 44 | package com.sigalhu.statepattern.impl; 45 | 46 | public class ConcreteState1 extends State { 47 | @Override 48 | public void handle1() { 49 | //本状态下必须处理的逻辑 50 | } 51 | 52 | @Override 53 | public void handle2() { 54 | //设置当前状态为state2 55 | super.context.setCurrentState(Context.STATE2); 56 | //过渡到state2状态,由Context实现 57 | super.context.handle2(); 58 | } 59 | } 60 | ``` 61 | ```java 62 | package com.sigalhu.statepattern.impl; 63 | 64 | public class ConcreteState2 extends State { 65 | @Override 66 | public void handle1() { 67 | //设置当前状态为state1 68 | super.context.setCurrentState(Context.STATE1); 69 | //过渡到state1状态,由Context实现 70 | super.context.handle1(); 71 | } 72 | 73 | @Override 74 | public void handle2() { 75 | //本状态下必须处理的逻辑 76 | } 77 | } 78 | ``` 79 | 环境角色: 80 | ```java 81 | package com.sigalhu.statepattern.impl; 82 | 83 | public class Context { 84 | //定义状态 85 | public final static State STATE1 = new ConcreteState1(); 86 | public final static State STATE2 = new ConcreteState2(); 87 | 88 | //当前状态 89 | private State currentState; 90 | 91 | //获得当前状态 92 | public State getCurrentState(){ 93 | return currentState; 94 | } 95 | 96 | //设置当前状态 97 | public void setCurrentState(State currentState) { 98 | this.currentState = currentState; 99 | //切换状态 100 | this.currentState.setContext(this); 101 | } 102 | 103 | //行为委托 104 | public void handle1(){ 105 | this.currentState.handle1(); 106 | } 107 | public void handle2(){ 108 | this.currentState.handle2(); 109 | } 110 | } 111 | ``` 112 | 场景类: 113 | ```java 114 | package com.sigalhu.statepattern.impl; 115 | 116 | public class Client { 117 | public static void main(String[] args){ 118 | //定义环境角色 119 | Context context = new Context(); 120 | //初始化状态 121 | context.setCurrentState(new ConcreteState1()); 122 | //行为执行 123 | context.handle1(); 124 | context.handle2(); 125 | } 126 | } 127 | ``` 128 | 129 | #### 优点 130 | 131 | * 结构清晰。避免了过多的`switch...case`或者`if...else`语句的使用,避免了程序的复杂性,提高系统的可维护性; 132 | * 遵循设计原则。很好地体现了开闭原则和单一职责原则,每个状态都是一个子类,你要增加状态就要增加子类,你要修改状态,就只要修改一个子类就好了; 133 | * 封装性非常好。状态变换放置到类的内部来实现,外部的调用不用知道类内部如何实现状态和行为的变换。 134 | 135 | #### 缺点 136 | 137 | 如果完全使用状态模式就会有太多的子类,造成类膨胀,不好管理。 138 | 139 | #### 注意 140 | 141 | 状态模式适用于当某个对象在它的状态发生改变时,它的行为也随着发生比较大的变化,也就是说在行为受状态约束的情况下可以使用状态模式,而且使用时对象的状态最好不要超过5个。 142 | 143 | #### 源码:https://github.com/SigalHu/DesignPatterns -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/21. 状态模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/21. 状态模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/22. 解释器模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。 4 | 5 | #### 类图 6 | 7 | ![](22.%20解释器模式/1.png) 8 | 9 | **`AbstractExpression` 抽象解释器** 10 | 11 | 抽象解释器是生成语法集合的关键,每个语法集合完成指定语法解析任务,它是通过递归调用的方式,最终由最小的语法单元进行解析完成。具体的解释任务由各个实现类完成,具体的解释器分别由`TerminalExpression`和`NonterminalExpression`完成。 12 | 13 | **`TerminalExpression` 终结符表达式** 14 | 15 | 处理场景元素和数据的转换,实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。 16 | 17 | 注:终结符指需要具体赋值的对象,例如公式中的运算元素,除了需要赋值外,不需要做任何处理。 18 | 19 | **`NonterminalExpression` 非终结符表达式** 20 | 21 | 文法中的每条规则对应于一个非终结表达式,并且每个文法规则都只关心自己周边的文法规则的结果,原则上每个文法规则都对应一个非终结表达式。 22 | 23 | 注:非终结表达式对应公式中的运算符号,需要编写算法进行处理,每个运算符号都要对应处理单元,否则公式无法进行。 24 | 25 | 26 | #### 实现 27 | 28 | 抽象表达式: 29 | ```java 30 | package com.sigalhu.interpreterpattern.impl; 31 | 32 | public abstract class Expression { 33 | //每个表达式必须有一个解析任务 34 | public abstract Object interpreter(Context ctx); 35 | } 36 | ``` 37 | 终结符表达式: 38 | ```java 39 | package com.sigalhu.interpreterpattern.impl; 40 | 41 | public class TerminalExpression extends Expression { 42 | //通常终结符表达式只有一个,但是有多个对象 43 | @Override 44 | public Object interpreter(Context ctx) { 45 | return null; 46 | } 47 | } 48 | ``` 49 | 非终结符表达式: 50 | ```java 51 | package com.sigalhu.interpreterpattern.impl; 52 | 53 | public class NonterminalExpression extends Expression { 54 | //每个非终结符表达式都会对其他表达式产生依赖 55 | public NonterminalExpression(Expression... expressions){ 56 | } 57 | 58 | @Override 59 | public Object interpreter(Context ctx) { 60 | //进行文法处理 61 | return null; 62 | } 63 | } 64 | ``` 65 | 客户类: 66 | ```java 67 | package com.sigalhu.interpreterpattern.impl; 68 | 69 | import java.util.Stack; 70 | 71 | public class Client { 72 | public static void main(String[] args){ 73 | Context ctx = new Context(); 74 | //通常定一个语法容器,容纳一个具体的表达式 75 | Stack stack = null; 76 | for(;;){ 77 | //进行语法判断,并产生递归调用 78 | } 79 | //产生一个完整的语法数,由各个具体的语法分析进行解析 80 | Expression exp = stack.pop(); 81 | //具体元素进入场景 82 | exp.interpreter(ctx); 83 | } 84 | } 85 | ``` 86 | 87 | #### 优点 88 | 89 | 解释器是一个简单的语法分析工具,它最显著的优点就是扩展性,修改语法规则只要修改相应的非终结符表达式就可以了,若扩展语法,则只要增加非终结符类就可以了。 90 | 91 | #### 缺点 92 | 93 | * 会引起类膨胀。每个语法都要产生一个非终结符表达式,语法规则比较复杂时,就可能产生大量的类文件,为维护带来非常多的麻烦; 94 | * 采用递归调用方法,导致调试非常复杂; 95 | * 使用了大量的循环和递归,效率低下。 96 | 97 | #### 注意 98 | 99 | 尽量不要在重要的模块中使用解释器模式,否则维护会是一个很大的问题。在项目中可以使用`shell`、`JRuby`、`Groovy`等脚本语言来代替解释器模式,弥补`Java`编译型语言的不足。 100 | 101 | #### 源码:https://github.com/SigalHu/DesignPatterns -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/22. 解释器模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/22. 解释器模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/23. 享元模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 使用共享对象可有效地支持大量的细粒度的对象,我们可以将这些对象的信息分为两个部分:内部状态与外部状态。 4 | 5 | * 内部状态是对象可共享出来的信息,存储在享元对象内部并且不会随环境改变而改变,它可以作为一个对象的动态附加信息,不必直接储存在具体某个对象中,属于可以共享的部分; 6 | * 外部状态是对象得以依赖的一个标记,是随环境改变而改变的、不可以共享的状态,它是一批对象的统一标识,是唯一的一个索引值。 7 | 8 | #### 类图 9 | 10 | ![](23.%20享元模式/1.png) 11 | 12 | **`Flyweight` 抽象享元角色** 13 | 14 | 简单地说就是一个产品的抽象类,同时定义出对象的外部状态和内部状态的接口或实现。 15 | 16 | **`ConcreteFlyweight` 具体享元角色** 17 | 18 | 具体的一个产品类,实现抽象角色定义的业务。需要注意的是,内部状态处理应该与环境无关,不应该出现一个操作改变了内部状态,同时修改了外部状态,这是绝对不允许的。 19 | 20 | **`UnsharedConcreteFlyweight` 不可共享的享元角色** 21 | 22 | 不存在外部状态或者安全要求(如线程安全)不能够使用共享技术的对象,该对象一般不会出现在享元工厂中。 23 | 24 | **`FlyweightFactory` 享元工厂** 25 | 26 | 构造一个池容器,同时提供从池中获得对象的方法。 27 | 28 | #### 实现 29 | 30 | 抽象享元角色: 31 | ```java 32 | package com.sigalhu.flyweightpattern.impl; 33 | 34 | public abstract class Flyweight { 35 | //内部状态 36 | private String intrinsic; 37 | //外部状态 38 | protected final String extrinsic; 39 | 40 | //要求享元角色必须接受外部状态 41 | public Flyweight(String extrinsic){ 42 | this.extrinsic = extrinsic; 43 | } 44 | 45 | //定义业务操作 46 | public abstract void operate(); 47 | 48 | //内部状态的getter/setter 49 | public String getIntrinsic() { 50 | return intrinsic; 51 | } 52 | public void setIntrinsic(String intrinsic) { 53 | this.intrinsic = intrinsic; 54 | } 55 | } 56 | ``` 57 | 具体享元角色: 58 | ```java 59 | package com.sigalhu.flyweightpattern.impl; 60 | 61 | public class ConcreteFlyweight1 extends Flyweight { 62 | //接受外部状态 63 | public ConcreteFlyweight1(String extrinsic){ 64 | super(extrinsic); 65 | } 66 | 67 | //根据外部状态进行逻辑处理 68 | @Override 69 | public void operate() { 70 | //业务逻辑 71 | } 72 | } 73 | ``` 74 | ```java 75 | package com.sigalhu.flyweightpattern.impl; 76 | 77 | public class ConcreteFlyweight2 extends Flyweight { 78 | //接受外部状态 79 | public ConcreteFlyweight2(String extrinsic){ 80 | super(extrinsic); 81 | } 82 | 83 | //根据外部状态进行逻辑处理 84 | @Override 85 | public void operate() { 86 | //业务逻辑 87 | } 88 | } 89 | ``` 90 | 享元工厂: 91 | ```java 92 | package com.sigalhu.flyweightpattern.impl; 93 | 94 | import java.util.HashMap; 95 | 96 | public class FlyweightFactory { 97 | //定义一个池容器 98 | private static HashMap pool = new HashMap<>(); 99 | 100 | //享元工厂 101 | public static Flyweight getFlyweight(String extrinsic){ 102 | //需要返回的对象 103 | Flyweight flyweight = null; 104 | //在池中没有该对象 105 | if(pool.containsKey(extrinsic)){ 106 | flyweight = pool.get(extrinsic); 107 | } else { 108 | //根据外部状态创建享元对象 109 | flyweight = new ConcreteFlyweight1(extrinsic); 110 | //放置到池中 111 | pool.put(extrinsic, flyweight); 112 | } 113 | return flyweight; 114 | } 115 | } 116 | ``` 117 | 118 | #### 优缺点 119 | 120 | 享元模式是一个非常简单的模式,它可以大大减少应用程序创建的对象,降低程序内存的占用,增强程序的性能,但它同时也提高了系统复杂性,需要分离出外部状态和内部状态,而且外部状态具有固化特性,不应该随内部状态改变而改变,否则导致系统的逻辑混乱。 121 | 122 | #### 源码:https://github.com/SigalHu/DesignPatterns -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/23. 享元模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/23. 享元模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/24. 桥梁模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 将抽象和实现解耦,使得两者可以独立地变化。 4 | 5 | #### 类图 6 | 7 | ![](24.%20桥梁模式/1.png) 8 | 9 | **`Abstraction` 抽象化角色** 10 | 11 | 定义出该角色的行为,同时保存为一个对实现化角色的引用,一般是抽象类。 12 | 13 | **`Implementor` 实现化角色** 14 | 15 | 定义角色必需的行为和属性,一般是接口或抽象类。 16 | 17 | **`RefinedAbstraction` 修正抽象化角色** 18 | 19 | 引用实现化角色对抽象化角色进行修正。 20 | 21 | **`ConcreteImplementor` 具体实现化角色** 22 | 23 | 实现接口或抽象类定义的方法和属性。 24 | 25 | #### 实现 26 | 27 | 实现化角色: 28 | ```java 29 | package com.sigalhu.bridgepattern.impl; 30 | 31 | public interface Implementor { 32 | //基本方法 33 | public void doSomething(); 34 | public void doAnything(); 35 | } 36 | ``` 37 | 具体实现化角色: 38 | ```java 39 | package com.sigalhu.bridgepattern.impl; 40 | 41 | public class ConcreteImplementor1 implements Implementor { 42 | @Override 43 | public void doSomething() { 44 | //业务逻辑处理 45 | } 46 | 47 | @Override 48 | public void doAnything() { 49 | //业务逻辑处理 50 | } 51 | } 52 | ``` 53 | ```java 54 | package com.sigalhu.bridgepattern.impl; 55 | 56 | public class ConcreteImplementor2 implements Implementor { 57 | @Override 58 | public void doSomething() { 59 | //业务逻辑处理 60 | } 61 | 62 | @Override 63 | public void doAnything() { 64 | //业务逻辑处理 65 | } 66 | } 67 | ``` 68 | 抽象化角色: 69 | ```java 70 | package com.sigalhu.bridgepattern.impl; 71 | 72 | public abstract class Abstraction { 73 | //定义对实现化角色的引用 74 | private Implementor imp; 75 | 76 | //约束子类必须实现该构造函数 77 | public Abstraction(Implementor imp){ 78 | this.imp = imp; 79 | } 80 | 81 | //自身的行为和属性 82 | public void request(){ 83 | this.imp.doSomething(); 84 | } 85 | 86 | //获得实现化角色 87 | public Implementor getImp() { 88 | return imp; 89 | } 90 | } 91 | ``` 92 | 具体抽象化角色: 93 | ```java 94 | package com.sigalhu.bridgepattern.impl; 95 | 96 | public class RefinedAbstraction extends Abstraction { 97 | //覆写构造函数 98 | public RefinedAbstraction(Implementor imp){ 99 | super(imp); 100 | } 101 | 102 | //修正父类的行为 103 | @Override 104 | public void request() { 105 | //业务处理 106 | super.request(); 107 | super.getImp().doAnything(); 108 | } 109 | } 110 | ``` 111 | 场景类: 112 | ```java 113 | package com.sigalhu.bridgepattern.impl; 114 | 115 | public class Client { 116 | public static void main(String[] args){ 117 | //定义一个实现化角色 118 | Implementor imp = new ConcreteImplementor1(); 119 | //定义一个抽象化角色 120 | Abstraction abs = new RefinedAbstraction(imp); 121 | //执行行文 122 | abs.request(); 123 | } 124 | } 125 | ``` 126 | 127 | #### 优点 128 | 129 | * 抽象与实现分离,实现可以不受抽象的约束,不用再绑定在一个固定的抽象层次上; 130 | * 优秀的扩充能力,只要对外暴露的接口允许增加实现或抽象,其变化的可能性已经被减到最小; 131 | * 实现细节对客户透明,客户不用关心细节的实现,它已经由抽象层通过聚合关系完成了封装。 132 | 133 | #### 注意 134 | 135 | 使用桥梁模式时主要考虑如何拆分抽象和实现,并不是一涉及到继承就要考虑使用该模式。桥梁模式的意图是对变化的封装,尽量把可能变化的因素封装到最细、最小的逻辑单元中,避免风险扩散。 136 | 137 | #### 源码:https://github.com/SigalHu/DesignPatterns -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/24. 桥梁模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/24. 桥梁模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/25. 创建类模式比较.md: -------------------------------------------------------------------------------- 1 | 创建类模式包括[工厂方法模式](3.%20工厂方法模式.md)、[建造者模式](6.%20建造者模式.md)、[抽象工厂模式](4.%20抽象工厂模式.md)、[单例模式](2.%20单例模式.md)和[原型模式](8.%20原型模式.md),它们都能够提供对象的创建和管理职责。其中,单例模式是要保持在内存中只有一个对象,原型模式是要求通过复制的方式产生一个新的对象,这两个不容易混淆,剩下的工厂方法模式、抽象工厂模式和建造者模式有较多的相似性。 2 | 3 | #### 工厂方法模式与建造者模式 4 | 5 | 工厂方法模式注重的是整体对象的创建方法,生产出的产品应该具有相似的功能和架构,而不需要考虑具体怎么生产、怎么组装。 6 | 7 | 建造者模式注重的是部件构建的过程,旨在通过一步一步地精确构造创建出一个复杂对象。建造者模式关注产品的各个部分,在某些应用场景下甚至会关心产品的构建顺序,即使是相同的部件,装配顺序不同,产生的结果也不同,简而言之,建造者模式可以通过不同的部件、不同装配产生不同的复杂对象。 8 | 9 | **区别** 10 | 11 | * 意图不同。工厂方法模式关注的是一个产品的整体,无须关心产品的各部分是如何创建出来的,建造者模式关注的是产品组成部分的创建过程; 12 | * 产品的复杂度不同。工厂方法模式创建的产品一般都是单一性质的产品,而建造者模式创建的是一个复合产品,由各个部件复合而成,部件不同产品对象当然不同。 13 | 14 | #### 抽象工厂模式与建造者模式 15 | 16 | 抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合。抽象工厂模式不关心构建过程,只关心什么产品由什么工厂生产即可。 17 | 18 | 建造者模式要求按照指定的蓝图建造产品,其主要目的是通过组装零件配件而产生一个新产品。 19 | 20 | **区别** 21 | 22 | 抽象工厂模式比建造者模式的尺度要大,它关注产品整体,而建造者模式关注构建过程,因此只要能够提供具体的工艺流程,建造者模式可以很容易地构建出一个崭新的产品。 23 | 24 | 如果希望屏蔽对象的创建过程,只提供一个封装良好的对象,则可以选择抽象工厂模式,而建造者模式可以用在构件的装配方面,如通过装配不同的组件或者相同的组件的不同顺序,可以产生出一个新的对象,它可以产生一个非常灵活的架构,方便地扩展和维护系统。 -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/26. 结构类模式比较.md: -------------------------------------------------------------------------------- 1 | 结构类模式包括[适配器模式](14.%20适配器模式.md)、[桥梁模式](24.%20桥梁模式.md)、[组合模式](16.%20组合模式.md)、[装饰模式](12.%20装饰模式.md)、[门面模式](18.%20门面模式.md)、[享元模式](23.%20享元模式.md)和[代理模式](7.%20代理模式.md)。它们都是通过组合类或对象产生更大结构以适应更高层次的逻辑需求。 2 | 3 | #### 代理模式与装饰模式 4 | 5 | 装饰模式就是代理模式的一个特殊应用,两者的共同点是都具有相同的接口,不同点则是代理模式着重对代理过程的控制,而装饰模式则是对类的功能进行加强或减弱,它着重类的功能变化。 6 | 7 | 代理模式是把当前的行为或功能委托给其他对象执行,代理类负责接口限定:是否可以调用真实角色,以及是否对发送到真实角色的消息进行变形处理,它不对被代理类的功能进行任何处理,保证原汁原味的调用。 8 | 9 | 装饰模式是在要保证接口不变的情况下加强类的功能,它保证的是被修饰的对象功能比原始对象丰富(当然,也可以减弱),但不做准入条件判断和准入参数过滤,如是否可以执行类的功能,过滤输入参数是否合规等。 10 | 11 | #### 装饰模式与适配器模式 12 | 13 | 装饰模式与适配器模式的功能相似:都是包装作用,都是通过委托方式实现其功能。不同点是:装饰模式包装的是自己的兄弟类,隶属于同一个家族(相同接口或父类),适配器模式则修饰非血缘关系类,把一个非本家族的对象伪装成本家族的对象,注意是伪装,因此它的本质还是非相同接口的对象。 14 | 15 | **区别** 16 | 17 | * 意图不同。装饰模式的意图是加强对象的功能,它不改变类的行为和属性,只是增加或减弱功能;而适配器模式关注的则是转化,它的主要意图是两个不同对象之间的转化。 18 | * 施与对象不同。装饰模式装饰的对象必须是相同的接口或父类,只要在具有相同的属性和行为的情况下,才能比较行为是增加还是减弱;适配器模式则必须是两个不同的对象,因为它着重于转换,只有两个不同的对象才有转换的必要。 19 | * 场景不同。装饰模式在任何时候都可以使用,只要是想增强类的功能;而适配器模式则是一个补救模式,一般出现在系统成熟或已经构建完毕的项目中,作为一个紧急处理手段使用。 20 | * 扩展性不同。装饰模式很容易扩展,但适配器模式就不同了,它在两个不同对象之间架起了一座沟通的桥梁,建立容易,去掉就比较困难了,需要从系统整体考虑是否能够撤销。 -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/27. 行为类模式比较.md: -------------------------------------------------------------------------------- 1 | 行为类模式包括[责任链模式](11.%20责任链模式.md)、[命令模式](10.%20命令模式.md)、[解释器模式](22.%20解释器模式.md)、[迭代器模式](15.%20迭代器模式.md)、[中介者模式](9.%20中介者模式.md)、[备忘录模式](19.%20备忘录模式.md)、[观察者模式](17.%20观察者模式.md)、[状态模式](21.%20状态模式.md)、[策略模式](13.%20策略模式.md)、[模板方法模式](5.%20模板方法模式.md)、[访问者模式](20.%20访问者模式.md)。 2 | 3 | #### 策略模式与命令模式 4 | 5 | 策略模式的意图是封装算法,它认为“算法”已经是一个完整的、不可拆分的原子业务,即其意图是让这些算法独立,并且可以相互替换,让行为的变化独立于拥有行为的客户;而命令模式则是对动作的解耦,把一个动作的执行分为执行对象、执行行为,让两者相互独立而不相互影响。 6 | 7 | **区别** 8 | 9 | * 关注点不同。策略模式关注的是算法的完整性、封装性,只有具备了这两个条件才能保证其可以自由切换;命令模式则关注的是解耦问题。 10 | * 角色功能不同。策略模式中的具体算法是负责一个完整算法逻辑,它是不可拆分的原子业务单元,一旦变更就是对算法整体的变更;而命令模式关注的是命令的实现,也就是功能的实现。 11 | * 使用场景不同。策略模式适用于算法要求变换的场景;而命令模式适用于解耦两个有紧耦合关系的对象场合或者多命令多撤销的场景。 12 | 13 | #### 策略模式与状态模式 14 | 15 | **策略模式通用类图** 16 | 17 | ![](27.%20行为类模式比较/1.png) 18 | 19 | **状态模式通用类图** 20 | 21 | ![](27.%20行为类模式比较/2.png) 22 | 23 | 策略模式与状态模式都是通过`Context`类封装一个具体的行为,都提供了一个封装的方法,是高扩展性的设计模式。但策略模式封装的是不同的算法,算法之间没有交互,以达到算法可以自由切换的目的;而状态模式封装的是不同的状态,以达到状态切换行为随之发生改变的目的。 24 | 25 | **区别** 26 | 27 | * 环境角色的职责不同。策略模式的环境角色只是一个委托作用,负责算法的替换;而状态模式的环境角色不仅仅是委托行为,它还具有登记状态变化的功能,与具体的状态类协作,共同完成状态切换行为随之切换的任务。 28 | * 解决问题的重点不同。策略模式将内部算法的改变对外界的影响降低到最小,它保证的是算法可以自由地切换;而状态模式旨在解决内在状态的改变而引起行为改变的问题。 29 | * 解决问题的方法不同。策略模式只是确保算法可以自由切换,但是什么时候用什么算法它决定不了;而状态模式封装了状态的变化而暴露了不同的行为或行为结果。 30 | * 应用场景不同。策略模式只是一个算法的封装,可以是一个有意义的对象,也可以是一个无意义的逻辑片段,它是一系列平行的、可相互替换的算法封装后的结果;状态模式则要求有一系列状态发生变化的场景,它要求的是有状态且有行为的场景,也就是一个对象必须具有二维(状态和行为)描述才能采用状态模式,如果只有状态而没有行为,则状态的变化就失去意义。 31 | * 复杂度不同。通常策略模式比较简单,这里的简单指的是结构简单,扩展比较容易,而且代码也容易阅读;而状态模式则通常比较复杂,因为它要从两个角色看到一个对象状态和行为的改变。 32 | 33 | #### 观察者模式与责任链模式 34 | 35 | 在观察者模式中,一个具体的角色既可以是观察者,也可以是被观察者,这样就形成了一个观察者链。这与责任链模式非常类似,它们都实现了事务的链条化处理。 36 | 37 | **区别** 38 | 39 | * 链中的消息对象不同。责任链模式基本上不改变消息对象的结构,虽然每个节点都可以参与消费(一般是不参与消费);但是在触发链模式中,链中传递的对象可以自由变化,只要上下级节点对传递对象了解即可,它不要求链中的消息对象不变化,它只要求链中相邻两个节点的消息对象固定。 40 | * 上下节点的关系不同。在责任链模式中,上下节点没有关系,都是接收同样的对象,所有传递的对象都是从链首传递过来,上一节点是什么没有关系,只要按照自己的逻辑处理就成;而触发链模式的上下级关系很亲密,链中的任意两个相邻节点都是一个牢固的独立团体。 41 | * 消息的分销渠道不同。在责任链模式中,一个消息从链首传递进来后,就开始沿着链条向链尾运动,方向是单一的、固定的;而触发链模式由于采用观察者模式,具有非常大的灵活性,一个消息传递到链首后,具体怎么传递是不固定的,可以以广播方式传递,也可以以跳跃方式传递,这取决于处理消息的逻辑。 -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/27. 行为类模式比较/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/27. 行为类模式比较/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/27. 行为类模式比较/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/27. 行为类模式比较/2.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/3. 工厂方法模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 工厂方法模式一般定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。 4 | 5 | #### 类图 6 | 7 | ![](3.%20工厂方法模式/1.png) 8 | 9 | 在工厂方法模式中,抽象产品类`Product`负责定义产品的共性,实现对事物最抽象的定义;`Creator`为抽象创建类,也就是抽象工厂,具体如何创建产品类是由具体的实现工厂`ConcreteCreator`完成的。 10 | 11 | #### 实现 12 | 13 | 抽象产品类: 14 | ```java 15 | package com.sigalhu.factorymethod.impl; 16 | 17 | public abstract class Product { 18 | //产品类的公共方法 19 | public void method1(){ 20 | //业务逻辑处理 21 | } 22 | //抽象方法 23 | public abstract void method2(); 24 | } 25 | ``` 26 | 具体产品类: 27 | ```java 28 | package com.sigalhu.factorymethod.impl; 29 | 30 | public class ConcreteProduct1 extends Product { 31 | @Override 32 | public void method2() { 33 | //业务逻辑处理 34 | } 35 | } 36 | ``` 37 | ```java 38 | package com.sigalhu.factorymethod.impl; 39 | 40 | public class ConcreteProduct2 extends Product { 41 | @Override 42 | public void method2() { 43 | //业务逻辑处理 44 | } 45 | } 46 | ``` 47 | 抽象工厂类: 48 | ```java 49 | package com.sigalhu.factorymethod.impl; 50 | 51 | public abstract class Creator { 52 | //创建一个产品对象,其输入参数类型可以自行设置 53 | public abstract T createProduct(Class c); 54 | } 55 | ``` 56 | 具体工厂类: 57 | ```java 58 | package com.sigalhu.factorymethod.impl; 59 | 60 | public class ConcreateCreator extends Creator { 61 | @Override 62 | public T createProduct(Class c) { 63 | Product product = null; 64 | try{ 65 | product = (Product)Class.forName(c.getName()).newInstance(); 66 | }catch (Exception e){ 67 | //异常处理 68 | } 69 | return (T)product; 70 | } 71 | } 72 | ``` 73 | 场景类: 74 | ```java 75 | package com.sigalhu.factorymethod.impl; 76 | 77 | public class Client { 78 | public static void main(String[] args){ 79 | Creator creator = new ConcreateCreator(); 80 | Product product = creator.createProduct(ConcreteProduct1.class); 81 | //继续业务处理 82 | } 83 | } 84 | ``` 85 | 86 | #### 优点 87 | 88 | * 具有良好的封装性,代码结构清晰。只要知道产品的类名,而不需要知道创建对象的过程,降低模块间的耦合; 89 | * 具有很好的扩展性。在增加产品类的情况下,只要适当地修改具体的工厂类或扩展一个工厂类,就可以完成“拥抱变化”; 90 | * 屏蔽产品类。产品类的实现如何变化,调用者都不需要关心,它只需要关心产品的接口,只要接口保持不变,系统中的上层模块就不需要变化; 91 | * 该模式是典型的解耦框架。高层模块只需要知道产品的抽象类,其他的实现类都不用关心,符合最少知识原则;只依赖产品类的抽象,符合依赖倒置原则;可以使用产品子类替换产品父类,符合里式替换原则。 92 | 93 | #### 源码:https://github.com/SigalHu/DesignPatterns -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/3. 工厂方法模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/3. 工厂方法模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/4. 抽象工厂模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 抽象工厂模式要求为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类。 4 | 5 | #### 类图 6 | 7 | ![](4.%20抽象工厂模式/1.png) 8 | 9 | #### 实现 10 | 11 | 抽象产品类: 12 | ```java 13 | package com.sigalhu.abstractfactory.impl; 14 | 15 | public abstract class AbstractProductA { 16 | //每个产品共有的方法 17 | public void shareMethod(){ 18 | } 19 | 20 | //每个产品相同方法,不同实现 21 | public abstract void doSomething(); 22 | } 23 | ``` 24 | ```java 25 | package com.sigalhu.abstractfactory.impl; 26 | 27 | public abstract class AbstractProductB { 28 | //每个产品共有的方法 29 | public void shareMethod(){ 30 | } 31 | 32 | //每个产品相同方法,不同实现 33 | public abstract void doSomething(); 34 | } 35 | ``` 36 | 具体产品类: 37 | ```java 38 | package com.sigalhu.abstractfactory.impl; 39 | 40 | public class ProductA1 extends AbstractProductA { 41 | @Override 42 | public void doSomething() { 43 | System.out.println("产品A1的实现方法"); 44 | } 45 | } 46 | ``` 47 | ```java 48 | package com.sigalhu.abstractfactory.impl; 49 | 50 | public class ProductA2 extends AbstractProductA { 51 | @Override 52 | public void doSomething() { 53 | System.out.println("产品A2的实现方法"); 54 | } 55 | } 56 | ``` 57 | ```java 58 | package com.sigalhu.abstractfactory.impl; 59 | 60 | public class ProductB1 extends AbstractProductB { 61 | @Override 62 | public void doSomething() { 63 | System.out.println("产品B1的实现方法"); 64 | } 65 | } 66 | ``` 67 | ```java 68 | package com.sigalhu.abstractfactory.impl; 69 | 70 | public class ProductB2 extends AbstractProductB { 71 | @Override 72 | public void doSomething() { 73 | System.out.println("产品B2的实现方法"); 74 | } 75 | } 76 | ``` 77 | 抽象工厂类: 78 | ```java 79 | package com.sigalhu.abstractfactory.impl; 80 | 81 | public abstract class AbstractCreator { 82 | //创建A产品家族 83 | public abstract AbstractProductA createProductA(); 84 | 85 | //创建B产品家族 86 | public abstract AbstractProductB createProductB(); 87 | } 88 | ``` 89 | 产品等级1的实现类: 90 | ```java 91 | package com.sigalhu.abstractfactory.impl; 92 | 93 | public class Creator1 extends AbstractCreator { 94 | //只生产产品等级为1的A产品 95 | @Override 96 | public AbstractProductA createProductA() { 97 | return new ProductA1(); 98 | } 99 | 100 | //只生产产品等级为1的B产品 101 | @Override 102 | public AbstractProductB createProductB() { 103 | return new ProductB1(); 104 | } 105 | } 106 | ``` 107 | 产品等级2的实现类: 108 | ```java 109 | package com.sigalhu.abstractfactory.impl; 110 | 111 | public class Creator2 extends AbstractCreator { 112 | //只生产产品等级为2的A产品 113 | @Override 114 | public AbstractProductA createProductA() { 115 | return new ProductA2(); 116 | } 117 | 118 | //只生产产品等级为2的B产品 119 | @Override 120 | public AbstractProductB createProductB() { 121 | return new ProductB2(); 122 | } 123 | } 124 | ``` 125 | 场景类: 126 | ```java 127 | package com.sigalhu.abstractfactory.impl; 128 | 129 | public class Client { 130 | public static void main(String[] args){ 131 | //定义出两个工厂 132 | AbstractCreator creator1 = new Creator1(); 133 | AbstractCreator creator2 = new Creator2(); 134 | //产生A1的对象 135 | AbstractProductA a1 = creator1.createProductA(); 136 | a1.doSomething(); 137 | //产生A2的对象 138 | AbstractProductA a2 = creator2.createProductA(); 139 | a2.doSomething(); 140 | //产生B1的对象 141 | AbstractProductB b1 = creator1.createProductB(); 142 | b1.doSomething(); 143 | //产生B2的对象 144 | AbstractProductB b2 = creator2.createProductB(); 145 | b2.doSomething(); 146 | } 147 | } 148 | ``` 149 | 150 | #### 优点 151 | 152 | * 封装性。高层模块不关心每个产品的实现类,也不关心对象是如何创建出来的,只要知道工厂类就能创建一个需要的对象; 153 | * 产品族内的约束为非公开状态。具体的产品族内的约束是在工厂内实现的,生产过程对调用工厂类的高层模块来说是透明的,它不需要知道这个约束。 154 | 155 | #### 缺点 156 | 157 | 产品族扩展非常困难。如果需要增加一个产品,需要修改抽象工厂类,严重违反了开闭原则,抽象类与接口是一个契约,不应该被修改。虽然产品族扩展困难,但产品等级非常容易扩展,增加一个产品等级,只要增加一个工厂类负责新增加出来的产品生产任务即可。 158 | 159 | #### 源码:https://github.com/SigalHu/DesignPatterns -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/4. 抽象工厂模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/4. 抽象工厂模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/5. 模板方法模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 模板方法模式要求定义一个操作中的算法框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。通常,算法框架可使用抽象类定义,其中,可以有一个或几个实现对基本方法进行调度的具体方法,也就是一个框架,叫做模板方法;而由子类实现,并被模板方法调用的方法叫做基本方法。 4 | 5 | #### 类图 6 | 7 | ![](5.%20模板方法模式/1.png) 8 | 9 | #### 实现 10 | 11 | 抽象模板类: 12 | ```java 13 | package com.sigalhu.templatemethod.impl; 14 | 15 | public abstract class AbstractClass { 16 | //基本方法 17 | protected abstract void doSomething(); 18 | 19 | //基本方法 20 | protected abstract void doAnything(); 21 | 22 | //模板方法 23 | public void templateMethod(){ 24 | //调度基本方法,完成相关逻辑 25 | this.doAnything(); 26 | this.doSomething(); 27 | } 28 | } 29 | ``` 30 | 具体模板类: 31 | ```java 32 | package com.sigalhu.templatemethod.impl; 33 | 34 | public class ConcreteClass1 extends AbstractClass{ 35 | //实现基本方法 36 | @Override 37 | protected void doAnything() { 38 | //业务逻辑处理 39 | } 40 | 41 | @Override 42 | protected void doSomething() { 43 | //业务逻辑处理 44 | } 45 | } 46 | ``` 47 | ```java 48 | package com.sigalhu.templatemethod.impl; 49 | 50 | public class ConcreteClass2 extends AbstractClass{ 51 | //实现基本方法 52 | @Override 53 | protected void doAnything() { 54 | //业务逻辑处理 55 | } 56 | 57 | @Override 58 | protected void doSomething() { 59 | //业务逻辑处理 60 | } 61 | } 62 | ``` 63 | 场景类: 64 | ```java 65 | package com.sigalhu.templatemethod.impl; 66 | 67 | public class Client { 68 | public static void main(String[] args){ 69 | AbstractClass class1 = new ConcreteClass1(); 70 | AbstractClass class2 = new ConcreteClass2(); 71 | //调用模板方法 72 | class1.templateMethod(); 73 | class2.templateMethod(); 74 | } 75 | } 76 | ``` 77 | 78 | #### 优点 79 | 80 | * 封装不变部分,扩展可变部分。把认为是不变部分的算法封装到父类实现,而可变部分的则可以通过继承来继续扩展; 81 | * 提取公共部分,便于维护; 82 | * 行为由父类控制,子类实现,因此子类可以通过扩展的方式增加相应的功能,符合开闭原则。 83 | 84 | #### 缺点 85 | 86 | 在模板方法模式中,抽象类定义了部分抽象方法,由子类实现,子类执行的结构影响了父类的结果,也就是子类对父类产生了影响,这在复杂的项目中,会带来代码阅读的难度。 87 | 88 | #### 源码:https://github.com/SigalHu/DesignPatterns -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/5. 模板方法模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/5. 模板方法模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/6. 建造者模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 建造者模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 4 | 5 | #### 类图 6 | 7 | ![](6.%20建造者模式/1.png) 8 | 9 | **`Product` 产品类** 10 | 11 | 通常实现了模板方法模式,也就是有模板方法和基本方法。 12 | 13 | **`Builder` 抽象建造者** 14 | 15 | 规范产品的组建,一般是由子类实现。 16 | 17 | **`ConcreteBuilder` 具体建造者** 18 | 19 | 实现抽象类定义的所有方法,并返回一个组建好的对象。 20 | 21 | **`Director` 导演类** 22 | 23 | 负责安排已有模板的顺序,然后告诉`Builder`开始建造。 24 | 25 | #### 实现 26 | 27 | 产品类: 28 | ```java 29 | package com.sigalhu.builderpattern.impl; 30 | 31 | public class Product { 32 | public void doSomething(){ 33 | //独立业务处理 34 | } 35 | } 36 | ``` 37 | 抽象建造者: 38 | ```java 39 | package com.sigalhu.builderpattern.impl; 40 | 41 | public abstract class Builder { 42 | //设置产品的不同部分,已获得不同产品 43 | public abstract void setPart(); 44 | 45 | //建造产品 46 | public abstract Product buildProduct(); 47 | } 48 | ``` 49 | 具体建造者: 50 | ```java 51 | package com.sigalhu.builderpattern.impl; 52 | 53 | public class ConcreteBuilder extends Builder { 54 | private Product product = new Product(); 55 | 56 | //设置产品零件 57 | @Override 58 | public void setPart() { 59 | //产品类内的逻辑处理 60 | } 61 | 62 | //组建一个产品 63 | @Override 64 | public Product buildProduct() { 65 | return product; 66 | } 67 | } 68 | ``` 69 | 导演类: 70 | ```java 71 | package com.sigalhu.builderpattern.impl; 72 | 73 | public class Director { 74 | private Builder builder = new ConcreteBuilder(); 75 | 76 | //构建不同的产品 77 | public Product getAProduct(){ 78 | builder.setPart(); 79 | //设置不同的零件,产生不同的产品 80 | return builder.buildProduct(); 81 | } 82 | } 83 | ``` 84 | 85 | #### 优点 86 | 87 | * 封装性。使用建造者模式可以使客户端不必知道产品内部组成的细节; 88 | * 建造者独立,容易扩展; 89 | * 便于控制细节风险,可以对建造过程逐步细化,而不对其他的模块产生任何影响。 90 | 91 | #### 注意 92 | 93 | 建造者模式最主要的功能是基本方法的调用顺序安排,也就是这些基本方法已经实现了,顺序不同产生的对象也不同;而工厂方法模式则重点是创建,创建零件是它的主要职责,组装顺序则不是它关心的。 94 | 95 | #### 源码:https://github.com/SigalHu/DesignPatterns -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/6. 建造者模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/6. 建造者模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/7. 代理模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/7. 代理模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/7. 代理模式/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/7. 代理模式/2.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/8. 原型模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 原型模式是指用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,简而言之,就是不通过`new`关键字来产生一个对象,而是通过对象复制来实现。 4 | 5 | #### 类图 6 | 7 | ![](8.%20原型模式/1.png) 8 | 9 | #### 实现 10 | 11 | 实现一个接口,然后重写`clone`方法,就完成了原型模式: 12 | 13 | ```java 14 | package com.sigalhu.prototypepattern.impl; 15 | 16 | public class PrototypeClass implements Cloneable{ 17 | //覆写父类Object方法 18 | @Override 19 | protected PrototypeClass clone(){ 20 | PrototypeClass prototypeClass = null; 21 | try{ 22 | prototypeClass = (PrototypeClass)super.clone(); 23 | } catch (CloneNotSupportedException e){ 24 | //异常处理 25 | } 26 | return prototypeClass; 27 | } 28 | } 29 | ``` 30 | 31 | #### 优点 32 | 33 | * 性能优良。原型模式是在内存二进制流的拷贝,要比直接`new`一个对象性能好很多; 34 | * 逃避构造函数的约束。由于直接在内存中拷贝,因此不会执行构造函数。 35 | 36 | #### 注意 37 | 38 | * 对象拷贝时构造函数不会执行; 39 | * `Object`类提供的`clone`方法只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址; 40 | * 要使用`clone`方法,类的成员变量上不要增加`final`关键字。 41 | 42 | #### 源码:https://github.com/SigalHu/DesignPatterns -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/8. 原型模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/8. 原型模式/1.png -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/9. 中介者模式.md: -------------------------------------------------------------------------------- 1 | #### 定义 2 | 3 | 中介者模式用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。 4 | 5 | #### 类图 6 | 7 | ![](9.%20中介者模式/1.png) 8 | 9 | **`Mediator` 抽象中介者角色** 10 | 11 | 抽象中介者角色定义统一的接口,用于各同事角色之间的通信。 12 | 13 | **`ConcreteMediator` 具体中介者角色** 14 | 15 | 具体中介者角色通过协调各同事角色实现协作行为,因此它必须依赖于各个同事角色。 16 | 17 | **`Colleague` 同事角色** 18 | 19 | 每一个同事角色都知道中介者角色,而且与其他的同事角色通信的时候,一定要通过中介者角色协作。 20 | 21 | 每个同事类的行为分为两种:一种是同事本身的行为,与其他同事类或中介者没有任何的依赖,叫做自发行为;第二种是必须依赖中介者才能完成的行为,叫做依赖方法。 22 | 23 | #### 实现 24 | 25 | 通用抽象中介者: 26 | ```java 27 | package com.sigalhu.mediatorpattern.impl; 28 | 29 | public abstract class Mediator { 30 | //定义同事类 31 | protected ConcreteColleague1 c1; 32 | protected ConcreteColleague2 c2; 33 | 34 | //通过getter/setter方法把同事类注入进来 35 | public ConcreteColleague1 getC1() { 36 | return c1; 37 | } 38 | public void setC1(ConcreteColleague1 c1) { 39 | this.c1 = c1; 40 | } 41 | public ConcreteColleague2 getC2() { 42 | return c2; 43 | } 44 | public void setC2(ConcreteColleague2 c2) { 45 | this.c2 = c2; 46 | } 47 | 48 | //中介者模式的业务逻辑 49 | public abstract void doSometing1(); 50 | public abstract void doSometing2(); 51 | } 52 | ``` 53 | 通用中介者: 54 | ```java 55 | package com.sigalhu.mediatorpattern.impl; 56 | 57 | public class ConcreteMediator extends Mediator { 58 | @Override 59 | public void doSometing1() { 60 | //调用同事类的方法,只要是public方法都可以调用 61 | super.c1.selfMethod1(); 62 | super.c2.selfMethod2(); 63 | } 64 | 65 | @Override 66 | public void doSometing2() { 67 | //调用同事类的方法,只要是public方法都可以调用 68 | super.c1.selfMethod1(); 69 | super.c2.selfMethod2(); 70 | } 71 | } 72 | ``` 73 | 抽象同事类: 74 | ```java 75 | package com.sigalhu.mediatorpattern.impl; 76 | 77 | public abstract class Colleague { 78 | protected Mediator mediator; 79 | public Colleague(Mediator _mediator){ 80 | this.mediator = _mediator; 81 | } 82 | } 83 | ``` 84 | 具体同事类: 85 | ```java 86 | package com.sigalhu.mediatorpattern.impl; 87 | 88 | public class ConcreteColleague1 extends Colleague { 89 | //通过构造函数传递中介者 90 | public ConcreteColleague1(Mediator _mediator){ 91 | super(_mediator); 92 | } 93 | 94 | //自有方法self-method 95 | public void selfMethod1(){ 96 | //处理自己的业务逻辑 97 | } 98 | 99 | //依赖方法dep-method 100 | public void depMethod1(){ 101 | //处理自己的业务逻辑 102 | //自己不能处理的业务逻辑,委托给中介者处理 103 | super.mediator.doSometing1(); 104 | } 105 | } 106 | ``` 107 | ```java 108 | package com.sigalhu.mediatorpattern.impl; 109 | 110 | public class ConcreteColleague2 extends Colleague{ 111 | //通过构造函数传递中介者 112 | public ConcreteColleague2(Mediator _mediator){ 113 | super(_mediator); 114 | } 115 | 116 | //自有方法self-method 117 | public void selfMethod2(){ 118 | //处理自己的业务逻辑 119 | } 120 | 121 | //依赖方法dep-method 122 | public void depMethod2(){ 123 | //处理自己的业务逻辑 124 | //自己不能处理的业务逻辑,委托给中介者处理 125 | super.mediator.doSometing2(); 126 | } 127 | } 128 | ``` 129 | 130 | #### 优点 131 | 132 | 减少类间的依赖,把原有的一对多的依赖变成了一对一的依赖,同事类只依赖中介者,减少了依赖,当然同时也降低了类间的耦合。 133 | 134 | #### 缺点 135 | 136 | 中介者会膨胀得很大,且逻辑复杂,原本多个对象直接的相互依赖关系转换成中介者和同事类的依赖关系,同事类越多,中介者的逻辑就越复杂。 137 | 138 | #### 注意 139 | 140 | 中介者模式适用于多个对象之间紧密耦合的情况,紧密耦合的标准是:在类图中出现了蜘蛛网状结构。在这种情况下一定要考虑使用中介者模式,这有利于把蜘蛛网梳理为星型结构,使原本复杂混乱的关系变得清晰简单。 141 | 142 | #### 源码:https://github.com/SigalHu/DesignPatterns -------------------------------------------------------------------------------- /读书笔记/《设计模式之禅》读书笔记/9. 中介者模式/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SigalHu/MyBlog/7246176f7c42bca5e6d96a03a45e876672f8b422/读书笔记/《设计模式之禅》读书笔记/9. 中介者模式/1.png --------------------------------------------------------------------------------