├── README.md ├── SUMMARY.md ├── cover.jpg ├── cover_small.jpg ├── database ├── README.md ├── orm.md └── transaction.md ├── img └── pattern │ ├── abstract_factory.PNG │ ├── adapter_class.PNG │ ├── adapter_interface.PNG │ ├── adapter_object.PNG │ ├── bridge1.PNG │ ├── bridge2.PNG │ ├── chain_of_responsibility.PNG │ ├── command.PNG │ ├── composite.PNG │ ├── decorator.PNG │ ├── facade.PNG │ ├── flyweight.PNG │ ├── flyweight2.PNG │ ├── iterator.PNG │ ├── observer.PNG │ ├── proxy.PNG │ ├── simple_factory.PNG │ └── template_method.PNG ├── java ├── README.md ├── collection.md ├── collection │ └── README.md ├── io.md ├── io │ ├── README.md │ ├── bio-nio-aio.md │ ├── direct-buffer.md │ ├── nio.md │ └── read-ways.md ├── java-model.md ├── javaBasic │ ├── README.md │ ├── collection.md │ ├── collection │ │ ├── README.md │ │ └── map │ │ │ ├── README.md │ │ │ ├── code-hashmap-hashtable.md │ │ │ ├── compare.md │ │ │ ├── concurrent-hash-map.md │ │ │ ├── how-to-syn-hashmap.md │ │ │ ├── linked-hash-map-code.md │ │ │ ├── linkedhashmap.md │ │ │ └── red-tredd-balanced-tree.md │ ├── others │ │ ├── README.md │ │ ├── basic-type-transfer.md │ │ ├── final-finally-finalize.md │ │ ├── interface-abstract.md │ │ ├── override.md │ │ └── static.md │ ├── platform.md │ ├── platform │ │ ├── README.md │ │ ├── bytecode.md │ │ ├── difference-jdk-jre.md │ │ └── prnciple-platform.md │ ├── reflection.md │ ├── reflection │ │ ├── README.md │ │ ├── create-object.md │ │ ├── scene.md │ │ └── tuning.md │ ├── string │ │ ├── README.md │ │ ├── interview.md │ │ └── string-stringbuffer-stringbuilder.md │ ├── throwable.md │ └── throwable │ │ ├── README.md │ │ ├── compare-c.md │ │ └── finally.md ├── jvm.md ├── jvm │ ├── Class-loading-mechanism.md │ ├── GC-analysis.md │ ├── README.md │ ├── gc-algorithm-garbage.md │ ├── java-model.md │ └── jvm-Memory-structure.md ├── network-communication │ ├── README.md │ ├── http │ │ ├── README.md │ │ ├── cache.md │ │ ├── communication.md │ │ ├── cookie-session.md │ │ ├── get-post.md │ │ ├── https.md │ │ ├── partial-content.md │ │ ├── status-code.md │ │ ├── url-uri.md │ │ └── version.md │ ├── serizalizable.md │ ├── serizalizable │ │ ├── README.md │ │ ├── interview.md │ │ ├── java-serizaliable.md │ │ └── transient.md │ ├── tcp-udp.md │ └── tcp-udp │ │ ├── README.md │ │ ├── non-block-socket.md │ │ ├── socket.md │ │ ├── tcp-handshake.md │ │ └── tcp-transfer-status.md ├── spring.md ├── spring │ └── README.md └── thread │ ├── README.md │ ├── interview │ ├── README.md │ ├── communication.md │ ├── exception.md │ ├── others.md │ ├── philosopher.md │ ├── produce-consume.md │ └── thread-pool.md │ ├── multi-thread │ ├── README.md │ ├── concurrent.md │ ├── pool.md │ └── syn-lock.md │ ├── security │ ├── README.md │ ├── atomic-visible.md │ ├── happen-before.md │ └── volatile.md │ └── thread-process.md ├── middleware ├── README.md └── redis │ └── README.md └── pattern ├── README.md ├── base ├── README.md └── principles.md ├── behavioral ├── README.md ├── chain_of_responsibility.md ├── command.md ├── iterator.md ├── observer.md ├── strategy.md └── template_method.md ├── creational ├── README.md ├── builder.md ├── factory │ ├── README.md │ ├── abstract.md │ └── simple.md ├── prototype.md └── singleton │ ├── README.md │ ├── disadvan.md │ └── impl.md └── structural ├── README.md ├── adapter ├── README.md ├── class.md ├── interface.md └── object.md ├── bridge.md ├── composite.md ├── decorator.md ├── facade.md ├── flyweight.md ├── proxy.md └── proxy └── README.md /README.md: -------------------------------------------------------------------------------- 1 | # IT Interview 2 | 3 | ### [阅读地址](https://justdojava.gitbooks.io/it-interview) 4 | 5 | ### [Github托管地址](https://github.com/justdojava/it-interview) 6 | 7 | --- 8 | 9 | 准备找时间整理一下IT技术栈,大概分几块: 10 | 11 | * Java相关 12 | * 数据库 13 | * 设计模式 14 | * 中间件 15 | 16 | --- 17 | 18 | ### 如何参与 19 | 20 | 欢迎大家一起将此文档维护好,造福同行。大家如果有想法可以一起参与,会开放权限给每一个加入的伙伴 ,任何问题都欢迎直接联系我 ityouknow@126.com 21 | 22 | Gitbook 提供了非常棒的在线编辑功能, 所以想贡献的同学可以直接联系我申请权限! 23 | 24 | --- 25 | 26 | ### 许可证 27 | 28 | 本作品采用 Apache License 2.0 国际许可协议 进行许可. 传播此文档时请注意遵循以上许可协议. 关于本许可证的更多详情可参考[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) 29 | 30 | --- 31 | 32 | ### 贡献者列表 33 | 34 | | 成员 | 联系方式 | Github | 35 | | :--- | :--- | :--- | 36 | | ityouknow | ityouknow@126.com | [Github](https://github.com/justdojava/it-interview) | 37 | 38 | > 本书绝大多数内容来源于互联网整理于此,如有侵权请联系ityouknow@126.com 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [关于](README.md) 4 | * [java](java/README.md) 5 | * [Java基础](java/javaBasic/README.md) 6 | * [String](java/javaBasic/string/README.md) 7 | * [string-stringbuffer-stringbuilder](java/javaBasic/string/string-stringbuffer-stringbuilder.md) 8 | * [interview](java/javaBasic/string/interview.md) 9 | * [collection](java/javaBasic/collection/README.md) 10 | * [map](java/javaBasic/collection/map/README.md) 11 | * [HashMap与HashTable](java/javaBasic/collection/map/code-hashmap-hashtable.md) 12 | * [compare](java/javaBasic/collection/map/compare.md) 13 | * [concurrentHashMap](java/javaBasic/collection/map/concurrent-hash-map.md) 14 | * [LinkedHashMap](java/javaBasic/collection/map/linked-hash-map-code.md) 15 | * [红黑树与平衡二叉树](java/javaBasic/collection/map/red-tredd-balanced-tree.md) 16 | * [reflection](java/javaBasic/reflection/README.md) 17 | * [create-object](java/javaBasic/reflection/create-object.md) 18 | * [scene](java/javaBasic/reflection/scene.md) 19 | * [tuning](java/javaBasic/reflection/tuning.md) 20 | * [throwable](java/javaBasic/throwable/README.md) 21 | * [compare-c](java/javaBasic/throwable/compare-c.md) 22 | * [finally](java/javaBasic/throwable/finally.md) 23 | * [platform](java/javaBasic/platform/README.md) 24 | * [bytecode](java/javaBasic/platform/bytecode.md) 25 | * [difference-jdk-jre](java/javaBasic/platform/difference-jdk-jre.md) 26 | * [prnciple-platform](java/javaBasic/platform/prnciple-platform.md) 27 | * [others](java/javaBasic/others/README.md) 28 | * [basic-type-transfer](java/javaBasic/others/basic-type-transfer.md) 29 | * [final&finally&finalize](java/javaBasic/others/final-finally-finalize.md) 30 | * [interface-abstract](java/javaBasic/others/interface-abstract.md) 31 | * [override](java/javaBasic/others/override.md) 32 | * [static](java/javaBasic/others/static.md) 33 | * [jvm](java/jvm.md) 34 | * [jvm介绍](java/jvm/README.md) 35 | * [类的加载机制](java/jvm/Class-loading-mechanism.md) 36 | * [jvm内存结构](java/jvm/jvm-Memory-structure.md) 37 | * [GC算法 垃圾回收](java/jvm/gc-algorithm-garbage.md) 38 | * [GC分析 命令调优](java/jvm/GC-analysis.md) 39 | * [java-model](java/jvm/java-model.md) 40 | * [io](java/io.md) 41 | * [磁盘IO的访问方式](java/io/read-ways.md) 42 | * [堆内内存vs堆外内存](java/io/direct-buffer.md) 43 | * [NIO](java/io/nio.md) 44 | * [BIO/NIO/AIO](java/io/bio-nio-aio.md) 45 | * [线程](java/thread/README.md) 46 | * [线程与进程](java/thread/thread-process.md) 47 | * [多线程](java/thread/multi-thread/README.md) 48 | * [线程池](java/thread/multi-thread/pool.md) 49 | * [concurrent](java/thread/multi-thread/concurrent.md) 50 | * [锁](java/thread/multi-thread/syn-lock.md) 51 | * [线程安全](java/thread/security/README.md) 52 | * [happen-before](java/thread/security/happen-before.md) 53 | * [volatile](java/thread/security/volatile.md) 54 | * [特性](java/thread/security/atomic-visible.md) 55 | * [面试题](java/thread/interview/README.md) 56 | * [多线程](java/thread/interview/thread-pool.md) 57 | * [多线程通信](java/thread/interview/communication.md) 58 | * [生产者消费者](java/thread/interview/produce-consume.md) 59 | * [线程异常](java/thread/interview/exception.md) 60 | * [线程死锁](java/thread/interview/philosopher.md) 61 | * [其它](java/thread/interview/others.md) 62 | * [网络通信](java/network-communication/README.md) 63 | * [http](java/network-communication/http/README.md) 64 | * [cache](java/network-communication/http/cache.md) 65 | * [communication](java/network-communication/http/communication.md) 66 | * [cookie-session](java/network-communication/http/cookie-session.md) 67 | * [get-post](java/network-communication/http/get-post.md) 68 | * [https](java/network-communication/http/https.md) 69 | * [partial-content](java/network-communication/http/partial-content.md) 70 | * [status-code](java/network-communication/http/status-code.md) 71 | * [url-uri](java/network-communication/http/url-uri.md) 72 | * [version](java/network-communication/http/version.md) 73 | * [serizalizable](java/network-communication/serizalizable/README.md) 74 | * [java-serizaliable](java/network-communication/serizalizable/java-serizaliable.md) 75 | * [transient](java/network-communication/serizalizable/transient.md) 76 | * [interview](java/network-communication/serizalizable/interview.md) 77 | * [tcp-udp](java/network-communication/tcp-udp/README.md) 78 | * [non-block-socket](java/network-communication/tcp-udp/non-block-socket.md) 79 | * [socket](java/network-communication/tcp-udp/socket.md) 80 | * [tcp-handshake](java/network-communication/tcp-udp/tcp-handshake.md) 81 | * [tcp-transfer-status](java/network-communication/tcp-udp/tcp-transfer-status.md) 82 | * [数据库](database/README.md) 83 | * [ORM](database/orm.md) 84 | * [事务](database/transaction.md) 85 | * [设计模式](pattern/README.md) 86 | * [设计原则](pattern/base/README.md) 87 | * [六大原则](pattern/base/principles.md) 88 | * [创建型模式](pattern/creational/README.md) 89 | * [工厂模式](pattern/creational/factory/README.md) 90 | * [简单工厂](pattern/creational/factory/simple.md) 91 | * [抽象工厂](pattern/creational/factory/abstract.md) 92 | * [单例模式](pattern/creational/singleton/README.md) 93 | * [单例模式实现方法](pattern/creational/singleton/impl.md) 94 | * [双检索](pattern/creational/singleton/disadvan.md) 95 | * [建造者模式](pattern/creational/builder.md) 96 | * [原型模式](pattern/creational/prototype.md) 97 | * [结构型模式](pattern/structural/README.md) 98 | * [适配器模式](pattern/structural/composite.md) 99 | * [类的适配器模式](pattern/structural/adapter/class.md) 100 | * [对象的适配器模式](pattern/structural/adapter/object.md) 101 | * [接口的适配器模式](pattern/structural/adapter/interface.md) 102 | * [桥接模式](pattern/structural/bridge.md) 103 | * [装饰模式](pattern/structural/decorator.md) 104 | * [代理模式](pattern/structural/proxy.md) 105 | * [外观模式](pattern/structural/facade.md) 106 | * [组合模式](pattern/structural/composite.md) 107 | * [享元模式](pattern/structural/flyweight.md) 108 | * [行为型模式](pattern/behavioral/README.md) 109 | * [策略模式](pattern/behavioral/strategy.md) 110 | * [模板方法模式](pattern/behavioral/template_method.md) 111 | * [观察者模式](pattern/behavioral/observer.md) 112 | * [迭代子模式](pattern/behavioral/iterator.md) 113 | * [责任链模式](pattern/behavioral/chain_of_responsibility.md) 114 | * [命令模式](pattern/behavioral/command.md) 115 | * [中间件](middleware/README.md) 116 | * [redis](middleware/redis/README.md) 117 | 118 | -------------------------------------------------------------------------------- /cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/cover.jpg -------------------------------------------------------------------------------- /cover_small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/cover_small.jpg -------------------------------------------------------------------------------- /database/README.md: -------------------------------------------------------------------------------- 1 | # 数据库 2 | 3 | 数据库包含的考察点: 4 | 5 | * mysql 6 | * orm 7 | * 事务 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /database/orm.md: -------------------------------------------------------------------------------- 1 | # ORM 对象关系映射 2 | 3 | ##一、ORM简介 4 | 5 | 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。那么,到底如何实现持久化呢?一种简单的方案是采用硬编码方式,为每一种可能的数据库访问操作提供单独的方法。 6 | 7 | - 这种方案存在以下不足: 8 | 9 | 1.持久化层缺乏弹性。一旦出现业务需求的变更,就必须修改持久化层的接口 10 | 11 | 2.持久化层同时与域模型与关系数据库模型绑定,不管域模型还是关系数据库模型发生变化,毒药修改持久化曾的相关程序代码,增加了软件的维护难度。 12 | 13 | - ORM提供了实现持久化层的另一种模式,它采用映射元数据来描述对象关系的映射,使得ORM中间件能在任何一个应用的业务逻辑层和数据库层之间充当桥梁。 14 | 15 | - Java典型的ORM中间件有:Hibernate,ibatis,speedframework。 16 | 17 | 18 | 19 | ORM的方法论基于三个核心原则: 20 | 21 | 1. 简单:以最基本的形式建模数据。 22 | 23 | 1. 传达性:数据库结构被任何人都能理解的语言文档化。 24 | 25 | 1. 精确性:基于数据模型创建正确标准化了的结构。 26 | 27 | ##二、ORM的概念 28 | 29 | - 让我们从O/R开始。字母O起源于"对象"(Object),而R则来自于"关系"(Relational)。几乎所有的程序里面,都存在对象和关系数据库。在业务逻辑层和用户界面层中,我们是面向对象的。当对象信息发生变化的时候,我们需要把对象的信息保存在关系数据库中。 30 | 31 | 32 | 33 | - 当你开发一个应用程序的时候(不使用O/R Mapping),你可能会写不少数据访问层的代码,用来从数据库保存,删除,读取对象信息,等等。你在DAL中写了很多的方法来读取对象数据,改变状态对象等等任务。而这些代码写起来总是重复的。 34 | 35 | 36 | 37 | - ORM解决的主要问题是对象关系的映射。域模型和关系模型分别是建立在概念模型的基础上的。域模型是面向对象的,而关系模型是面向关系的。一般情况下,一个持久化类和一个表对应,类的每个实例对应表中的一条记录,类的每个属性对应表的每个字段。 38 | 39 | 40 | 41 | - ORM技术特点: 42 | 43 | 1.提高了开发效率。由于ORM可以自动对Entity对象与数据库中的Table进行字段与属性的映射,所以我们实际可能已经不需要一个专用的、庞大的数据访问层。 44 | 45 | 2.ORM提供了对数据库的映射,不用sql直接编码,能够像操作对象一样从数据库获取数据。 46 | 47 | ##三、ORM的优缺点 48 | 49 | 50 | 51 | - ORM的缺点是会牺牲程序的执行效率和会固定思维模式。 52 | 53 | 从系统结构上来看,采用ORM的系统一般都是多层系统,系统的层次多了,效率就会降低。ORM是一种完全的面向对象的做法,而面向对象的做法也会对性能产生一定的影响。 54 | 55 | 56 | 57 | - 在我们开发系统时,一般都有性能问题。性能问题主要产生在算法不正确和与数据库不正确的使用上。ORM所生成的代码一般不太可能写出很高效的算法,在数据库应用上更有可能会被误用,主要体现在对持久对象的提取和和数据的加工处理上,如果用上了ORM,程序员很有可能将全部的数据提取到内存对象中,然后再进行过滤和加工处理,这样就容易产生性能问题。 58 | 59 | 60 | 61 | - 在对对象做持久化时,ORM一般会持久化所有的属性,有时,这是不希望的。 62 | 63 | 但ORM是一种工具,工具确实能解决一些重复,简单的劳动。这是不可否认的。但我们不能指望工具能一劳永逸的解决所有问题,有些问题还是需要特殊处理的,但需要特殊处理的部分对绝大多数的系统,应该是很少的。 64 | -------------------------------------------------------------------------------- /database/transaction.md: -------------------------------------------------------------------------------- 1 | # 事务是什么? 2 | 3 | ##引言 4 | 5 | 软件开发领域里,全有或者全无的操作被称为[事务] 6 | 7 | 例如,买电影票这件事,可能需要以下几个步骤: 8 | 9 | - 检查可售出的座位,确保买到票的人能有座位 10 | 11 | - 每卖出一张票,空闲座位数就减少一个 12 | 13 | - 为购买的电影票进行支付 14 | 15 | - 将电影票分配给你 16 | 17 | 这个流程用事务管理就是: 18 | 19 | ![事务管理示意图](pic/transaction.png) 20 | 21 | 这样,就可以确保购票者和影院都没有损失,即,用户不会买到没有座位的票,影院也不会有用户买到票了却没有收到钱等情况出现 22 | 23 | ##事务的四个特性 24 | 25 | - 原子性(Atomic) 即确保事务内的一个或者多个活动全部发生或全部不发生。如果事务内的活动均成功,则事务成功;如果有失败,则事务失败并回滚。 26 | 27 | - 一致性(Consistent) 一旦事务完成,不论成功还是失败,业务内的数据应处于一致的状态。 28 | 29 | - 隔离性(Isolated) 事务允许多个用户对相同的数据进行读写,事务应该彼此隔离,避免发生同步读写相同数据的事情 30 | 31 | - 持久性(Durable) 一旦事务完成,结果应该被持久化,以保证在出现系统崩溃等情况时的恢复能力 32 | 33 | 其实,这几个特性是互相保证的,原子性通过保证系统数据永远不会处于不一致或部分完成的状态来确保一致性,隔离性也同样保证了一致性。持久性保证了事务的结果不会丢失。 34 | 35 | # Spring 对事务管理的支持 36 | 37 | 38 | ## 声明方式管理事务 39 | 40 | ###事务的5种属性 41 | 42 | ####传播行为 43 | 44 | 传播行为的以下几种规则回答了这样一个问题:即新的事务应该被启动还是被刮起,或者方法是否要在事务环境中运行。 45 | 46 | | 传播行为 | 含义 | 47 | 48 | |--------|--------| 49 | 50 | |PROPOGATION_MANDATORY|表示当前方法必须在事务中运行,如果事务不存在,则会抛出一个异常| 51 | 52 | |**PROPOGATION_NESTED**|如果已经存在事务,则在嵌套事务中运行,可独立于已存在的事务单独地提交或者回滚;如果当前事物不存在,则与PROPOGATION_REQUIERED一样| 53 | 54 | |PROPOGATION_NEVER| 表示当前方法不应该运行在事务上下文中,如果当前正有一个事务正在运行,则会抛出异常| 55 | 56 | |PROPOGATION_NOT_SUPPORTED| 表示当前方法不应该运行在事务中,如果当前存在事务,则在该方法运行期间被挂起| 57 | 58 | |**PROPOGATION_REQUIRED**|表示当前方法必须在事务中,如果当前事务存在,方法将会在该事务中运行;如果当前事务不存在,会启动一个新的事务| 59 | 60 | |**PROPOGATION_REQUIRES_NEW**| 表示当前方法必须运行在它自己的事务中,一个新的事务将被启动,如果存在当前事务,则在该方法执行期间,当前事务会被挂起| 61 | 62 | |PROPOGATION_SUPPORTS| 表示当前方法不需要运行在事务上下文中,但如果存在当前事务的话,那么该方法会在这个事务中运行| 63 | 64 | ####隔离级别 65 | 66 | | 隔离级别 | 含义 |说明| 67 | 68 | |--------|--------|---------| 69 | 70 | | ISOLATION_DEFAULT | 默认|使用后端数据库默认的隔离级别 71 | 72 | |ISOLATION_READ_UNCOMMITTED|读未提交|脏读,不可重复读,幻读都有可能出现,效率最高 73 | 74 | |ISOLATION_READ_COMMITTED |读已提交 |可避免脏读 75 | 76 | |ISOLATION_REPEATABLE_READ |可重复读 |可避免脏读,不可重复读 77 | 78 | |ISOLATION_SERIALIZABLE |序列化|完全服从ACID的隔离级别,效率最低 79 | 80 | ####是否只读? 81 | 82 | 如果事务只对后端数据库进行读的操作,数据库可以利用事务的只读特性进行一些特定的优化 83 | 84 | 注:只有传播行为为:PROPOGATION_NESTED/PROPOGATION_REQUIRED/PROPOGATION_REQUIRES_NEW的方法来说,将事务声明为只读才有意义 85 | 86 | ####事务回滚规则 87 | 88 | 默认情况下,事务只有在遇到运行时异常才会回滚,遇到检查型异常时不会回滚 89 | 90 | 但是,可以通过配置实现制定具体事务回滚规则 91 | 92 | ####事务超时 93 | 94 | 为了使程序能更好的运行,事务不能运行太久的时间,因此事务的超时特性也很重要 95 | 96 | 注:也只在传播行为为PROPOGATION_NESTED/PROPOGATION_REQUIRED/PROPOGATION_REQUIRES_NEW的方法来说有意义 97 | 98 | 99 | -------------------------------------------------------------------------------- /img/pattern/abstract_factory.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/img/pattern/abstract_factory.PNG -------------------------------------------------------------------------------- /img/pattern/adapter_class.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/img/pattern/adapter_class.PNG -------------------------------------------------------------------------------- /img/pattern/adapter_interface.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/img/pattern/adapter_interface.PNG -------------------------------------------------------------------------------- /img/pattern/adapter_object.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/img/pattern/adapter_object.PNG -------------------------------------------------------------------------------- /img/pattern/bridge1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/img/pattern/bridge1.PNG -------------------------------------------------------------------------------- /img/pattern/bridge2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/img/pattern/bridge2.PNG -------------------------------------------------------------------------------- /img/pattern/chain_of_responsibility.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/img/pattern/chain_of_responsibility.PNG -------------------------------------------------------------------------------- /img/pattern/command.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/img/pattern/command.PNG -------------------------------------------------------------------------------- /img/pattern/composite.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/img/pattern/composite.PNG -------------------------------------------------------------------------------- /img/pattern/decorator.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/img/pattern/decorator.PNG -------------------------------------------------------------------------------- /img/pattern/facade.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/img/pattern/facade.PNG -------------------------------------------------------------------------------- /img/pattern/flyweight.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/img/pattern/flyweight.PNG -------------------------------------------------------------------------------- /img/pattern/flyweight2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/img/pattern/flyweight2.PNG -------------------------------------------------------------------------------- /img/pattern/iterator.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/img/pattern/iterator.PNG -------------------------------------------------------------------------------- /img/pattern/observer.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/img/pattern/observer.PNG -------------------------------------------------------------------------------- /img/pattern/proxy.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/img/pattern/proxy.PNG -------------------------------------------------------------------------------- /img/pattern/simple_factory.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/img/pattern/simple_factory.PNG -------------------------------------------------------------------------------- /img/pattern/template_method.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/img/pattern/template_method.PNG -------------------------------------------------------------------------------- /java/README.md: -------------------------------------------------------------------------------- 1 | ## java 2 | 3 | java 知识体系 4 | 5 | 6 | -------------------------------------------------------------------------------- /java/collection.md: -------------------------------------------------------------------------------- 1 | ## 集合 2 | 3 | 集合在开发过程中使用频率非常高,也是面试中必考知识点 4 | 5 | -------------------------------------------------------------------------------- /java/collection/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/java/collection/README.md -------------------------------------------------------------------------------- /java/io.md: -------------------------------------------------------------------------------- 1 | ## io -------------------------------------------------------------------------------- /java/io/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/java/io/README.md -------------------------------------------------------------------------------- /java/io/bio-nio-aio.md: -------------------------------------------------------------------------------- 1 | # BIO/NIO/AIO 2 | 3 | ##BIO—同步阻塞 4 | 5 | 在Jdk1.4之前,用Java编写网络请求都是建立在一个ServerSocket上,然后客户端简历Socket时会询问是否有线程可以处理:如果没有,要么拒绝,要么等到 6 | 7 | - 一个连接,要求Server对应一个处理线程 8 | 9 | ##NIO—同步非阻塞 10 | 11 | Jdk1.4之后新加入的网络编程jar包 12 | 13 | - 在java.nio包及其子包内 14 | 15 | - 基于事件驱动思想来完成的,其主要想解决的是BIO大并发问题 16 | 17 | - NIO基于Reactor设计模式,当socket有流可读或可写入socket时,操作系统会相应的通知应用程序进行处理,应用程序再将流读取到缓冲区/写入操作系统 18 | 19 | ##AIO—异步非阻塞 20 | 21 | - 对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并**通知**应用程序 22 | 23 | - 对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统**主动通知 **应用程序 24 | 25 | - 在Jdk1.7中被引入AIO,被称作NIO.2,主要在java.nio.channels中 26 | -------------------------------------------------------------------------------- /java/io/direct-buffer.md: -------------------------------------------------------------------------------- 1 | # 堆内内存vs堆外内存 2 | 3 | 简单的说,我们需要牢记三点: 4 | 5 | 1. 平时的read/write,都会在I/O设备与应用程序空间之间经历一个“内核缓冲区”。 6 | 7 | 2. Direct Buffer(直接内存,也是堆外内存)就好比是“内核缓冲区”上的缓存,不直接受GC管理;而Heap Buffer(堆内内存)就仅仅是byte[]字节数组的包装形式。因此把一个Direct Buffer写入一个Channel的速度要比把一个Heap Buffer写入一个Channel的速度要快 8 | 9 | 3. Direct Buffer创建和销毁的代价很高,所以要用在尽可能重用的地方 10 | 11 | ##堆内内存(InDirectBuffer || HeapBuffer) 12 | 13 | Java程序磁盘进行读操作: 14 | 15 | 1. JVM只能读取OS中的Buffer 16 | 17 | 2. OS可以读取磁盘中的数据,并存入本层的Buffer中 18 | 19 | 3. 操作系统将自己的Buffer拷贝给JVM堆里的Buffer 20 | 21 | ##堆外内存(DirectBuffer) 22 | 23 | Java程序磁盘进行读操作: 24 | 25 | 1. OS从磁盘读取数据后,直接存到了堆外内存中 26 | 27 | 2. 接缓冲区在使用Socket传递数据时性能很好,因为若使用间接缓冲区,JVM会先将数据复制到直接缓冲区再进行传递;但是直接缓冲区的缺点是在分配内存空间和释放内存时比堆缓冲区更复杂 28 | 29 | 3. jdk7后的nio使用了堆外内存 30 | 31 | ###优点 32 | 33 | 1. 节约了时间 34 | 35 | 2. 节约了堆内内存,频繁的IO将会占用大量堆内内存,将buffer放到堆外内存进行调优,即ByteBuffer.allocateDirect(int)——例如Hadoop中 36 | 37 | 3. 不受young GC的限制 38 | 39 | 40 | -------------------------------------------------------------------------------- /java/io/nio.md: -------------------------------------------------------------------------------- 1 | # NIO分析 2 | 3 | - Nio最核心的是**Reactor模式** 4 | 5 | - Selector使用一个线程来管理多个通道,做轮询选择准备好的通道 6 | 7 | - 这种轮询方式处理多线程请求时不需要上下文切换 8 | 9 | 10 | ##为什么需要NIO 11 | 12 | 1. BIO读写速度慢 13 | 14 | 2. BIO的阻塞特性将一部分时间浪费在等待IO操作上,同时大量创建、管理、销毁以及上下文切换使得性能下降和消耗大量系统资源 15 | 16 | ###解决方式 17 | 18 | 1. 提高IO读写速度,同时采取**非阻塞**方式读取,减少等待时间 19 | 20 | 2. 提高线程的使用率,减少系统资源的消耗(线程池) 21 | 22 | 3. 对于频繁的IO操作系统,IO是瓶颈,NIO可以更好结余IO操作的时间 23 | 24 | 4. Stream/Reader的读写单位是byte/char, NIO的单位是buffer 25 | 26 | ##NIO概述 27 | 28 | Java NIO由以下3个核心部分组成: 29 | 30 | 1. Channels 与 Buffers(通道和缓冲区):标准的IO基于字节流和字符流进行操作的,而NIO是基于Channel和Buffer进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。 31 | 32 | - 这些通道覆盖了UDP和TCP网络IO,以及文件IO 33 | 34 | 2. Selectors(选择器):Java NIO引入了选择器的概念,选择器用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个的线程可以监听多个数据通道 35 | 36 | - 在一个单线程中使用一个Selector处理多个Channel 37 | 38 | - 要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直**阻塞**到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。 39 | 40 | ##NIO实现代码分析 41 | 42 | ###Buffer 43 | 44 | NIO读写的容器,从Buffer中读取数据或者将数据写入到Buffer中 45 | 46 | 对比上诉InputSteam/OutputStream, Stream用byte数组byte[]来做缓存,状态无法记录,不知道读/写了多少,还有多少是有效字节,下次从哪里开始读/写 47 | 48 | ####Buffer内部的3个控制位置字段 49 | 50 | 1. **position**:下一个要读/写的位置 51 | 52 | - 读:position指定了下一个字节将放到数组的哪一个元素中。因此,如果您从通道中读三个字节到缓冲区中,那么缓冲区的 position 将会设置为3,指向数组中第四个元素 53 | 54 | - 写:position 值跟踪从缓冲区中获取了多少数据。更准确地说,它指定下一个字节来自数组的哪一个元素。因此如果从缓冲区写了5个字节到通道中,那么缓冲区的 position 将被设置为5,指向数组的第六个元素。 55 | 56 | 2. **limit**:有效位置边界 57 | 58 | - 读:能读到的最大位置 59 | 60 | - 写:可以被写到的最大位置 61 | 62 | 3. **capacity**:容量 63 | 64 | 一般而言,position <= limit <= capacity 65 | 66 | #####关于filp() 和 clear() 67 | 68 | - filp() 用在 read 和 write 之间,使 ready to write 69 | 70 | - clear() 用在write 和下一次 read 之间,使 ready to read 71 | 72 | ####Buffer的获取 73 | 74 | #####1. allocate通过工厂构造:使用堆内内存 75 | 76 | ```java 77 | 78 | public static ByteBuffer allocate(int capacity) { 79 | 80 | if(capacity < 0){ 81 | 82 | throw new IlleagalArgumentException(); 83 | 84 | } 85 | 86 | return new HeapByteBuffer(capacity, capacity); 87 | 88 | } 89 | 90 | ``` 91 | 92 | #####2.wrap封装byte数组:使用堆内内存 93 | 94 | ```java 95 | 96 | public static ByteBuffer wrap(byte[] array, int offset, int length){ 97 | 98 | try { 99 | 100 | return new HeapByteBuffer(array, offset, length); 101 | 102 | }catch(IlleagalArgumentException x) { 103 | 104 | throw new IndexOutOfBoundsException(); 105 | 106 | } 107 | 108 | } 109 | 110 | ``` 111 | 112 | #####3.allocateDirect:使用堆外内存 113 | 114 | ```java 115 | 116 | public static ByteBuffer allocateDirect(int capacity){ 117 | 118 | return new DirectByteBuffer(capacity); 119 | 120 | } 121 | 122 | ``` 123 | 124 | ###Channel 125 | 126 | NIO中Input/Oupt的管道 127 | 128 | - 矿山(File), 矿道(Channel),矿车(Buffer) 129 | 130 | - Channel 不同于 InputStream/OutputStream,Channel是双向的,即可作为读的管道也可作为写的管道 131 | 132 | ###Selector(选择器) 133 | 134 | 选择器是Java NIO中能够检测到一到多个NIO通道,并能够知道通道是否为事件(例如读写)做好准备的组件。这样,一个单独的线程可以管理多个channel,从而管理多个连接。 135 | 136 | ##参考资料 137 | 138 | 1. [Java NIO系列教程](http://www.iteye.com/magazines/132-Java-NIO) 139 | 140 | 2. [NIO入门](http://www.ibm.com/developerworks/cn/education/java/j-nio/j-nio.html) 141 | -------------------------------------------------------------------------------- /java/io/read-ways.md: -------------------------------------------------------------------------------- 1 | # 磁盘IO的访问方式 2 | 3 | ##缓存IO(标准IO) 4 | 5 | 数据先从磁盘复制到**内核空间的缓冲区**,然后从内核空间缓冲区复制到应用程序的地址空间 6 | 7 | ###读操作 8 | 9 | 1. 操作系统检查内核的缓冲区有没有需要的数据,如果已经缓存了,那么就直接从缓存中返回 10 | 11 | 2. 否则从磁盘中读取,然后缓存在操作系统的内核缓存中 12 | 13 | ###写操作 14 | 15 | 1. 将数据从用户空间复制到内核空间缓存中 16 | 17 | 2. 至于什么时候再写到磁盘中由**操作系统**决定 18 | 19 | - 例外:本地显示调用了sync同步命令 20 | 21 | ###优缺点 22 | 23 | ####优点 24 | 25 | 在一定程度上分离了内核空间和用户空间,保护系统本身的运行安全,减少读取磁盘的次数,从而提高性能 26 | 27 | ####缺点 28 | 29 | 应用程序地址空间和缓存之间进行了多次数据拷贝操作,造成开销大 30 | 31 | ##直接IO 32 | 33 | 应用程序直接访问磁盘数据,不经过内核缓冲区 34 | 35 | - 直接加载速度非常缓慢 36 | 37 | ##内存映射 38 | 39 | 将硬盘上文件的位置与进程**逻辑地址**空间中一块大小相同的区域一一对应,当要访问内存中一段数据时,可根据这个逻辑地址空间映射到实际文件中进行操作,从而不必执行IO操作。 40 | 41 | 说白了就是使用虚拟内存将磁盘的文件数据加载到虚拟内存的内存页,然后就可以直接操作内存页数据。 42 | 43 | - 适用于大量数据的传输 44 | 45 | - 内存映射处理文件时,不再执行IO操作 46 | 47 | ![内存映射](http://7d9o4k.com1.z0.glb.clouddn.com/内存映射.png) 48 | 49 | ###创建内存映射的文件 50 | 51 | ```java 52 | 53 | RandomAccessFile raf = new RandomAccessFile("test.txt", "rw";); 54 | 55 | FileChannel fc = raf.getChannel(); 56 | 57 | //将test.txt文件所有数据映射到虚拟内存,只读 58 | 59 | MappedByteBuffer mbuff = fc.map(MapMode.READ_ONLY, 0, fc.size()); 60 | 61 | ``` 62 | 63 | ## 参考资料 64 | 65 | 1. [内存映射](http://blog.csdn.net/abc_key/article/details/32435789) 66 | -------------------------------------------------------------------------------- /java/java-model.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/java/java-model.md -------------------------------------------------------------------------------- /java/javaBasic/README.md: -------------------------------------------------------------------------------- 1 | # Java基础知识 2 | 3 | 涉及到: 4 | 5 | 1. 跨平台 6 | 2. 集合类 7 | 3. 反射 8 | 4. 异常机制 9 | 5. 字符串 10 | 6. 以及常考的接口、抽象类、重载、关键字等 -------------------------------------------------------------------------------- /java/javaBasic/collection.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/java/javaBasic/collection.md -------------------------------------------------------------------------------- /java/javaBasic/collection/README.md: -------------------------------------------------------------------------------- 1 | # Collection集合类 2 | ##Java集合框架 3 | Java集合框架是一组实现集合数据结构的类和接口。 4 | 5 | Collection接口是一组允许重复的对象,有3个接口继承了该接口。值得注意的是,Map接口不由Collection派生。 6 | ![Java集合框架](http://askingwindy-gitcafe.qiniudn.com/collections framework overview.png) 7 | ##Collection 8 | Collection是集合层次的根。一个集合包含一组对象作为其元素。 9 | Java平台不提供任何直接实现这个接口。 10 | ###Set 11 | 是一个不能包含重复的元素的集合。此接口模型代表数学Set的抽象,用来代表一组Set,如一副扑克牌。 12 | ###List 13 | 是有序集合,可以包含重复的元素。 14 | 可以从它的索引访问任何元素。更像是动态长度的数组列表。 15 | #### ArryList、LinkedList、Vector 16 | - ArrayList底层实现是一个可变的数组 17 | - LinkedList底层实现是一个链表(可被用作Stack、Queue、Deque) 18 | - Vector与ArrayList相同,不过它是同步的 19 | 20 | 如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList 21 | ####Stack 22 | Stack继承自Vector。 23 | Stack模拟了一个后进先出的栈 24 | ###Queue 25 | 先进先出的队列 26 | ##Map 27 | 没有继承Collection接口,是一个键映射值的对象。一个Map不能包含重复键:每个key只能映射一个值。 28 | ## Iterator 29 | Iterator接口提供遍历集合的方法。 30 | 迭代器允许呼叫者在迭代过程中从集合中删除元素。 31 | #### Iterator 32 | 只能正向遍历集合,适用于获取移除元素。 33 | ####ListIerator 34 | 继承Iterator,可以双向列表的遍历,同样支持元素的修改。 -------------------------------------------------------------------------------- /java/javaBasic/collection/map/README.md: -------------------------------------------------------------------------------- 1 | # Map 2 | 主要是面试中遇到的关于Map的点,特别关注`LinkedHashMap`,其中LRU算法可用其实现 -------------------------------------------------------------------------------- /java/javaBasic/collection/map/code-hashmap-hashtable.md: -------------------------------------------------------------------------------- 1 | # HashMap与HashTable部分源码比较 2 | ##HashMap与HashTable源码的异同 3 | |喵|允许null插入|支持线程同步|如何得到hash值| 4 | |--|--|--|--| 5 | |HashMap|是|不支持|调用hash()方法,二次hash| 6 | |HashTable|否|支持|直接使用对象的hashCode| 7 | ##准备工作:Entry类 8 | Entry是用于实现链表的一个节点,有key,value用于存储自身节点数据,还有next用于下个节点的引用。 9 | ```java 10 | static class Entry implements Map.Entry {//Entry继承了Map接口中的静态子类Entry;Entry是槽中的元素,用作链表来解决散列冲突 11 | final K key;//键 12 | V value;//值 13 | Entry next;//用来实现链表结构,同一链表中的key的hash是相同的 14 | int hash; 15 | 16 | protected Entry(int hash, K key, V value, Entry next) { 17 | this.hash=hash;this.key=key;this.value=value;this.next=next; 18 | } 19 | } 20 | ``` 21 | ###为什么`Entry`是静态类? 22 | `Map.Entry`是`Map`接口中的子接口,默认为static,继承了static类的子类自然必须是静态类 23 | 24 | ##HashMap源码简要分析 25 | HashMap找寻value的过程: 26 | 27 | 1. 对nullkey做单独处理,其实就是放在table[0]中 28 | 2. 根据key的hash值,决定entry在哪个桶 29 | 3. 对桶内的entry链表进行遍历,当查找到时返回value 30 | 4. 找不到,返回null 31 | 32 | 33 | ```java 34 | public class HashMap{ 35 | static final int DEFAULT_INITIAL_CAPACITY = 16 ; // 默认初始容量是16。(必须是2的次方) 36 | static final int MAXIMUM_CAPACITY = 1 << 30 ; // 即2的30次方 37 | static final float DEFAULT_LOAD_FACTOR = 0.75f; // 默认装载因子 38 | 39 | Entry[] table; // Entry表 40 | int size; // Entry[]实际存储的Entry个数 41 | int threshold; // reash的阈值,=capacity * loadfactor 42 | final float loadFactor; 43 | } 44 | 45 | ``` 46 | 47 | - 为什么容量是2的倍数? 48 | 为了寻址的快速。寻址是通过 index & (table.length-1)实现的,其实就是一个取模,如果table.length 是2的倍数的话,table.length-1 总是 111111... 的结构,与运算可以方便的得到mod值 49 | 50 | ###put()方法 51 | ```java 52 | public V put(K key, V value) { 53 | int hash = hash(key.hashCode()); // key的hash值,调用了hash()方法,相当于做了二次hash 54 | int i = indexFor(hash,table.length); // 得到槽位 55 | 56 | // 寻找是否已经有key存在,如果已经存在,使用新值覆盖旧值,返回旧值 57 | for (Entry e = table[i]; e != null; e = e.next) { 58 | Object k; 59 | if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { 60 | V oldValue = e.value; 61 | e.value = value; 62 | return oldValue; 63 | } 64 | } 65 | 66 | // 添加Entry 67 | addEntry(hash,key,value,i); 68 | return null; 69 | } 70 | ``` 71 | put()方法中,如果遇见的键值对是新的,那么会调用addEntry()方法,将这个键值对存储在hash值相同的槽位的头部(链表的头插入) 72 | ```java 73 | void addEntry(int hash, K key, V value, int bucketIndex) { 74 | Entry e = table[bucketIndex]; //bucketIndex是hash值所在的槽位,得到此时的槽位头元素 75 | table[bucketIndex] = new Entry<>(hash, key, value, e);//利用头插入法,在构造函数中为链表插入新的元素 76 | if (size++ >= threshold) 77 | resize(2 * table.length); //如果容量超出范围,进行rehash()的过程,即调用resize()方法 78 | } 79 | ``` 80 | ###hash()方法 81 | 这个函数以位移和异或的方式保证了hashcode不会有最坏情况出现(即大多数kv都分到了同一个桶)。在默认的加载因子下最坏不超过8的冲突。其实就是让分布更加的均化。 82 | ```java 83 | static int hash(int h){ 84 | h ^= (h>>>20)^(h>>>12); 85 | return h^(h>>>7)^(h>>>4); 86 | } 87 | ``` 88 | ###resize()方法 89 | 扩容做的事情包括: 90 | 1. 重新申请空间存放table 91 | 2. transfer(把老数据移到新的table中),并将table指向到新的table(之后老table自动垃圾收集掉) 92 | 3. 调整新的threshold值,以便下一次的resize()能够在put方法中被激活 93 | 94 | ```java 95 | void resize(int newCapacity) { 96 | Entry[] oldTable = table; 97 | int oldCapacity = oldTable.length; 98 | Entry[] newTable = new Entry[newCapacity]; //构造一个新的桶来存储元素 99 | transfer(newTable); //把老数据移到新的表中 100 | table = newTable; //table指向新的表 101 | threshold = (int)(newCapacity * loadFactor); //调整新的threshold值 102 | } 103 | ``` 104 | ####transfer()方法 105 | tansfer()方法将老数据移到了新的表中,由于链表构造是头插入,新表中每一个槽位的链表元素顺序与原顺序相反 106 | ```java 107 | void transfer(Entry[] newTable) { 108 | Entry[] src = table; 109 | int newCapacity = newTable.length; 110 | for (int j = 0; j < src.length; j++) { 111 | Entry e = src[j]; 112 | if (e != null) { 113 | src[j] = null; 114 | do { 115 | Entry next = e.next; 116 | int i = indexFor(e.hash, newCapacity); 117 | e.next = newTable[i]; 118 | newTable[i] = e; 119 | e = next; 120 | } while (e != null); 121 | } 122 | } 123 | } 124 | ``` 125 | 126 | ##HashTable源码简要分析 127 | HashTable与HashMap相似,它们利用Entry[]数组来存储元素,利用链表法来解决冲突。Entry类来当做链表的节点,将hash值相同的键值对放入同一个链表中 128 | ###put()方法 129 | ```java 130 | public synchronized V put(K key,V value) { 131 | // 检查key是否已经存在,若存在则覆盖已经存在value,并返回被覆盖的value。 132 | Entry tab[] = table; 133 | int hash = key.hashCode(); //直接得到hash值,不需要二次散列 134 | int index = (hash & 0x7FFFFFFF) % tab.length; // 存储槽位索引。 135 | for(Entry e = tab[index]; e!=null; e=e.next ) { // 在冲突链表中寻找 136 | if( (e.hash == hash ) && e.key.equals(key) ) { 137 | V old = e.value; 138 | e.value = value; // 新value覆盖旧value 139 | return old; 140 | } 141 | } 142 | 143 | // 是否需要rehash 144 | if(count >= threshold){ 145 | rehash(); 146 | tab = table; // rehash完毕后,修正tab指针指向新的Entry[] 147 | index = (hash & 0x7FFFFFFF) % tab.length; // 重新计算Slot的index 148 | } 149 | 150 | // 存储到槽位,如果有冲突,新来的元素被放到了链表前面。 151 | Entry e = tab[index]; // 旧有Entry 152 | tab[index] = new Entry<>(hash,key,value,e/* 旧有Entry成为了新增Entry的next */); 153 | count ++; 154 | return null; 155 | } 156 | ``` 157 | ###rehash()方法 158 | 当Entry[]的实际元素数量(Count)超过了分配容量(Capacity)的75%时,新建一个Entry[]是原先的2倍,并重新Hash(rehash) 159 | ```java 160 | protected void rehash() { 161 | int oldCapacity = table.length; 162 | Entry[] oldMap = table; 163 | int newCapacity = (oldCapacity << 1) + 1; // 2倍+1 164 | Entry[] newMap = new Entry[newCapacity]; 165 | threshold = (int)(newCapacity * loadFactor); 166 | table = newMap; 167 | 168 | for( int i=oldCapacity; i-- >0;){ // i的取值范围为 [oldCapacity-1,0] 169 | for (Entry old = oldMap[i]; old!=null;){ // 遍历旧Entry[] 170 | Entry e = old; 171 | int index = (e.hash & 0x7FFFFFFF) % newCapacity; // 重新计算各个元素在新Entry[]中的槽位index。 172 | e.next = newMap[index]; // 已经存在槽位中的Entry放到当前元素的next中 173 | newMap[index]=e; // 放到槽位中 174 | old = old.next; 175 | } 176 | } 177 | 178 | } 179 | ``` 180 | 181 | 182 | -------------------------------------------------------------------------------- /java/javaBasic/collection/map/compare.md: -------------------------------------------------------------------------------- 1 | # 比较:TreeMap、HashMap、LinkedHashMap、HashTable 2 | ##HashMap 3 | * 内部结构是一个数组,线性顺序存储,二次结构使用线性的单链表 4 | * 据键可以直 接获取它的值,具有很快的访问速度 5 | * 不支持线程的同步,即任 一 时刻可以有多个线程同时写HashMap;可能会导致数据的不一致 6 | * 允许一个空键值 7 | * Map interface的一个实 现 8 | * 首先通过类本身的hashcode得到hash值 ,然后再通过hash()方法得到新的hash函数,防止碰撞 9 | 10 | ##HashTable 11 | * 采用hash法进行索引 12 | * 不允许空键值 13 | * 继承自Dictionary 14 | * 方法是线程安全的 15 | * 直接使用对象的hashCode 16 | 17 | ##TreeMap 18 | * 内部结构是一棵**红黑树**(又叫排序数,是二叉树的一种),使用链式存储,可以指定比较器Comparator,key需实现Comparable接口 19 | * key值不能是null 20 | * 实现SortMap接口,把保存的记录根据键排序 21 | 22 | ##LinkedHashMap 23 | * HashMap的子类 24 | * 内部结构是一个数组,线性顺序存储,二次结构使用线性的单链表,但同时内部维护了一个双向循环链表,可以保持记录的插入顺序、或者访问顺序(将get方法访问的元素移至链表尾部) 25 | * 实现LRU算法的缓存容器,当然要指定淘汰时的使用频率和容量上限 LRU是Least Recently Used 近期最少使用算法。 26 | 27 | 28 | ##怎样使HashMap同步 29 | 30 | HashMap可以通过`Map m = Collections.synchronizedMap(hashMap)`来达到同步的效果 31 | -------------------------------------------------------------------------------- /java/javaBasic/collection/map/concurrent-hash-map.md: -------------------------------------------------------------------------------- 1 | #ConcurrentHashMap与锁分离 2 | ConcurrentHashMap是Java 5中支持高并发、高吞吐的HashMap。 3 | 4 | 它的关键技术是:**锁分离技术**。它使用了多个锁来控制对hash表的不同部分进行修改。 5 | 6 | ##ConcurrentHashMap实现原理 7 | ConcurrentHashMap包含了两个静态内部类: 8 | 9 | - HashEntry 10 | - Segment 11 | 12 | ConcurrentHashMap中的Segment就相当于一个小的HashTable,每个HashTable由多个HashEntry组成。每个Segment持有自己的锁,只要修改操作发生在不同的Segment上,就可以并发执行 13 | 14 | ![组成](http://askingwindy-gitcafe.qiniudn.com/ConcurrentHashMap.png) 15 | 16 | ###HashEntry 17 | HashEntry用来封装映射表的键值对 18 | 19 | ###Segment 20 | Segment用来充当锁的角色,是一种可重入锁ReentrantLock 21 | 利用了**锁分离**技术来保护不同的segment 22 | 23 | ####具体实现 24 | - 每个Segment对象守护整个散列表的若干个桶 25 | - 每个桶由若干个HashEntry对象连接起来的 26 | 27 | ##ConcurrentHashMap是弱一致的 28 | ConcurrentHashMap进行操作时,put操作将一个元素加入到底层数据结构后,get可能在某段时间内还看不到这个元素。 29 | 30 | ConcurrentHashMap的弱一致性主要是为了提升效率,是一致性与效率之间的一种权衡。 31 | 32 | 要成为强一致性,就得到处使用锁,甚至是全局锁,这就与Hashtable和同步的HashMap一样了 33 | 34 | 35 | ##锁分离 36 | 每个Segment持有自己的锁,只要修改操作发生在不同的Segment上,就可以并发执行 37 | ###跨Segment的锁 38 | 有些方法需要跨Segment执行:`size()`、`containsValue()`,他们可能需要锁定整个表而不仅仅是某个Segment。 39 | 40 | 需要按顺序锁定所有的段,操作完毕后,需要按顺序释放所有段的锁(确保不发生死锁)。 41 | -------------------------------------------------------------------------------- /java/javaBasic/collection/map/how-to-syn-hashmap.md: -------------------------------------------------------------------------------- 1 | # 怎样使HashMap同步 2 | HashMap可以通过`Map m = Collections.synchronizedMap(hashMap)`来达到同步的效果 -------------------------------------------------------------------------------- /java/javaBasic/collection/map/linked-hash-map-code.md: -------------------------------------------------------------------------------- 1 | #LinkedHashMap源码研读 2 | LinkedHashMap是HashMap+LinkedList的结合 3 | 4 | 新元素put进来的Entry会保存在HashMap中,同时它也会被加入一个header为头指针的双向循环链表的尾部! 5 | 6 | ![LinkedHashMap具体结构](http://askingwindy-gitcafe.qiniudn.com/LinkedHashMap.png) 7 | ##LinkedHashMap的有序性 8 | 能够保证某种有序性,非排序有序性,而是指某种稳定性: 9 | - access-ordered 10 | 按照访问时间排序,最近访问的排在最前面。这一点可以被用作cache。 11 | - insert-ordered 12 | 按照插入顺序排序,最后插入的排在最前面。更新不影响次序。 13 | 14 | 上面2点有序性是互斥的,即2者必居其一。 你可以通过下面的构造函数指定这种有序性,默认是插入有序: 15 | ```java 16 | public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { 17 | super(initialCapacity, loadFactor); 18 | this.accessOrder = accessOrder; 19 | } 20 | ``` 21 | 关于access-ordered特性的实现,通过覆盖HashMap.Entry.recordAccess方法。 HashMap.Entry.还有一个recordRemoval方法,在Entry被remove的时候调用。 22 | ##Entry类 23 | 这里的Entry类实现了一个双向链表 24 | ```java 25 | private static class Entry extends HashMap.Entry{ 26 | // 继承自HashMap的Entry(已有key/value/hash/next字段) 27 | // 双向链表 28 | Entry before; 29 | Entry after; 30 | 31 | // 构造方法 32 | Entry(int hash, K key, V value, HashMap.Entry next) { 33 | super(hash, key, value, next); 34 | } 35 | } 36 | ``` 37 | ###addBefore()方法 38 | 在当前结点的前面添加结点 39 | ```java 40 | //将当前的entry插入到existingEntry的前面 41 | private void addBefore(Entry existingEntry){ 42 | after = existingEntry; 43 | before = existingEntry.before; 44 | before.after = this; 45 | after.before = this; 46 | } 47 | ``` 48 | ###recordAccess()方法 49 | 如果定义了LinkedHashMap的迭代顺序为访问顺序排序, 删除以前位置上的元素,并将最新访问的元素添加到链表尾部。 50 | ```java 51 | void recordAccess(HashMap m) { 52 | LinkedHashMap lm = (LinkedHashMap)m; // 把调用者的this转化成LinkedHashMap 53 | if(lm.accessOrder){ 54 | remove(); // 删除当前结点 55 | addBefore(lm.header); // 插入到header双链表尾部 56 | } 57 | } 58 | ``` 59 | ##LinkedHashMap类 60 | ```java 61 | public class LinkedHashMap extends HashMap implements Map{ 62 | //额外定义的双链表的指针,每次新put()进来的元素都要插入到双链表的尾部 63 | private transient Entry header; 64 | //accessOrder==true,表示访问顺序排序 65 | //accessOrder ==false,表示按插入顺序排序 66 | private final boolean accessOrder; 67 | } 68 | ``` 69 | ###put()方法 70 | put() 继承自 HashMap。添加元素时,不仅按照HashMap的方式散列存储,而且还通过双向链表记录先后顺序 71 | ```java 72 | public V put(K key, V value) { 73 | int hash = hash(key.hashCode()); // key的特殊hash值 74 | int i = indexFor(hash,table.length); // 槽位 index 75 | 76 | // key是否已经存在,存在则返回value。 77 | for (Entry e = table[i]; e != null; e = e.next) { 78 | Object k; 79 | if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { 80 | V oldValue = e.value; 81 | e.value = value; 82 | e.recordAccess(this); //!!LinkedHashMap特有 83 | return oldValue; 84 | } 85 | } 86 | 87 | // key不存在就添加Entry 88 | addEntry(hash,key,value,i); 89 | return null; 90 | } 91 | ``` 92 | ####addEntry()方法 93 | 与HashMap中addEntry()功能一致,将一个新的Entry类添加入桶中 94 | ```java 95 | /** 96 | *@param bucketIndex [Entry[]中的槽位] 97 | */ 98 | void addEntry(int hash, K key, V value, int bucketIndex) { 99 | //将Entry加入桶中 100 | createEntry(hash, key, value, bucketIndex); 101 | 102 | //删除最近最少使用元素的策略定义 103 | Entry eldest = header.after; 104 | if (removeEldestEntry(eldest)) { 105 | removeEntryForKey(eldest.key); 106 | } else { 107 | if (size >= threshold) 108 | resize(2 * table.length); 109 | } 110 | } 111 | ``` 112 | 利用头插入法,将entry实例加入HashMap中,同时需要将它加入一个循环双链表的尾部! 113 | ```java 114 | void createEntry(int hash, K key, V value, int bucketIndex) { 115 | HashMap.Entry old = table[bucketIndex]; 116 | //同样,利用头插入法 117 | Entry e = new Entry<>(hash, key, value, old); 118 | table[bucketIndex] = e; 119 | e.addBefore(header); 120 | size++; 121 | } 122 | ``` 123 | 124 | ##LRU算法 125 | 首先,当accessOrder为true时,才会开启按访问顺序排序的模式,才能用来实现LRU算法。 126 | 127 | 无论是put方法还是get方法,都会导致目标Entry成为最近访问的Entry,因此便把该Entry加入到了双向链表的末尾 128 | 129 | - get方法通过调用recordAccess方法来实现 130 | - put方法在覆盖已有key的情况下,也是通过调用recordAccess方法来实现,在插入新的Entry时,则是通过createEntry中的addBefore方法来实现 131 | 132 | 这样便把最近使用了的Entry放入到了双向链表的后面 133 | 134 | 多次操作后,双向链表前面的Entry便是最近没有使用的,这样当节点个数满的时候,删除的最前面的Entry(header后面的那个Entry)便是最近最少使用的Entry。 135 | -------------------------------------------------------------------------------- /java/javaBasic/collection/map/linkedhashmap.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/java/javaBasic/collection/map/linkedhashmap.md -------------------------------------------------------------------------------- /java/javaBasic/collection/map/red-tredd-balanced-tree.md: -------------------------------------------------------------------------------- 1 | # 红黑树与平衡二叉树 2 | ##红黑树 3 | 红黑树是一种特殊的排序二叉树 4 | ###什么是红黑树 5 | 1. 每个节点要么是红色,要么是黑色 6 | 2. 根节点永远是黑色的 7 | 3. 所有的叶节点都是空节点(即 null),并且是黑色的 8 | 4. 每个红色节点的两个子节点都是黑色 9 | 5. 从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点 10 | 11 | ###性质 12 | 1. 对于给定的黑色高度为 N 的红黑树,从根到叶子节点的最短路径长度为 N-1,最长路径长度为 2 * (N-1) 13 | 2. 红黑树上的插入/删除都会改变红黑树特性,必须进一步进行维护 14 | 15 | ##平衡二叉树 16 | 平衡二叉树是一棵**空树**或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。 17 | 18 | ##平衡二叉树与红黑树的区别 19 | 平衡二叉树的追求的是全局均衡,如在做插入,删除操作时,需要调整整棵树,显然这是费时的,因此希望在做调整时,是**局部调整**,因此提出了红黑树,这样一种高效的数据结构(也是最变态的一种数据结构)。 20 | 21 | 22 | 红黑树和平衡二叉树(AVL树)类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。 23 | 24 | 红黑树和AVL树的区别在于它使用颜色来标识结点的高度,它所追求的是局部平衡而不是AVL树中的非常严格的平衡。 -------------------------------------------------------------------------------- /java/javaBasic/others/README.md: -------------------------------------------------------------------------------- 1 | # 其他 2 | 收集了不属于以上各类的信息 -------------------------------------------------------------------------------- /java/javaBasic/others/basic-type-transfer.md: -------------------------------------------------------------------------------- 1 | # 基本类型之间的转换 2 | ##基本类型 3 | Java中默认的类型:整型是`int`,浮点数是`double` 4 | 5 | |名字|长度| 6 | |--|--| 7 | |boolean|1位| 8 | |byte|8位| 9 | |char|16位| 10 | |short|16位| 11 | |int|32位| 12 | |float|32位| 13 | |long|64位| 14 | |double|64位| 15 | 16 | ##自动转换 17 | **低位向高位**的转换时自动完成的 18 | - 任何类型可以转换为`double`,其次是`float`,再是`long`, 再其次是`int` 19 | - boolean不能和其他任何类型相互转换 20 | 21 | ##运算符`+-*/%`时类型转换: 22 | 1. byte/char/short + int ==> int 23 | 24 | 2. int + float ==> float 25 | 26 | 3. int + double/long ==>double/long 27 | 28 | 4. float + double/long ==> double/long 29 | 30 | 5. long + double ==> double 31 | 32 | ##两个例子 33 | ![int与double之间转换](http://askingwindy-gitcafe.qiniudn.com/1.png) 34 | ![int与float之间转换](http://askingwindy-gitcafe.qiniudn.com/2.png) 35 | -------------------------------------------------------------------------------- /java/javaBasic/others/final-finally-finalize.md: -------------------------------------------------------------------------------- 1 | # 关键字final&finally&finalize 2 | ## final关键字 3 | ###final修饰类 4 | final类通常功能是完整的,它们不能被继承。 5 | 6 | Java中有许多类是final的,譬如String, Interger以及其他包装类。 7 | ###final修饰变量 8 | 凡是对成员变量或者本地变量(在方法中的或者代码块中的变量称为本地变量)声明为final的都叫作final变量。 9 | 10 | final变量经常和static关键字一起使用,作为常量。 11 | 12 | final变量可以在任何可以被始化的地方被始化,但只能被初始化一次.一旦被初始化后就不能再次赋 值(重新指向其它对象),作为成员变量一定要显式初始化,而作为临时变量则可以只定义不初始化(当然也不能引用) 13 | ###final修饰方法 14 | 方法前面加上final关键字,代表这个方法不可以被子类的方法重写。 15 | ###final修饰方法形参 16 | 形参的值无法被更改(编译时报错) 17 | 参考博文:[JAVA方法中的参数用final来修饰的原因](http://blog.csdn.net/tavor/article/details/1920336) 18 | ```java 19 | //如果不是final 的话,我可以在checkInt方法内部把i的值改变(有意或无意的, 20 | //虽然不会改变实际调用处的值),特别是无意的,可能会引用一些难以发现的BUG 21 | publicstaticvoid checkInt(int i) 22 | { 23 | i = 200;//这样是可以的,不会编译出错的 24 | //do something 25 | } 26 | 27 | //如果是final 的话,我可以在checkInt方法内部就没办法把i的值改变 28 | //可以完全避免上面的问题 29 | publicstaticvoid checkInt(final int i) 30 | { 31 | i = 200;//这样是不可以的,会编译出错的 32 | //do something 33 | } 34 | 35 | //final 的引用类型方法参数 36 | publicstaticvoid checkLoginInfo(final LoginInfo login) 37 | { 38 | login = new LoginInfo();//error,编译不过去 39 | //do something 40 | } 41 | //非final的引用类型方法参数 42 | public staticvoid checkLoginInfo(LoginInfo login) 43 | { 44 | //没有任何问题,但是肯定不符合此参数存在的初衷 45 | login = new LoginInfo(); 46 | //do something 47 | } 48 | ``` 49 | 50 | ##finally关键字 51 | 用于try/catch语句中,表示这段语句最终总是被执行 52 | 53 | ##finalize关键字 54 | finalize 是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等. 55 | 56 | 一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法。并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存 -------------------------------------------------------------------------------- /java/javaBasic/others/interface-abstract.md: -------------------------------------------------------------------------------- 1 | # 接口与抽象类的异同 2 | ## 接口特性 3 | - 接口中**只**包含方法的**定义**,没有方法的实现 4 | 5 | - 接口中所有的方法都是**抽象的**(没有方法体) 6 | 7 | - 接口中的成员变量默认为`public static final`,必须赋予**初值** 8 | 9 | - 所有成员方法必须且只能是`public abstract` 10 | 11 | - 有些接口中没有声明任何方法,他就是标识接口,仅仅充当标识作用(用instanceof识别) 12 | 13 | ##接口与方法的异同 14 | 1. 都不能被实例化 15 | 16 | 2. 接口中的方法只有定义,抽象类中的某些方法可以被实现 17 | 18 | 3. 接口需要`implements`, 抽象类只能`extends` 19 | 20 | 4. 一个类可以实现多个接口,但是只能继承一个类 21 | -------------------------------------------------------------------------------- /java/javaBasic/others/override.md: -------------------------------------------------------------------------------- 1 | # 静态类型与实际类型 2 | ```java 3 | Human human = new Man(); 4 | ``` 5 | - Human是静态类型,编译时确定 6 | - Man是实际类型,运行时确定 7 | 8 | 虚拟机**重载**是通过参数的**静态类型**确定的,即编译时确定 9 | -------------------------------------------------------------------------------- /java/javaBasic/others/static.md: -------------------------------------------------------------------------------- 1 | # 关键字static 2 | |修饰的对象|作用| 3 | |--|--| 4 | |类中的成员变量|这个变量是静态变量,属于类,不属于某个实例(不需要创建对象可以使用);在内存中只有一个复制,在类加载时,这个变量就会被分配空间| 5 | |类中的方法|这个方法是静态方法,属于类,不需要创建对象可以使用| 6 | |代码块|加载类时会执行static代码块,且只被执行一次;通常用来初始化静态变量| 7 | |内部类|可以不依赖外部类被实例化| 8 | 9 | ## 常问问题 10 | - 静态方法只能调用静态方法、只能使用静态变量 -------------------------------------------------------------------------------- /java/javaBasic/platform.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/java/javaBasic/platform.md -------------------------------------------------------------------------------- /java/javaBasic/platform/README.md: -------------------------------------------------------------------------------- 1 | # Java跨平台 2 | java代码不是直接运行在CPU上,而是运行在java虚机(简称JVM)上的 3 | 4 | java是先把java文件编译成二进制字节码的class文件,jvm就解释执行class文件 5 | - 因为java是运行在jvm上的,所以它的代码就能不经修改,就能在不同平台的jvm上运行 6 | - 在UNIX用UNIX的jvm,在linux上用linux的jvm,在windows上用windows的jvm 7 | -------------------------------------------------------------------------------- /java/javaBasic/platform/bytecode.md: -------------------------------------------------------------------------------- 1 | # 字节码是什么 2 | 3 | 字节码就是一串二进制的字节流 4 | 5 | ## 字节码 6 | Java 字节码(Java bytecode)是 Java 虚拟机器执行的指令的形式。每个字节码指令在长度上是一个字节(因此叫这个名字) 7 | 8 | Java字节码时经过源文件(.java文件)经过编译后的产物(.class文件)。Java编译为字节码时,不会对变量和方法的引用编译为数值引用,也不确定执行过程中内存的布局,完成以上工作的是Java解释器 9 | 10 | **TIPS:** 11 | 一个.java的文件可能会被编译为多个.class文件 12 | 13 | ## 虚拟机与字节码的关系 14 | 虚拟机不和包括JAVA在内的任何语言绑定,只和"class文件"这种特定的字节码文件相关 15 | 16 | ## 字节码语言无关性 17 | 1. 虚拟机只与字节码相关,不关心class文件来源,可能是Java语言/Ruby语言编译得到) 18 | 2. 字节码的存储结构固定 -------------------------------------------------------------------------------- /java/javaBasic/platform/difference-jdk-jre.md: -------------------------------------------------------------------------------- 1 | # JDK与JRE的区别 2 | **JDK包含JRE** 3 | ## JRE 4 | Java运行环境(Java Runtime Enviroment) 是运行Java程序的基本的Java虚拟机,包括执行applet的浏览器插件 5 | ## JDK 6 | JDK (Java Development Kit) 是为了开发,编译和执行Java应用程序,针对Java的全功能的软件开发包,包含了JRE,编译器和工具(比如说 JavaDoc 和Java Debugger) 7 | 8 | -------------------------------------------------------------------------------- /java/javaBasic/platform/prnciple-platform.md: -------------------------------------------------------------------------------- 1 | # JVM跨平台的原理 2 | Java的独立性是通过JVM实现的,但是JVM是与平台相关的(32位、64位JVM等) 3 | ## 什么是JVM? 4 | Java虚拟机(Java Virtual Machine)是可以执行Java**字节码**的虚拟机,每个Java源文件将被编译成字节码文件,然后在JVM中执行。 5 | ##Java解释器 6 | Java解释器运行JVM字节码 7 | ###JVM字节码执行的两种方式 8 | 1. 即时编译方式(JIT):解释器将字节码编译成机器码,然后再执行机器码 9 | 2. 解释执行方式:解释器通过每次解释并执行一小段代码来完成Java字节码程序的操作 10 | 11 | 12 | -------------------------------------------------------------------------------- /java/javaBasic/reflection.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/java/javaBasic/reflection.md -------------------------------------------------------------------------------- /java/javaBasic/reflection/README.md: -------------------------------------------------------------------------------- 1 | # 反射机制 2 | 所谓的反射机制就是java语言在运行时可以进行自我检查,同时允许对其内部成员进行操作。 3 | ##反射机制的实现 4 | 反射机制主要借助以下4个类进行实现 5 | 1. Class :运行时的类对象 6 | 2. Constructor:类的构造器对象 7 | 3. Field:类的属性对象 8 | 4. Method:类的方法对象 9 | 10 | ##反射机制提供的功能 11 | 1. 得到一个对象所属的类 12 | 2. 获取一个类的所有成员变量和方法 13 | 3. 在运行时创建对象 14 | 4. 在运行时调用类方法 15 | 16 | ###如何获得class类? 17 | ```java 18 | Class.forName("类的路径"); 19 | 类名.class; 20 | 实例.getClass(); 21 | ``` -------------------------------------------------------------------------------- /java/javaBasic/reflection/create-object.md: -------------------------------------------------------------------------------- 1 | # Java中创建对象的4种方法 2 | 1. 通过new语句实例化一个对象 3 | 2. 通过反射机制创建对象 4 | 3. 通过clone()方法创建 5 | 4. 通过反序列化的方式创建 -------------------------------------------------------------------------------- /java/javaBasic/reflection/scene.md: -------------------------------------------------------------------------------- 1 | # 应用场景 2 | 反射可以用于: 3 | 1. Spring的IOC(控制注入) 4 | 2. Hibernater的JDBC封装 5 | 3. 白盒测试 6 | 7 | ###反射的例子1 8 | 如果想用一个list,但是在不同的场景下需要用ArrayList或者LinkedList,编译时无法确定是哪一个 9 | 10 | 这时可以利用反射,在运行时候通过配置文件之类的载入具体实现类的名字, 然后通过反射机制得到自己想要的list,如下: 11 | ```java 12 | List list = (List)Class.forName("ArrayList").newInstance(); 13 | ``` 14 | 15 | 类似于`switch(类名)`,然后通过`Class.forName().nesInstance()`得到想要引用的类型 16 | ###反射的例子2 17 | ![反射的例子2](http://askingwindy-gitcafe.qiniudn.com/反射的例子.png) -------------------------------------------------------------------------------- /java/javaBasic/reflection/tuning.md: -------------------------------------------------------------------------------- 1 | # 如何优化反射机制 2 | 1. 不要重复反射 3 | 2. 将最慢的反射结果缓存起来,如方法和成员的获取,从而较少“查找”地使用反射 4 | 3. 使用代码动态生成技术,通过调用代理类的方式来模拟反射 5 | -------------------------------------------------------------------------------- /java/javaBasic/string/README.md: -------------------------------------------------------------------------------- 1 | # String 2 | 在判断String存在什么地方时,需要先明确常量池与堆的概念 3 | 4 | ##常量池简单说明 5 | 常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量 6 | 7 | ##String存储位置 8 | - **编译时**能确定的变量直接存在常量池中 9 | - **运行时**才能确定的(运算符中包含变量),却是在堆空间创建对象 10 | 11 | ##intern()方法 12 | 存在于.class文件中的常量池,在运行期被JVM装载,并且可以**扩充** 13 | 14 | String的intern()方法就是扩充常量池的一个方法,当一个String实例str调用intern()方法时,Java查找常量池中是否有相同Unicode的字符串常量: 15 | - 如果有,则返回其的引用 16 | - 如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用 17 | 18 | ```java 19 | s3 = new String("123");//在堆空间中创建对象 20 | s4 = s3.intern();//s4指向的对象在常量池中 21 | ``` 22 | -------------------------------------------------------------------------------- /java/javaBasic/string/interview.md: -------------------------------------------------------------------------------- 1 | # 关于String的面试题 2 | Q1: `String s = new String("abc")`创建了几个对象? 3 | A: 1个或2个。如果常量池原来有“abc”,那么只创建一个对象放在堆。如果没有,那么还要在常量池中新创建一个“abc”。 4 | 5 | Q2: 请问s1==s3是true还是false,s1==s4是false还是true。s1==s5呢? 6 | ```java 7 | String s1 = "abc"; 8 | String s2 = "a"; 9 | String s3 = s2 + "bc"; 10 | String s4 = "a" + "bc"; 11 | String s5 = s3.intern();` 12 | ``` 13 | intern()方法:String类的intern方法,返回一个值相同的String对象,但是这个对象就像一个字符串字面常数一样,意思就是,他也到**字符串池**(常量池)里面去了。 14 | 15 | ```java 16 | String s1 = "abc"; 17 | String s2 = "a"; 18 | String s3 = s2 + "bc";(运行时才能决定,是new的,在堆中,s1!=s3) 19 | String s4 = "a" + "bc";(在字符串池中,s1==s4) 20 | String s5 = s3.intern();(是s3放到字符串池里返回的对象,即s1==s5) 21 | ``` 22 | -------------------------------------------------------------------------------- /java/javaBasic/string/string-stringbuffer-stringbuilder.md: -------------------------------------------------------------------------------- 1 | # String/StringBuffer/StringBuilder 2 | - String 字符串常量 3 | - StringBuffer 字符串变量(线程安全) 4 | - StringBuilder 字符串变量(非线程安全) 5 | 6 | String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是**不可变**的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象 7 | - 所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响 8 | - 特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的 9 | 10 | ## 字符串拼接效率比较 11 | 1. 直接使用`+` 最慢 12 | 13 | 2. 使用`StringBuilder.append()`,速度最快,但有线程安全问题,只有JDK5支持 14 | 15 | 3. Join和StringBuffer速度差不多 16 | 17 | 4. StringUtils:join(Object[] array, String separator),将字符串数组转成字符串(和split对应) 18 | 19 | 5. string(s).concat()比`+`快不了多少 20 | -------------------------------------------------------------------------------- /java/javaBasic/throwable.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/java/javaBasic/throwable.md -------------------------------------------------------------------------------- /java/javaBasic/throwable/README.md: -------------------------------------------------------------------------------- 1 | # 异常处理 2 | 3 | 异常:程序运行时所发生的非正常情况或错误 4 | 5 | ## 异常机制 6 | 7 | JAVA中把异常当做对象来处理,并定义了一个基类Throwable作为所有异常的父类 8 | 9 | ## Throwable类 10 | 11 | Throwable类是Java 语言中所有错误或异常的超类,他有两个子类:Error与Exception 12 | 13 | ### Error 14 | 15 | 表示恢复是很困难的一件事,他用来表示系统错误或者低层资源的错误 16 | 17 | ### Exception 18 | 19 | 表示一种设计或实现问题。通常由一个程序员导致的错误 20 | 21 | #### CheckedException 22 | 23 | 需要Try Catch显示的捕获,常见的 IOException 24 | 25 | #### UncheckedException 26 | 27 | 又称RuntimeException,主要包括: 28 | 29 | * NullPointerException 30 | * IndexOutOfBoundException 31 | * ClassCastException \(类型强制转换异常\) 32 | * IllegalArgumentException \(传递非法参数异常\) 33 | * ArrayStoreException \(向数组中存放与声明类型不兼容对象异常\) 34 | 35 | ## 异常捕获 36 | 37 | 对于try..catch捕获异常的形式来说,对于异常的捕获,可以有多个catch。 38 | 39 | 对于try里面发生的异常,他会根据发生的异常和catch里面的进行匹配\(怎么匹配,按照catch块从上往下匹配\),当它匹配某一个catch块的时候,他就直接进入到这个catch块里面去了,后面在再有catch块的话,它不做任何处理,直接跳过去,全部忽略掉。如果有finally的话进入到finally里面继续执行。 40 | 41 | 换句话说,如果有匹配的catch,它就会忽略掉这个catch后面所有的catch。 42 | 43 | ## 异常捕获的顺序 44 | 45 | catch\(\)语句中异常捕获范围必须从小到大 46 | 以下例子无法**编译**通过: 47 | 48 | ```java 49 | import java.io.IOException; 50 | public class ExceptionTryCatchTest { 51 | public void doSomething() throws IOException{ 52 | System.out.println("do somthing"); 53 | } 54 | public static void main(String[] args){ 55 | ExceptionTryCatchTest et = new ExceptionTryCatchTest(); 56 | try { 57 | et.doSomething(); 58 | } catch (Exception e) { 59 | // 60 | } catch (IOException e) { 61 | // 62 | } 63 | } 64 | } 65 | ``` 66 | 67 | 对我们这个方法来说,抛出的是IOException 68 | 当执行etct.doSomething\(\);时,一但抛出IOException,它首先进入到catch \(Exception e\) {}里面,先和Exception匹配,所以后面的catch都不会执行了,所以catch \(IOException e\) {}永远都执行不到,就给我们报出了**编译的错误**:已捕捉到异常 java.io.IOException。 69 | 70 | -------------------------------------------------------------------------------- /java/javaBasic/throwable/compare-c.md: -------------------------------------------------------------------------------- 1 | # 异常处理的作用(与C语言比较) 2 | * JAVA里能把代码正常功能和对异常的处理分开写,C里如果想处理异常就要把异常处理插到正常代码里 3 | * 如果涉及到一些资源关闭或者恢复之类的工作,C里只能处理完每个异常手动关闭一下,java里直接finally统一处理就行。try with resource 4 | * java对于不想在当前处理的异常还可以直接throws出去,让上层处理 -------------------------------------------------------------------------------- /java/javaBasic/throwable/finally.md: -------------------------------------------------------------------------------- 1 | #一道关于finally的面试题 2 | ##程序求输出 3 | ```java 4 | public class Test{ 5 | public static void main(String[] args){ 6 | System.out.println(new Test().test()); 7 | } 8 | public int test(){ 9 | int res = 1; 10 | try{ 11 | throw new Exception(); 12 | }catch(Exception e){ 13 | return res; 14 | }finally{ 15 | res = 2; 16 | System.out.println("finally"); 17 | } 18 | } 19 | } 20 | ``` 21 | ##输出结果: 22 | ```java 23 | finally 24 | 1 25 | ``` 26 | ##解释: 27 | return 之前跳到finally,此时要return 的值已经被存到一个局部引用变量里了,finally中对res的改变对返回值没有影响 28 | -------------------------------------------------------------------------------- /java/jvm.md: -------------------------------------------------------------------------------- 1 | 2 | ## jvm 总体梳理 3 | 4 | jvm体系总体分四大块: 5 | 6 | - 类的加载机制 7 | 8 | - jvm内存结构 9 | 10 | - GC算法 垃圾回收 11 | 12 | - GC分析 命令调优 13 | 14 | 15 | 这里画了一个思维导图,将所有的知识点进行了陈列,因为图比较大可以点击右键下载了放大查看。 16 | 17 | 18 | ![](http://www.ityouknow.com/assets/images/2017/jvm/JVM.jpg) 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /java/jvm/Class-loading-mechanism.md: -------------------------------------------------------------------------------- 1 | ## 类的加载机制 2 | 3 | 主要关注点: 4 | 5 | - 什么是类的加载 6 | 7 | - 类的生命周期 8 | 9 | - 类加载器 10 | 11 | - 双亲委派模型 12 | 13 | **什么是类的加载** 14 | 15 | 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。 16 | 17 | **类的生命周期** 18 | 19 | 类的生命周期包括这几个部分,加载、连接、初始化、使用和卸载,其中前三部是类的加载的过程,如下图; 20 | 21 | 22 | ![](http://www.ityouknow.com/assets/images/2017/jvm/class.png) 23 | 24 | - 加载,查找并加载类的二进制数据,在Java堆中也创建一个java.lang.Class类的对象 25 | 26 | - 连接,连接又包含三块内容:验证、准备、初始化。1)验证,文件格式、元数据、字节码、符号引用验证;2)准备,为类的静态变量分配内存,并将其初始化为默认值;3)解析,把类中的符号引用转换为直接引用 27 | 28 | - 初始化,为类的静态变量赋予正确的初始值 29 | 30 | - 使用,new出对象程序中使用 31 | 32 | - 卸载,执行垃圾回收 33 | 34 | > *几个小问题?* 35 | 36 | > *1、JVM初始化步骤 ? 2、类初始化时机 ?3、哪几种情况下,Java虚拟机将结束生命周期?* 37 | 38 | > *答案参考这篇文章[jvm系列(一):java类的加载机制](http://www.cnblogs.com/ityouknow/p/5603287.html)* 39 | 40 | **类加载器** 41 | 42 | 43 | ![](http://www.ityouknow.com/assets/images/2017/jvm/calssloader.png) 44 | 45 | - 启动类加载器:Bootstrap ClassLoader,负责加载存放在JDK\jre\lib(JDK代表JDK的安装目录,下同)下,或被-Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库 46 | 47 | - 扩展类加载器:Extension ClassLoader,该加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载DK\jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库(如javax.*开头的类),开发者可以直接使用扩展类加载器。 48 | 49 | - 应用程序类加载器:Application ClassLoader,该类加载器由sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器 50 | 51 | **类加载机制** 52 | 53 | - 全盘负责,当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入 54 | 55 | - 父类委托,先让父类加载器试图加载该类,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类 56 | 57 | - 缓存机制,缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效 58 | 59 | 60 | **类加载** 61 | 62 | 类只有被加载到JVM后才能运行,这个加载的过程是由类加载器完成的,由ClassLoader和它的子类来实现。 63 | 64 | 类加载的实质是把类文件从**硬盘**读取到内存中。 65 | 66 | **类加载的分类** 67 | 68 | 1. 显式加载:直接调用class.forName()来创建对象 69 | 70 | 2. 隐式加载:使用new来创建对象,会隐式的调用类加载器 71 | 72 | **类加载的特性** 73 | 74 | 1. 通过全限定名定义二进制字节流 75 | 76 | 2. 字节流的静态存储结构转化为方法区运行时的数据结构 77 | 78 | 3. 在内存中生产**java.lang.Class**, 作为方法区对这个类的访问入口 79 | 80 | 4. 动态的, 并不会一次性对所有类进行加载;除了基础类外,其他类仅在需要时才运行。 81 | 82 | 83 | **Class文件格式 ** 84 | 85 | | 类型 |名称|物理意义|数量| 86 | |--|--|--|--| 87 | |u4|magic|魔数|1| 88 | |u2|minor_version|版本号|1| 89 | |u2|major_version|版本号|1| 90 | |u2|constant_pool_count|常量池入口|1| 91 | |cp_info|constant_pool|常量池入口|1| 92 | |u2|access_flags|访问标志|1| 93 | |u2|this_class|类索引|1| 94 | |u2|super_class|类索引|1| 95 | |u2|interface_count|接口索引集合|1| 96 | |u2|interface|接口索引集合|interface_count| 97 | |u2|fields_count|字段表|1| 98 | |u2|fields|字段表|fields_count| 99 | |field_info|fields|字段表|fields_count| 100 | |u2|methods_count|方法表集合|methods_count| 101 | |method_info|methods|方法表集合|methods_count| 102 | |u2|attribute_count|属性表|1| 103 | |attribute_info|attributes|属性表|attributes_count| 104 | 105 | 106 | 107 | > *参考 [jvm系列(一):java类的加载机制](http://www.cnblogs.com/ityouknow/p/5603287.html)* 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /java/jvm/GC-analysis.md: -------------------------------------------------------------------------------- 1 | ## GC分析 命令调优 2 | 3 | 主要关注点: 4 | 5 | - GC日志分析 6 | 7 | - 调优命令 8 | 9 | - 调优工具 10 | 11 | **GC日志分析** 12 | 13 | 摘录GC日志一部分(前部分为年轻代gc回收;后部分为full gc回收): 14 | 15 | ``` xml 16 | 17 | 2016-07-05T10:43:18.093+0800: 25.395: [GC [PSYoungGen: 274931K->10738K(274944K)] 371093K->147186K(450048K), 0.0668480 secs] [Times: user=0.17 sys=0.08, real=0.07 secs] 18 | 19 | 2016-07-05T10:43:18.160+0800: 25.462: [Full GC [PSYoungGen: 10738K->0K(274944K)] [ParOldGen: 136447K->140379K(302592K)] 147186K->140379K(577536K) [PSPermGen: 85411K->85376K(171008K)], 0.6763541 secs] [Times: user=1.75 sys=0.02, real=0.68 secs] 20 | 21 | ``` 22 | 23 | 通过上面日志分析得出,PSYoungGen、ParOldGen、PSPermGen属于Parallel收集器。其中PSYoungGen表示gc回收前后年轻代的内存变化;ParOldGen表示gc回收前后老年代的内存变化;PSPermGen表示gc回收前后永久区的内存变化。young gc 主要是针对年轻代进行内存回收比较频繁,耗时短;full gc 会对整个堆内存进行回城,耗时长,因此一般尽量减少full gc的次数 24 | 25 | young gc 日志: 26 | 27 | {:.center} 28 | 29 | ![](http://www.ityouknow.com/assets/images/2017/jvm/yong.jpg) 30 | 31 | Full GC日志: 32 | 33 | ![](http://www.ityouknow.com/assets/images/2017/jvm/full.jpg) 34 | 35 | **调优命令** 36 | 37 | Sun JDK监控和故障处理命令有jps jstat jmap jhat jstack jinfo 38 | 39 | - jps,JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。 40 | 41 | - jstat,JVM statistics Monitoring是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。 42 | 43 | - jmap,JVM Memory Map命令用于生成heap dump文件 44 | 45 | - jhat,JVM Heap Analysis Tool命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看 46 | 47 | - jstack,用于生成java虚拟机当前时刻的线程快照。 48 | 49 | - jinfo,JVM Configuration info 这个命令作用是实时查看和调整虚拟机运行参数。 50 | 51 | > *详细的命令使用参考这里[jvm系列(四):jvm调优-命令篇](http://www.ityouknow.com/java/2016/01/01/jvm%E8%B0%83%E4%BC%98-%E5%91%BD%E4%BB%A4%E7%AF%87.html)* 52 | 53 | **调优工具** 54 | 55 | 常用调优工具分为两类,jdk自带监控工具:jconsole和jvisualvm,第三方有:MAT(Memory Analyzer Tool)、GChisto。 56 | 57 | - jconsole,Java Monitoring and Management Console是从java5开始,在JDK中自带的java监控和管理控制台,用于对JVM中内存,线程和类等的监控 58 | 59 | - jvisualvm,jdk自带全能工具,可以分析内存快照、线程快照;监控内存变化、GC变化等。 60 | 61 | - MAT,Memory Analyzer Tool,一个基于Eclipse的内存分析工具,是一个快速、功能丰富的Java heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗 62 | 63 | - GChisto,一款专业分析gc日志的工具 64 | 65 | > *工具使用参考 [jvm系列(七):jvm调优-工具篇](http://www.ityouknow.com/java/2017/02/22/jvm-tool.html)* 66 | 67 | 68 | -------------------------------------------------------------------------------- /java/jvm/README.md: -------------------------------------------------------------------------------- 1 | # JVM 2 | 3 | Java虚拟机(Java Virtual Machine,缩写为JVM)是一种能够运行Java bytecode的虚拟机,以堆栈结构机器来进行实做 4 | 5 | JVM有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统 6 | 7 | - JVM屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行(平台无关性) 8 | 9 | - 作为一种编程语言的虚拟机,只要生成的编译文件符合JVM对载入编译文件格式要求,任何语言都可以由JVM编译运行 10 | 11 | ##JVM实例 12 | 13 | 当启动一个Java程序时,一个JVM实例就产生了,任何一个拥有`public static void main(String[]args)`函数的class都可以作为JVM实例运行的起点 14 | 15 | - 需要显式的告诉JVM类名,也就是我们平时运行Java程序命令的由来,如`Java classA helloworld`,这里Java是告诉os运行SunJava2SDK的Java虚拟机,而classA则指出了运行JVM所需要的类名。 16 | 17 | ##JVM实例的生存周期 18 | 19 | ### JVM实例的运行 20 | 21 | `main()`作为该程序初始线程的起点,任何其他线程均由该线程启动 22 | 23 | JVM内部有两种线程 24 | 25 | - 守护线程 26 | 27 | - 非守护线程 28 | 29 | `main()`属于**非**守护线程,守护线程通常由JVM自己使用,Java程序也可以标明自己创建的线程是守护线程 30 | 31 | ### JVM实例的消亡 32 | 33 | 当程序中的所有非守护线程都终止时,JVM才退出;若安全管理器允许,程序也可以使用Runtime类或者System.exit()来退出。 34 | 35 | 36 | -------------------------------------------------------------------------------- /java/jvm/gc-algorithm-garbage.md: -------------------------------------------------------------------------------- 1 | ## GC算法 垃圾回收 2 | 3 | 主要关注点: 4 | 5 | - 对象存活判断 6 | 7 | - GC算法 8 | 9 | - 垃圾回收器 10 | 11 | **对象存活判断** 12 | 13 | 判断对象是否存活一般有两种方式: 14 | 15 | - 引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。 16 | 17 | - 可达性分析(Reachability Analysis):从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,不可达对象。 18 | 19 | **GC算法** 20 | 21 | GC最基础的算法有三种:标记 -清除算法、复制算法、标记-压缩算法,我们常用的垃圾回收器一般都采用分代收集算法。 22 | 23 | - 标记 -清除算法,“标记-清除”(Mark-Sweep)算法,如它的名字一样,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。 24 | 25 | - 复制算法,“复制”(Copying)的收集算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。 26 | 27 | - 标记-压缩算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存 28 | 29 | - 分代收集算法,“分代收集”(Generational Collection)算法,把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。 30 | 31 | **垃圾回收器** 32 | 33 | - Serial收集器,串行收集器是最古老,最稳定以及效率高的收集器,可能会产生较长的停顿,只使用一个线程去回收。 34 | 35 | - ParNew收集器,ParNew收集器其实就是Serial收集器的多线程版本。 36 | 37 | - Parallel收集器,Parallel Scavenge收集器类似ParNew收集器,Parallel收集器更关注系统的吞吐量。 38 | 39 | - Parallel Old 收集器,Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法 40 | 41 | - CMS收集器,CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。 42 | 43 | - G1收集器,G1 (Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征 44 | 45 | > *GC算法和垃圾回收器算法图解以及更详细内容参考 [jvm系列(三):GC算法 垃圾收集器](http://www.cnblogs.com/ityouknow/p/5614961.html)* 46 | 47 | 48 | -------------------------------------------------------------------------------- /java/jvm/java-model.md: -------------------------------------------------------------------------------- 1 | # Java的内存模型 2 | ##物理机器上的内存访问 3 | 对于频繁访问的数据,我们总是习惯把它们放到内存缓存中 4 | 5 | 但是CPU的运算速度比起内存的访问速度还要快几个量级,为了平衡这个差距,于是就专门为CPU引入了高速缓存,频繁使用的数据放到高速缓存当中 6 | 7 | 每个CPU都拥有自己的高速缓存,内存又是所有CPU共享的公共资源,于是内存此时就成了一个临界区,如果控制不好各个CPU对内存的并发访问,那么就会产生错误,出现数据不一致的情 8 | 9 | ![硬件模型](http://7d9o4k.com1.z0.glb.clouddn.com/硬件内存模型.PNG) 10 | 11 | ##Java内存模型 12 | Java内存模型与物理机器上内存的访问具有可比性 13 | 14 | - Java内存模型有一套自己的同步协议来屏蔽掉各种底层硬件和操作系统的不同,实现了跨平台 15 | 16 | - 对于Java来说开发者并不需要关心任何硬件细节,多核CPU和高速缓存在JVM中对应的是Java语言内置的线程和每个线程所拥有的独立栈空间 17 | 18 | ![JVM内存模型](http://7d9o4k.com1.z0.glb.clouddn.com/jvm内存模型.PNG) 19 | 20 | ### 主内存与工作内存 21 | 22 | * 新变量只能在**主内存**诞生 23 | * **不允许**工作内存直接使用未被初始化的变量(意思是在对变量实施use,store前,必须先assign和Load) 24 | 25 | #### 主内存 26 | 27 | * 所有的变量都存储在主内存中(不包括局部变量与方法参数;局部变量如果是reference,它自身存储在线程私有的内存中,但是它指向的内容存在堆中) 28 | * 变量:实例字段、静态字段、构成数组的元素 29 | 30 | #### 工作内存 31 | 32 | * 每条线程有自己的工作内存,保存主内存的副本拷贝 33 | 34 | * 线程对变量的所有操作(赋值、读取等)都在工作内存中进行,不能直接对主内存进行操作 35 | 36 | ### 原子操作 37 | 一共有8个操作: 38 | 39 | 40 | 41 | | 原子操作 | 意义 | 操作地 |备注| 42 | |--------|--------|-----|------| 43 | | lock | 锁定变量,为一个线程独占 |主| | 44 | |unlock|解除锁定|主|| 45 | |read|主内存的变量值读到工作内存中|主| | 46 | |load|read后,将值保存在工作内存副本中|工作|与read成对出现| 47 | |use|变量值传递给线程代码执行引擎|工作| | 48 | |assigh|执行引擎处理返回的值重新复制给变量副本|工作| | 49 | |store|变量副本的值存储到主内存中|工作| | 50 | |write|store后,将值写入到主内存共享变量中|主|与store成对出现| 51 | 52 | - 变量在同一时刻只允许一个线程对其进行lock,有多少次lock操作,就必须有多少次unlock操作。在lock操作之后会清空此变量在工作内存中原先的副本,需要再次从主内存read-load新的值。在执行unlock操作前,需要把改变的副本同步回主存 -------------------------------------------------------------------------------- /java/jvm/jvm-Memory-structure.md: -------------------------------------------------------------------------------- 1 | ## jvm内存结构 2 | 3 | 主要关注点: 4 | 5 | - jvm内存结构都是什么 6 | 7 | - 对象分配规则 8 | 9 | **jvm内存结构** 10 | 11 | ![](http://www.ityouknow.com/assets/images/2017/jvm/structure.png) 12 | 13 | > 方法区和堆是所有线程共享的内存区域;而java栈、本地方法栈和程序计数器是运行是线程私有的内存区域。 14 | 15 | - Java堆(Heap),是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。 16 | 17 | - 方法区(Method Area),方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。 18 | 19 | - 程序计数器(Program Counter Register),程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。 20 | 21 | - JVM栈(JVM Stacks),与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。 22 | 23 | - 本地方法栈(Native Method Stacks),本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。 24 | 25 | **对象分配规则** 26 | 27 | - 对象优先分配在Eden区,如果Eden区没有足够的空间时,虚拟机执行一次Minor GC。 28 | 29 | - 大对象直接进入老年代(大对象是指需要大量连续内存空间的对象)。这样做的目的是避免在Eden区和两个Survivor区之间发生大量的内存拷贝(新生代采用复制算法收集内存)。 30 | 31 | - 长期存活的对象进入老年代。虚拟机为每个对象定义了一个年龄计数器,如果对象经过了1次Minor GC那么对象会进入Survivor区,之后每经过一次Minor GC那么对象的年龄加1,知道达到阀值对象进入老年区。 32 | 33 | - 动态判断对象的年龄。如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代。 34 | 35 | - 空间分配担保。每次进行Minor GC时,JVM会计算Survivor区移至老年区的对象的平均大小,如果这个值大于老年区的剩余值大小则进行一次Full GC,如果小于检查HandlePromotionFailure设置,如果true则只进行Monitor GC,如果false则进行Full GC。 36 | 37 | ## JVM启动参数的意义 38 | ### `-verbose` 39 | 40 | ###用法: 41 | 42 | 1. `-verbose:class` 43 | 2. `-verbose:gc` 44 | 3. `-verbose:jni` 45 | 46 | ###意义: 47 | 48 | 1. `class`: 将类 加载情况在控制台中输出 49 | 2. `gc`:将虚拟机的垃圾回收事件信息打印 50 | 3. `jni`:将本地方法调用信息打印 51 | 52 | ##启动时内存分配 53 | - `-Xms`:设置堆内存初始值 54 | - `-XX:PermSize`:设置非堆内存初始值 55 | - `-Xmx`:设置JAVA 堆内存的最大值, 取决于`-Xms`的设置 56 | - `-Xss`:设置每个线程的Stack大小 57 | 58 | 59 | > *参考此文章:[jvm系列(二):JVM内存结构](http://www.cnblogs.com/ityouknow/p/5610232.html)* 60 | 61 | 62 | -------------------------------------------------------------------------------- /java/network-communication/README.md: -------------------------------------------------------------------------------- 1 | # 网络通信 2 | 主要是TCP与HTTP,涉及到计算机网络基础知识,同时包括了网络通信编程 3 | -------------------------------------------------------------------------------- /java/network-communication/http/README.md: -------------------------------------------------------------------------------- 1 | # HTTP 2 | HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传送协议。它可以使浏览器更加高效,使网络传输减少。 3 | 4 | 它不仅保证计算机正确快速地传输超文本文档,还确定传输文档中的哪一部分,以及哪部分内容首先显示(如文本先于图形)等。 5 | 6 | - HTTP是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器模 7 | 8 | - HTTP是一个无状态的协议 9 | 10 | ##HTTP在协议栈中的位置 11 | HTTP协议通常承载于TCP协议之上,有时也承载于TLS或SSL协议层之上(HTTPS) 12 | 13 | 默认HTTP的端口号为80,HTTPS的端口号为443 14 | 15 | ##HTTP请求响应模型 16 | HTTP协议**永远**都是客户端发起请求,服务器回送响应 17 | 18 | - HTTP协议无法实现在客户端没有发起请求的时候,服务器将消息推送给客户端 19 | 20 | - HTTP协议是一个**无状态**的协议,同一个客户端的这次请求和上次请求是没有对应关系 -------------------------------------------------------------------------------- /java/network-communication/http/cache.md: -------------------------------------------------------------------------------- 1 | # Web缓存与客户端缓存 2 | 3 | ##Web缓存 4 | ###WHAT 5 | * 位于Web服务器与客户端之间 6 | 7 | * 缓存会根据请求保存输出内容的副本 8 | 9 | ###WHY 10 | 11 | * 减少延迟、减少网络消耗带宽 12 | 13 | ###HOW 14 | * 服务器收到请求时,会在 200OK中回送该资源的 Last-Modified和 ETag头,客户端将该资源保存在 cache中,并记录这两个属性 15 | 16 | * 当客户端需要发送相同的请求时,会在请求中携带 If-Modified-Since和 If-None-Match两个头。两个头的值分别是响应中 Last-Modified和 ETag头的值 17 | 18 | * 服务器过这两个头判断本地资源未发生变化,客户端不需要重新下载,返回 304响应 19 | 20 | ##客户端缓存 21 | * 浏览器缓存有效,不必再向服务器申请资源 22 | 23 | * 缓存过期,再次请求新资源 24 | 25 | 26 | -------------------------------------------------------------------------------- /java/network-communication/http/communication.md: -------------------------------------------------------------------------------- 1 | # Http通信数据转发过程 2 | 1. 代理 3 | * 利用缓存,减少网络带宽流量 4 | 5 | * 分为:是否使用缓存;是否更改报文 6 | 7 | 2. 网关 8 | * 提高安全性、提供非HTTP得服务 9 | 10 | * Web购物连接信用卡结算时用到的 11 | 12 | * ![网关示意](http://7d9o4k.com1.z0.glb.clouddn.com/gateway.png) 13 | 14 | 3. 隧道 15 | * +ssl 16 | 17 | 18 | -------------------------------------------------------------------------------- /java/network-communication/http/cookie-session.md: -------------------------------------------------------------------------------- 1 | # Cookie与Session 2 | Cookie与Session都是为了用来保存状态信息,保存客户端状态的机制,它们是我了解决HTTP无状态问题而努力 3 | 4 | - Session可以用Cookie来实现,也可以用URL回写机制实现 5 | 6 | ![Cookie与Session](http://7d9o4k.com1.z0.glb.clouddn.com/cookie-session.png) 7 | 8 | ##Session 9 | Session是一种**服务器端**的机制,服务器使用一种类似于散列表的结构来保存信息(Session id来保存用户信息) 10 | 11 | ###Cookie实现Session机制 12 | 1. 服务器给每个 Session分配一个唯一的 JSESSIONID, 并通过 Cookie发送给客户端 13 | 14 | 2. 当客户端发起新的请求的时候,将在 Cookie头中携带这个 JSESSIONID。这样服务器能够找到这个客户端对应的 Session 15 | 16 | ![Cookie实现Session机制](http://7d9o4k.com1.z0.glb.clouddn.com/cookie-session-2.png) 17 | 18 | ###URL回写实现Session机制 19 | 服务器在发送给浏览器页面的所有链接中都携带 JSESSIONID的参数,这样客户端点击任何一个链接都会把 JSESSIONID带会服务器 20 | 21 | ##Cookie 22 | Cookies是服务器在本地机器上存储的**小段文本**,并随每一个请求发送至同一个服务器,用来保存一种状态 23 | 24 | ##两者比较 25 | * Cookie将状态保存在客户端, Session将状态保存在服务器端 26 | 27 | * Session是针对每一个用户的,变量的值保存在服务器上,用一个sessionID来区分是哪个用户session变量,这个值是通过用户的浏览器在访问的时候返回给服务器,当客户禁用cookie时,这个值也可能设置为由get来返回给服务器 28 | 29 | ##具体应用-基于表单的认证 30 | 1. 客户端发送:已登录信息(username && passport)=>服务器端 31 | 32 | 2. 服务器端回:包含session_id的Cookie =>客户端 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /java/network-communication/http/get-post.md: -------------------------------------------------------------------------------- 1 | # GET与POST的区别 2 | GET:一般用于信息**获取**,使用URL传递参数,对所发送信息的数量也有限制,一般在2000个字符 3 | POST:一般用于**修改**服务器上的资源,对所发送的信息没有限制 4 | 5 | GET方式需要使用 Request.QueryString 来取得变量的值 6 | POST方式通过 Request.Form 来获取变量的值 7 | 也就是说 Get 是通过**地址栏**来传值,而 Post 是通过**提交表单**来传值。 8 | 9 | 10 | 在以下情况中,请使用 POST 请求: 11 | 1. 无法使用缓存文件(更新服务器上的文件或数据库) 12 | 2. 向服务器发送大量数据(POST 没有数据量限制) 13 | 3. 发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠 -------------------------------------------------------------------------------- /java/network-communication/http/https.md: -------------------------------------------------------------------------------- 1 | # Https 2 | HTTPS = HTTP + 加密(SSL) + 认证 + 内容完整性 3 | 4 | * Why not always HTTPS? 5 | https更加消耗CPU与内存 6 | 7 | ##加密过程 8 | 1. 对称加密(只有一个密钥) 9 | 10 | 2. 非对称加密(公钥+密钥) 11 | 12 | ![加密](http://7d9o4k.com1.z0.glb.clouddn.com/https.png) 13 | 14 | ##通信过程 15 | 2-4:服务器向客户端传递公钥 16 | 17 | 5-7:客户端给服务器发送密钥 18 | 19 | ![加密过程](http://7d9o4k.com1.z0.glb.clouddn.com/https-com.png) 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /java/network-communication/http/partial-content.md: -------------------------------------------------------------------------------- 1 | # 续传与多线程下载原理 2 | 3 | ##断点续传:HTTP协议的 GET方法,支持只请求某个资源的某一部分 4 | * 响应码:206 Partial Content 部分内容响应 5 | 6 | * Range 请求的资源范围 7 | 8 | * Content-Range 响应的资源范围 9 | 10 | ##多线程下载 11 | * 下载工具开启多个发出 HTTP请求的线程 12 | 13 | * 每个 http请求只请求资源文件的一部分: Content-Range: bytes 20000-40000/47000 14 | 15 | * 合并每个线程下载的文件 16 | 17 | 18 | -------------------------------------------------------------------------------- /java/network-communication/http/status-code.md: -------------------------------------------------------------------------------- 1 | # 状态码 2 | HTTP 状态码是用以表示网页服务器HTTP响应状态的3位数字代码 3 | 4 | 第一个数字代表响应的五种状态之一: 5 | - **1**:代表消息 6 | - **2**:代表成功 7 | - **3**:代表重定向 8 | - **4**:代表请求错误 9 | - **5**:代表服务器错误 10 | 11 | ##1xx:信息 12 | - 101:Switching Protocols, 服务器讲遵从客户的请求转换到另外一种协议 13 | 14 | ##2xx:成功 15 | - 200:OK, 请求成功 16 | - 201:Created, 请求被创建完成,同时新的资源被创建 17 | 18 | ##3xx:重定向 19 | - 301: Moved Permanently, 锁清秋的页面转移到新的url 20 | 21 | ##4xx:客户端错误 22 | - 401:Unauthorized, 被请求的页面需要用户名和密码 23 | - 404:Not Found, 服务器找不到被请求的页面 24 | 25 | ##5xx:服务器错误 26 | - 501:Not Implemented, 请求未完成,服务器不支持所请求的功能 -------------------------------------------------------------------------------- /java/network-communication/http/url-uri.md: -------------------------------------------------------------------------------- 1 | # URL与URI 2 | ##URL 3 | URL:统一资源定位符(一般指网址) 4 | ### URL访问网页的流程(HTTP是基于TCP的) 5 | 6 | HTTP是基于TCP的 7 | 8 | ![URL访问页面过程](http://7d9o4k.com1.z0.glb.clouddn.com/url.png) 9 | 10 | 11 | ##URI 12 | 13 | 统一资源标示符(包括了URL,可标示互联网资源) 14 | 15 | ![绝对URI](http://7d9o4k.com1.z0.glb.clouddn.com/uri.png) -------------------------------------------------------------------------------- /java/network-communication/http/version.md: -------------------------------------------------------------------------------- 1 | # Http1.0与1.1 2 | ## Http 1.0 3 | 4 | 1. 一定是客户端发起的请求 5 | 6 | 2. 服务器对每个请求都要响应(短连接) 7 | 8 | 3. 不保存状态的协议==>快速处理大量的数据 9 | 10 | 11 | ## Http 1.1 12 | 13 | 14 | 1. 增加了Cookie管理状态 15 | 16 | 2. 长连接 17 | 18 | 3. 使用了管线技术 19 | ![管线技术](http://7d9o4k.com1.z0.glb.clouddn.com/long-pipeline.png) 20 | 21 | 22 | -------------------------------------------------------------------------------- /java/network-communication/serizalizable.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/java/network-communication/serizalizable.md -------------------------------------------------------------------------------- /java/network-communication/serizalizable/README.md: -------------------------------------------------------------------------------- 1 | # 序列化与反序列化 2 | ##什么是序列化? 3 | * 使用Java的序列化和反序列化可以实现信息的持久存储、或者也可以实现Java对象的深克隆 4 | 5 | * 为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来 6 | 7 | ##什么情况下可以序列化? 8 | * 想把的内存中的对象状态保存到一个文件中或者数据库中时候 9 | 10 | * 想用套接字在网络上传送对象的时候 11 | 12 | ##对象序列化的含义 13 | - Java 序列化技术可以使你将一个对象的状态写入一个Byte 流里,并且可以从其它地方把该Byte 流里的数据读出来,重新构造一个相同的对象。 14 | 15 | - 这种机制允许你将对象通过网络进行传播,并可以随时把对象持久化到数据库、文件等系统里 16 | 17 | ##什么是反序列化? 18 | 19 | * 反序列化的时候,并没有调用类的构造方法。而是直接根据他们的序列化数据在内存中创建新的对像 20 | 21 | ##其他 22 | * 序列化时,只对对象的**状态**进行保存,而不管对象的方法 23 | 24 | * Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常 25 | * 虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 `private static final long serialVersionUID = 1L`,虽然两个类的功能代码完全一致,但是序列化 ID 不同,他们无法相互序列化和反序列化 26 | 27 | * 加入**transient**关键字的field不会被序列化 28 | 29 | * 当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口 30 | 31 | * 当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化 32 | 33 | * 并非所有的对象都可以序列化 34 | * 安全原因:如果一个private实例被序列化后,在传输过程中不再受到保护 35 | 36 | 37 | -------------------------------------------------------------------------------- /java/network-communication/serizalizable/interview.md: -------------------------------------------------------------------------------- 1 | # 常见面试题 2 | 3 | ##1. Serializable and Externalizable 接口的区别是什么? 4 | * Externalizable继承了Serializable,并增加了两个新方法writeExternal()和readExternal(),只有在新方法中手动序列化的field才会被序列化 5 | 6 | * Externalizable接口默认的数据化是不被序列化的 7 | 8 | ##2. Serializable接口中有多少方法?作用是什么? 9 | * 0个方法,只是一个标记接口 10 | 11 | * 为了提醒编译器使用JAVA序列化机制来序列化对象 12 | 13 | ##3. 什么是serialVersionUID?它的作用是什么? 14 | * `public static final`的常量 15 | 16 | * 根据此id保证序列化后的对象能正确被反序列化 17 | 18 | * Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常 19 | 20 | ##4. 对于不需要序列化的field,该如何声明? 21 | * transient与static的变量不会被实例化 22 | 23 | ##5. 如果需要序列化的类里面引用了一个实例,这个实例并没有实现Serializable接口,会怎样? 24 | * 运行时抛出`NotSerializableException`异常 25 | 26 | ##6. 如果子类实现了Serializable接口,而它的父类没有实现,从父类继承的field反序列化后会怎样? 27 | * 对Serializable对象反序列化时,并不会调用任何构造函数 ,因此Serializable类无需默认构造函数,但是当Serializable类的父类没有实现Serializable接口时,反序列化过程会调用父类的默认构造函数,因此该父类必需有默认构造函数,否则会抛异常 28 | 29 | * 父类的属性是直接被跳过不保存 30 | 31 | 32 | -------------------------------------------------------------------------------- /java/network-communication/serizalizable/java-serizaliable.md: -------------------------------------------------------------------------------- 1 | # Java中的序列化与反序列化 2 | 3 | * java.io包有两个序列化对象的类。ObjectOutputStream负责将对象写入字节流,ObjectInputStream从字节流重构对象 4 | 5 | 使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态 6 | 7 | ![序列化与反序列化代码](http://7d9o4k.com1.z0.glb.clouddn.com/serizable.PNG) 8 | 9 | -------------------------------------------------------------------------------- /java/network-communication/serizalizable/transient.md: -------------------------------------------------------------------------------- 1 | # transient关键字 2 | 1. 一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问 3 | 4 | 2. transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口 5 | 6 | 3. 被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化 -------------------------------------------------------------------------------- /java/network-communication/tcp-udp.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/java/network-communication/tcp-udp.md -------------------------------------------------------------------------------- /java/network-communication/tcp-udp/README.md: -------------------------------------------------------------------------------- 1 | # TCP/UDP 2 | TCP的目的是提供可靠的数据传输,并在相互进行通信的设备或服务之间保持一个虚拟连接。TCP在数据包接收无序、丢失或在交付期间被破坏时,负责数据恢复。它通过为其发送的每个数据包提供一个序号来完成此恢复。 3 | 4 | || TCP | UDP | 5 | |---|--------|--------| 6 | |是否连接| 面向连接 | 面向非连接 | 7 | |传输可靠性|可靠|不可靠| 8 | |应用场合|传输大量数据|少量数据| 9 | |速度|慢|快| 10 | 11 | ##理解面向连接 12 | 面向连接的协议维护了分组之间的状态,使用这种协议的应用程序通常都会进行长期的对话。 13 | - 发送端可以记住哪些数据已经发送出去了但还未被确认,以及数据是什么时候发送的。如果在某段时间间隔内没有收到确认,发送端可以重传数据。接收端可以记住已经收到了哪些数据,并将重复的数据丢弃。如果分组不是按序到达的,接收端可以将其保存下来,直到逻辑上先于它的分组到达为止 14 | 15 | ##TCP/IP四层协议 16 | 17 | ![TCP/IP四层协议](http://7d9o4k.com1.z0.glb.clouddn.com/tcp-ip.PNG) 18 | 19 | ##面试题 20 | ###Http是基于TCP 21 | 22 | 23 | ###QQ聊天采用UDP协议 24 | 25 | QQ的登陆采用TCP协议和HTTP协议,你和好友之间发送消息,主要采用UDP协议,内网传文件采用了P2P技术。总来的说: 26 | 27 | 1. 登陆过程,客户端client 采用TCP协议向服务器server发送信息,HTTP协议下载信息。登陆之后,会有一个TCP连接来保持在线状态。 28 | 29 | 2. 和好友发消息,客户端client采用UDP协议,但是需要通过服务器转发。腾讯为了确保传输消息的可靠,采用上层协议来保证可靠传输。如果消息发送失败,客户端会提示消息发送失败,并可重新发送。 30 | 31 | 3. 如果是在内网里面的两个客户端传文件,QQ采用的是P2P技术,不需要服务器中转。 -------------------------------------------------------------------------------- /java/network-communication/tcp-udp/non-block-socket.md: -------------------------------------------------------------------------------- 1 | # 非阻塞SOCKET 2 | ##非阻塞的通信 3 | **ACCEPT**:立即返回。如果没有client连接则返回null,有连接则返回SocketChannel(相当于阻塞网络通信里的Socket) 4 | 5 | **READ**:立即返回 6 | - `read()`:如果读不了则立即返回 7 | - `read(ByteBuffer)`:尽量读,可能读几个甚至0个字节,也可能返回-1表示连接关闭 8 | 9 | **WRITE**:立即返回 10 | 可能写入几个甚至0个字节 11 | 12 | ##哪些是非阻塞 13 | NIO之前的IO都是**阻塞的**,NIO里面也有阻塞、非阻塞之分 14 | 15 | ![Channel子类](http://7d9o4k.com1.z0.glb.clouddn.com/nio-1.png) 16 | 17 | - 阻塞NIO是继承于AbstractInterruptibleChannel,例如FileChannel 18 | 19 | - 非阻塞的NIO是继承于SelectableChannel,包括ServerSocketChannel, SocketChannel(TCP), DatagramChannel(UDP) 20 | 21 | ![SelectableChannel子类](http://7d9o4k.com1.z0.glb.clouddn.com/nio-2.png) 22 | 23 | ###SelectableChannel 24 | 25 | ![SelectableChannel](http://7d9o4k.com1.z0.glb.clouddn.com/3.png) 26 | 27 | SelectableChannel通过configureBlocking(boolean) 来设置阻塞/非阻塞 28 | 29 | ##非阻塞实现:SelectableChannel + Selector 30 | ###Selector 31 | 非阻塞的IO实现了不用阻塞,但仍需人工不断去询问,因此需要一个用来轮询的东西帮忙管理,这就是Selecter,类似于OS层面的select/epoll 32 | 33 | - 存放的是SelectionKey 34 | - 构造:Select.open() 35 | 36 | ![Selector](http://7d9o4k.com1.z0.glb.clouddn.com/4.png) 37 | 38 | - Selector类似一个观察者,只要我们把需要探知的套接字通道socketchannel注册到Selector,程序不用阻塞等待,可以并行做别的事情,当有事件发生时,Selector会通知程序,传回一组SelectionKey,程序读取这些Key,就会获得注册过的socketchannel,然后,从这个Channel中读取和处理数据 39 | 40 | - - Selector内部原理实际是在做一个对所注册的channel的**轮询访问**,不断的轮询(目前就这一个算法),一旦轮询到一个channel有所注册的事情发生,比如数据来了,他就会站起来报告,交出一把钥匙,让我们通过这把钥匙来读取这个channel的内容。 41 | 42 | ###SelectionKey 43 | 用于标注注册事件的信息,以Channel为单位,因为register()是Channel的方法 44 | 45 | ![SelectionKey](http://7d9o4k.com1.z0.glb.clouddn.com/5.png) 46 | 47 | ###Channel 48 | - Channel会发生事件:ACCEPT、READ、WRITE等 49 | - Channel通过register()方法在Selector上注册需要监控的事件 50 | -![register](http://7d9o4k.com1.z0.glb.clouddn.com/6.png) 51 | - 其中Object是Attach的一个对象,取出时用 Selectionkey.attachment(),并强制类型转换 52 | -------------------------------------------------------------------------------- /java/network-communication/tcp-udp/socket.md: -------------------------------------------------------------------------------- 1 | # SOCKET通信实现 2 | ##Socket 3 | Socket 是通信的句柄 4 | 5 | 一般而言,Socket编程包括: 6 | 1. 创建Socket 7 | 8 | 2. 打开连接到Socket的输入/输出流 9 | 10 | 3. 对Socket进行读/写 11 | 12 | 4. 关闭Socket 13 | 14 | ##server端的Socket编程实现 15 | **step1**: server端口建立一个ServerSocket,从其accept()方法中得到一个Socket。 16 | 17 | 其中,ServerSocket只负责在端口上**监听**,没有读写功能 18 | ```java 19 | ServerSocket serverSocket = new ServerSocket(6228); 20 | Socket socket = serverSocket.accept(); 21 | ``` 22 | 23 | **step2**:从Socket上得到InputStream/OutputStream(如果读写字符则用BufferedReader/PrintWriter) 24 | ```java 25 | BufferedReader reader = new BufferReader(new InputStreamReader(socket.getInputStream())); 26 | PrintWriter writer = new PrintWriter(socket.getOutputStream()); 27 | ``` 28 | 29 | **step3**:用step2得到的IO进行读写操作 30 | **step4**:关闭socket,顺序是:reader->socket->serverSocket 31 | 32 | ##client端的Socket编程实现 33 | **step1**: client建立一个Scoket来连接server端口与ip 34 | 35 | 36 | ```java 37 | Socket socket = new Socket("127.0.0.1", 6228); 38 | ``` 39 | 40 | 剩下步骤与server端口一致 -------------------------------------------------------------------------------- /java/network-communication/tcp-udp/tcp-handshake.md: -------------------------------------------------------------------------------- 1 | # TCP三次握手与四次挥手 2 | ##三次握手 3 | ![三次握手图片](http://7d9o4k.com1.z0.glb.clouddn.com/handshake.PNG) 4 | ##四次挥手 5 | ![四次挥手图片](http://7d9o4k.com1.z0.glb.clouddn.com/bye.PNG) 6 | ##一些问题 7 | ### 为什么采用3次握手而不是2次握手? 8 | 如果两次握手的话,客户端有可能因为网络阻塞等原因会发送多个请求报文,这时服务器就会建立连接,浪费掉许多服务器的资源 9 | 10 | ### 为什么连接的时候是三次握手,关闭的时候却是四次握手? 11 | TCP是全双工模式,关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送 12 | 13 | ### 为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态? 14 | 15 | 为了保证A发送的最后一个ACK报文能到达B。网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。 16 | -------------------------------------------------------------------------------- /java/network-communication/tcp-udp/tcp-transfer-status.md: -------------------------------------------------------------------------------- 1 | # TCP状态迁移图 2 | ![状态转移图](http://7d9o4k.com1.z0.glb.clouddn.com/tcp-status.JPG) 3 | 4 | 5 | |序号|状态|备注| 6 | |--|---|---| 7 | |1|CLOSED| 关闭状态| 8 | |2|LISTEN|server在等待连接的状态(server在调用socket、bind、listen进入此状态),等待客户端的连接| 9 | |3|SYN_SENT|client发起连接,发SYN给server(如果server不能连接,直接进入CLOSED)| 10 | |4|SYN_RCVD|与3相对,server收到SYN后,server进入此状态;同时server回应一个ACK+SYN给client;当client收到server回复的ACK+SYN后,也会进入此状态| 11 | |5|ESTABLISHED|完成3次握手,server与client进入此状态| 12 | |6|FIN_WAIT_1|主动关闭的一方,从状态5进入此状态,并发送FIN给对方| 13 | |7|FIN_WAIT_2|主动关闭的一方,收到对方的FIN ACK进入此状态;此后不能再接收对方数据,但是可以发送数据| 14 | |8|CLOSE_WAIT|接收到FIN后,被动关闭方进入此状态,同时回复ACK| 15 | |9|LAST_ACK|被动关闭方发送FIN,由状态8进入此状态| 16 | |10|CLOSING|两边同时发起关闭请求时,会由FIN_WAIT_1进入此状态。具体动作是,接收到FIN请求,同时响应一个ACK| 17 | |11|TIME_WAIT|3个状态可进入此状态,具体分析见下| 18 | 19 | 20 | ##TIME_WAIT 21 | 1. 由**FIN_WAIT_2**进入此状态:在双方不同时发起FIN的情况下,主动关闭的一方在完成自身发起的关闭请求后,接收到被动关闭一方的FIN后进入的状态。 22 | 23 | 2. 由**CLOSING**状态进入:双方同时发起关闭,都做了发起FIN的请求,同时接收到了FIN并做了ACK的情况下,由CLOSING状态进入。 24 | 25 | 3. 由**FIN_WAIT_1**状态进入:同时接受到FIN(对方发起),ACK(本身发起的FIN回应),与b的区别在于本身发起的FIN回应的ACK先于对方的FIN请求到达,而b是FIN先到达。这种情况概率最小。 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /java/spring.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/java/spring.md -------------------------------------------------------------------------------- /java/spring/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/java/spring/README.md -------------------------------------------------------------------------------- /java/thread/README.md: -------------------------------------------------------------------------------- 1 | # 线程 2 | 在这里首先明确一些关于线程的基本概念: 3 | 4 | 一个程序中可以有多条执行线索同时执行,一个线程就是程序中的一条执行线索,每个线程上都关联有要执行的代码,即可以有多段程序代码同时运行,每个程序至少都有一个线程,即main方法执行的那个线程 5 | 6 | ##线程优先级? 7 | 8 | * 一般来说,高优先级的线程在运行时会具有优先权,但这依赖于线程调度的实现 9 | 10 | * 可以定义线程的优先级,但是这并不能保证高优先级的线程会在低优先级的线程前执行 11 | 12 | * 线程优先级是一个int变量(从1-10),1代表最低优先级,10代表最高优先级 13 | 14 | 15 | ##线程调度器(Thread Scheduler)? 16 | 线程调度器是一个操作系统服务,它负责为Runnable状态的线程分配CPU时间。一旦我们创建一个线程并启动它,它的执行便依赖于线程调度器的实现。 17 | 18 | ##时间分片(Time Slicing)? 19 | 时间分片是指将可用的CPU时间分配给可用的Runnable线程的过程。分配CPU时间可以基于线程优先级或者线程等待的时间。 20 | - 线程调度并不受到Java虚拟机控制,所以由应用程序来控制它是更好的选择(也就是说不要让你的程序依赖于线程的优先级)。 21 | 22 | ##多线程的上下文切换(context-switching)? 23 | 上下文切换是存储和恢复CPU状态的过程,它使得线程执行能够从中断点恢复执行。上下文切换是多任务操作系统和多线程环境的基本特征。 24 | 25 | 26 | ## 线程的状态 27 | 28 | 线程的5种状态: 29 | 30 | 1. 新建 31 | 32 | 2. 运行 33 | 34 | 3. 等待(waiting/timed waiting) 35 | 36 | 4. 阻塞 37 | 38 | 5. 结束 39 | 40 | ##线程的5种状态之间变化 41 | 42 | - 调用线程的`start`方法后线程进入**就绪**状态,线程调度系统将就绪状态的线程转为**运行**状态,遇到`synchronized`语句时,由运行状态转为**阻塞**,当synchronized获得锁后,由阻塞转为运行,在这种情况可以调用`wait`方法转为**等待**状态,当线程关联的代码执行完后,线程变为**结束**状态。 43 | 44 | ![线程状态变化](http://7d9o4k.com1.z0.glb.clouddn.com/线程状态变化.png) 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /java/thread/interview/README.md: -------------------------------------------------------------------------------- 1 | # 面试题 2 | 3 | 网上关于线程的面试题 -------------------------------------------------------------------------------- /java/thread/interview/communication.md: -------------------------------------------------------------------------------- 1 | # 多线程通信面试题 2 | ##Java多线程中调用wait() 和 sleep()方法有什么不同? 3 | * wait()方法用于线程间通信,如果等待条件为真且其它线程被唤醒时它会释放锁 4 | 5 | * sleep()方法仅仅释放CPU资源或者让当前线程停止执行一段时间,但不会释放锁 6 | 7 | ##如何在两个线程间共享数据? 8 | 9 | * 通过共享对象(用wait和notify方法) 10 | 11 | * 使用像阻塞队列这样并发的数据结构 12 | 13 | 14 | ##Java中notify 和 notifyAll有什么区别? 15 | 16 | * notify()方法不能唤醒某个具体的线程,所以只有一个线程在等待的时候它才有用武之地 17 | 18 | * notifyAll()唤醒所有线程并允许他们争夺锁确保了至少有一个线程能继续运行 19 | 20 | ##为什么wait, notify 和 notifyAll这些方法不在thread类里面? 21 | 22 | * 由于wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中因为锁属于对象 23 | 24 | * JAVA提供的锁是对象级的而不是线程级的 25 | 26 | 27 | -------------------------------------------------------------------------------- /java/thread/interview/exception.md: -------------------------------------------------------------------------------- 1 | # 线程异常面试题 2 | ##Java中如何停止一个线程? 3 | 4 | * JDK 1.0本来有一些像stop(), suspend() 和 resume()的控制方法但是由于潜在的死锁威胁因此在后续的JDK版本中他们被弃用了 5 | 6 | * 当run() 或者 call() 方法执行完的时候线程会自动结束 7 | 8 | * 可以用volatile 布尔变量来退出run()方法的循环或者是取消任务来中断线程 9 | 10 | ##一个线程运行时发生异常会怎样? 11 | 12 | * 异常没有被捕获该线程将会停止执行 13 | 14 | * 当一个未捕获异常将造成线程中断的时候JVM会使用Thread.getUncaughtExceptionHandler()来查询线程的UncaughtExceptionHandler并将线程和异常作为参数传递给handler的uncaughtException()方法进行处理。 15 | 16 | ##如果同步块内的线程抛出异常会发生什么? 17 | * 无论你的同步块是正常还是异常退出的,里面的线程都会释放锁 18 | 19 | ##Java中活锁和死锁有什么区别? 20 | 21 | * 处于活锁的线程或进程的状态是不断改变的,活锁可以认为是一种特殊的饥饿 22 | 23 | * 进程的状态可以改变但是却不能继续执行 24 | 25 | 26 | 27 | ##怎么检测一个线程是否拥有锁? 28 | 29 | * 在java.lang.Thread中有一个方法叫holdsLock(),它返回true如果当且仅当当前线程拥有某个具体对象的锁 30 | 31 | -------------------------------------------------------------------------------- /java/thread/interview/others.md: -------------------------------------------------------------------------------- 1 | # 其他面试题 2 | 3 | ##你如何在Java中获取线程堆栈(Thread Dump)? 4 | 5 | * 当你获取线程堆栈时,JVM会把所有线程的状态存到日志文件或者输出到控制台 6 | 7 | * 在Windows你可以使用Ctrl + Break组合键 8 | 9 | * Linux下用kill -3命令 10 | 11 | * 可以用jstack这个工具来获取,它对线程id进行操作,你可以用jps这个工具找到id。 12 | 13 | ##有三个线程T1,T2,T3,怎么确保它们按顺序执行? 14 | 15 | * 用线程类的join()方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行 16 | 17 | * 为了确保三个线程的顺序你应该先启动最后一个(T3调用T2,T2调用T1),这样T1就会先完成而T3最后完成 18 | 19 | ##Thread类中的yield方法有什么作用? 20 | 21 | * Yield方法可以暂停当前正在执行的线程对象,让其它有相同优先级的线程执行 22 | 23 | ##Java中ConcurrentHashMap的并发度是什么? 24 | 25 | * ConcurrentHashMap把实际map划分成若干部分来实现它的可扩展性和线程安全。 26 | 27 | * 这种划分是使用并发度获得的,它是ConcurrentHashMap类构造函数的一个可选参数,默认值为16,这样在多线程情况下就能避免争用 28 | 29 | 30 | ##如何强制启动一个线程? 31 | 32 | * 这个问题就像是如何强制进行Java垃圾回收,目前还没有觉得方法,虽然你可以使用System.gc()来进行垃圾回收,但是不保证能成功。 33 | 34 | * 在Java里面没有办法强制启动一个线程,它是被线程调度器控制着且Java没有公布相关的API。 35 | 36 | 37 | ##在多线程中,什么是上下文切换(context-switching)? 38 | 39 | * 下文切换是存储和恢复CPU状态的过程,它使得线程执行能够从中断点恢复执行 40 | 41 | ##如何确保main()方法所在的线程是Java程序最后结束的线程? 42 | 43 | * 使用Thread类的join()方法来确保所有程序创建的线程在main()方法退出前结束 44 | 45 | ##为什么Thread类的sleep()和yield()方法是静态的? 46 | 47 | * Thread类的sleep()和yield()方法将在当前正在执行的线程上运行 48 | 49 | * 让sleep()成为实例方法, 当前线程可以直接sleep别的线程, 50 | 51 | * sleep()方法是当前线程转入被阻塞的状态,而yield()方法使当前线程转入可运行状态; 52 | 53 | ##何创建守护线程? 54 | 55 | * 使用Thread类的setDaemon(true)方法可以将线程设置为守护线程 56 | 57 | * 当我们在Java程序中创建一个线程,它就被称为用户线程。一个守护线程是在后台执行并且不会阻止JVM终止的线程。当没有用户线程在运行的时候,JVM关闭程序并且退出。一个守护线程创建的子线程依然是守护线程 58 | 59 | 60 | -------------------------------------------------------------------------------- /java/thread/interview/philosopher.md: -------------------------------------------------------------------------------- 1 | # 线程死锁:哲学家问题 2 | ##死锁 3 | 死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去 4 | ###死锁发生的条件 5 | 死锁发生**同时满足**的四个条件: 6 | 1. 互斥条件:一个资源每次只能被一个进程使用 7 | 8 | 2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放 9 | 10 | 3. 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺 11 | 12 | 4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系 13 | 14 | ###避免死锁发生 15 | 避免死锁最简单的方法就是阻止循环等待条件,将系统中所有的资源设置标志位、排序,规定所有的线程申请资源必须以**一定的顺序(升序或降序)**做操作来避免死锁 16 | 17 | ##哲学家问题 18 | ###题目简述 19 | 5个哲学家围城一圈就餐,因为哲学家都比较穷,每个人只能买得起一根筷子,所以5个哲学家就餐 20 | 。因为哲学家通常都是在思考,所以5个哲学家并不是同时进餐.但是当他们进餐时,必须获取放置在他们左边和右边的筷子,如果他们左边或者右边的筷子已经被别人拿去了,那他就有等待别人放下筷子时才能获取筷子 21 | ###实现 22 | ![哲学家问题源码](http://7d9o4k.com1.z0.glb.clouddn.com/实现哲学家.png) 23 | 24 | ###破坏死锁 25 | 只要破坏死锁的4个条件当中的一个即可解决死锁问题 26 | - 对于哲学家问题来说,条件4最容易破解,让最后一个哲学家先拿其左边的筷子,然后拿右边筷子 27 | 28 | ```java 29 | for(inti=0; i queue; 20 | 21 | public Producer(BlockingQueue q){ 22 | this.queue = q; 23 | } 24 | 25 | @Override 26 | public void run(){ 27 | try{ 28 | while(true){ 29 | this.queue.put(1);//放入1;1相当于bread 30 | } 31 | }catch(InterruptException e){ 32 | e.printStackTrace(); 33 | } 34 | } 35 | } 36 | //消费者 37 | class Consumer implements Runnable { 38 | private final BlockingQueue queue; 39 | 40 | public Consumer(BlockingQueue q){ 41 | this.queue = q; 42 | } 43 | 44 | @Override 45 | public void run(){ 46 | try{ 47 | while(true){ 48 | this.queue.take();//拿出一个bread 49 | } 50 | }catch(InterruptException e){ 51 | e.printStackTrace(); 52 | } 53 | } 54 | } 55 | //测试 56 | public class Main { 57 | public static void main(String[] args){ 58 | int backetSize = 10; 59 | BlockingQueue backet = new LinkedBlockingQueue<>(backetSize); 60 | 61 | Producer producer = new Producer(backet); 62 | Consumer consumer = new Consumer(backet); 63 | 64 | new Thread(producer).start(); 65 | new Thread(consumer).start(); 66 | } 67 | } 68 | ``` -------------------------------------------------------------------------------- /java/thread/interview/thread-pool.md: -------------------------------------------------------------------------------- 1 | # 关于多线程的面试题 2 | ##使用Runnalbe还是Thread? 3 | JAVA里面允许调用多个接口,但是不允许多继承,所以实现Runable接口更好 4 | 5 | ##Runnable和Callable有什么不同? 6 | - Runnable和Callable都代表那些要在不同的线程中执行的任务 7 | 8 | - Callable是在JDK1.5增加的。它们的主要区别是Callable的 call() 方法可以返回值和抛出异常(返回Future对象) 9 | 10 | ##Thread类中的start() 和 run() 方法有什么区别? 11 | - start()方法被用来启动新创建的线程,而且start()内部调用了run()方法 12 | 13 | - 当你调用run()方法的时候,只会是在**原来的线程**中调用,没有新的线程启动,start()方法才会启动新线程 14 | 15 | ##为什么要使用线程池 16 | 17 | * 创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数有限 18 | 19 | * 在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程(JDK1.5开始提供Excutor) 20 | 21 | 22 | 23 | ##提交任务时,线程池队列已满。会时发会生什么? 24 | * 一个任务不能被调度执行那么ThreadPoolExecutor’s submit()方法将会抛出一个RejectedExecutionException异常 25 | 26 | * 不是会阻塞直到线程池队列有空位 27 | 28 | ##线程池中submit()和 execute()方法有什么区别? 29 | - 两个方法都可以向线程池提交任务 30 | 31 | * execute()方法的返回类型是void,它定义在Executor接口中 32 | 33 | * submit()方法可以返回持有计算结果的Future对象,它定义在ExecutorService接口中,它扩展了Executor接口 34 | 35 | ##什么是FutureTask? 36 | * FutureTask表示一个可以取消的异步运算 37 | 38 | * 有启动和取消运算、查询运算是否完成和取回运算结果等方法。只有当运算完成的时候结果才能取回,如果运算尚未完成get方法将会阻塞。 39 | 40 | * 一个FutureTask对象可以对调用了Callable和Runnable的对象进行包装,由于FutureTask也是调用了Runnable接口所以它可以提交给Executor来执行 41 | 42 | ##如何创建一个线程池? 43 | ```java 44 | class Worker implements Runnable { 45 | int id; 46 | public Worker(int i){ 47 | this.id = i; 48 | } 49 | @Override 50 | public void run(){ 51 | //... 52 | } 53 | } 54 | public class Main(){ 55 | public void create(){ 56 | ExecutorService executor = Executors.newFixedThreadPool(2); 57 | 58 | for(int i=1; i<2; i++){ 59 | Runnable worker = new Worker(i); 60 | executor.execute(worker); 61 | } 62 | } 63 | } 64 | 65 | ``` 66 | 67 | -------------------------------------------------------------------------------- /java/thread/multi-thread/README.md: -------------------------------------------------------------------------------- 1 | # 多线程 2 | 3 | ## 多线程的实现 4 | 5 | 1. 继承Thread类 6 | 7 | 2. 实现Runnable接口 8 | 9 | 3. 采用线程池 10 | 11 | 12 | ## 多线程的优势 13 | 14 | 1. 多线程可以**减少响应时间**。单线程的话,如果有特别耗时的操作,后面的操作将都不能进行响应,必须排队等着 15 | 16 | 2. 线程的创建和切换开销小。因为他们共享进程的代码段、数据段,而创建进程将分配独立的地址空间和数据结构来维护 17 | 18 | 3. 多CPU和多核计算机本来就有执行多线程的能力,使用单线程就无法充分利用计算机资源 19 | 20 | 4. 使用多线程可以简化程序结构 21 | 22 | 23 | > java 高并发参考此文章 [深入浅出 Java Concurrency](http://www.blogjava.net/xylz/archive/2010/07/08/325587.html) -------------------------------------------------------------------------------- /java/thread/multi-thread/concurrent.md: -------------------------------------------------------------------------------- 1 | # concurrent包中的常用类 2 | ##1. concurrentHashMap 3 | ##2. Excutor 4 | - 一种工厂类,建立不同的线程 5 | 6 | ##3. ThreadPoolExcutor 7 | - 生成线程池 8 | 9 | ##4. FutureTask 10 | - 当有一个任务需要交给某个线程去处理时,可以用FutureTask 11 | 12 | - FutureTask实现了Runnable接口,因此可以通过Thread启动,或者交给ExcutorService处理 13 | 14 | - FutureTask提供了get()方法,可以返回执行结果。在任务执行结束之前该方法阻塞,知道任务执行完,并返回结果。 15 | 16 | ##5. LinkedBlockingQueue 17 | - 用于生产者、消费者模型,取时队列为空就一直wait,存时队列满就一直wait -------------------------------------------------------------------------------- /java/thread/multi-thread/pool.md: -------------------------------------------------------------------------------- 1 | # 线程池 2 | 3 | ##什么是线程池? 为什么要使用它? 4 | 5 | - 在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程(JDK1.5开始提供Excutor) 6 | 7 | - 创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数有限,所以可以创建线程池,然后往里面提交需要开启的线程即可 8 | 9 | 10 | 11 | ## 线程池中断策略 12 | 线程池中的线程容器已经放不下先的任务了,必须要有一个相应的策略来处理 13 | ##ThreadPoolExecutor内部的4个中断策略 14 | ###1. Abort 15 | 默认策略,中止新加入的任务 16 | ###2. CallerRuns 17 | 调用者运行,它既不会丢弃任务,也不会抛弃异常,它会把任务推回到调用者那里以缓解任务流 18 | ###3. Discard 19 | 默认放弃这个任务 20 | ###4. DiscardOldest 21 | 遗弃最老的的任务 22 | -------------------------------------------------------------------------------- /java/thread/multi-thread/syn-lock.md: -------------------------------------------------------------------------------- 1 | # Java多线程同步(锁)的实现方法(synchronised 与reentrantlock) 2 | 3 | ##1. synchronized关键字 4 | 5 | - synchronized可以在任意对象上加锁,而加锁的这段代码将成为互斥区或临界区 6 | 7 | - 每个对象都可以做为锁,但一个对象做为锁时,应该被多个线程共享,这样显得有意义 8 | 9 | - sleep()和CPU调度都不会放锁,而wait()会放锁 10 | 11 | - 在什么对象上加锁 ? 12 | 13 | - 代码块 : 指定的对象 14 | 15 | - 方法上 : this引用 16 | 17 | - 静态方法上 : class对象 18 | 19 | 20 | 21 | - 另外:Collections类里有一组synchronized方法可以将Collection封装成同步的 22 | 23 | ##2. java.util.concurrent.locks.lock类 24 | 25 | - 临界区边界灵活了 26 | 27 | - 锁释放的顺序由用户决定 28 | 29 | - 可以有多个Condition 30 | 31 | ###ReentrantLock 32 | 33 | - 重入锁指的是在某一个线程中可以多次获得同一把锁,在线程中多次操作有锁的方法 34 | 35 | - 需要使用除了内置锁以外的锁特性,比如可中断,可等待的锁,平等锁等 36 | 37 | - 可轮询,可中断,定时,非块,公平队列等高级特性时候使用可重入锁 38 | 39 | ##Synchronized与Lock之间的区别? 40 | 41 | | Synchronized | Lock | 42 | |--------|--------| 43 | | 使用Object本身的notify、wait、notify调度机制 | 可以使用condition进行线程间的调度 | 44 | |必须显示声明加载在方法上或者指定代码块中|需要显示声明指定的起始位置与终止位置| 45 | |托管给JVM执行,不会因为异常、没有释放而发生死锁|手动释放锁(最好再finally中释放)| 46 | 47 | ##synchronized与ReentrantLock之间区别 48 | 49 | - JDK5中增加了一个Lock接口的实现类ReentrantLock 50 | 51 | - 多了锁投票、定时锁、等候和中断锁。竞争不是很激烈时,Synchronized的性能优于ReentrantLock,在竞争特别激烈时,syn的性能会下降的非常快 52 | 53 | 54 | -------------------------------------------------------------------------------- /java/thread/security/README.md: -------------------------------------------------------------------------------- 1 | # 线程安全 2 | 3 | ##线程安全强度 4 | 5 | ###1. 不可变 6 | 7 | - final修饰、String、枚举类、java.lang.Number的部分子类(Long、Double、BigInter)是不可变的 8 | 9 | - AtomicInteger等是不可变的 10 | 11 | ###2. 绝对线程安全 12 | 13 | ###3. 相对线程安全 14 | 15 | Vector、HashTable、Collections等 16 | 17 | ###4. 线程兼容 18 | 19 | HashMap等,它们自身不是线程安全的 20 | 21 | ###5. 线程对立 22 | 23 | Thread里面的suspend()与resume(),如果同时发生会造成死锁 24 | 25 | ##线程安全实现方法 26 | 27 | ###非阻塞同步 28 | 29 | - 先行操作,如果没有竞争则成功 30 | 31 | -如果产生冲突,进行补偿(不断尝试直到成功) 32 | 33 | ###互斥同步 34 | 35 | 1. synchronized关键字 36 | 37 | - 锁实例方法:对象的实例加锁 38 | 39 | - 锁类方法(static等属于类的方法):class加锁 40 | 41 | 2. ReenTrantLock 42 | 43 | 3. Lock 44 | 45 | ####锁优化 46 | 47 | 1. 适应性自旋 48 | 49 | 2. 锁消除 50 | 51 | 3. 锁粗化 52 | 53 | 4. 轻量级锁 54 | 55 | 5. 偏向锁 56 | 57 | ####synchronized与ReentrantLock之间区别 58 | 59 | ReentrantLock增加了:等待可中断、可实现公平锁、锁可以绑定 60 | 61 | 62 | ## 临界区 63 | 64 | 临界区是在程序中**可能**被多个线程同时访问的程序片段 65 | 66 | * 可以用**信号量**或**互斥量**来保护临界区。在Java中你可以用`synchronized`关键字或`ReentrantLock`来保护临界区 67 | -------------------------------------------------------------------------------- /java/thread/security/atomic-visible.md: -------------------------------------------------------------------------------- 1 | # 原子性、可见性、有序性 2 | ##原子性 3 | * load, read, assign, use,store, write是原子性的 4 | 5 | * 即:对基本数据的访问读写具有原子性 6 | 7 | * 原子操作就是指不可再分的操作,原子性就是指一段代码象原子一样不可再分,一次只能有一个线程执行这段代码,即代码的执行是互斥的 8 | 9 | ##可见性 10 | 11 | - 一个线程修改了共享变量的值,其他线程能够立即得到修改 12 | 13 | - 内存模型是通过变量修改后的新值同步回**主内存**(主内存概念参见下一章内存模型) 14 | 15 | 16 | ##有序性 17 | 18 | * 在本线程观察,所有操作都是有序的 19 | 20 | * 在其他线程观察这个线程,所有操作都是无序的 21 | 22 | * 多线程通过voatile和synchronized规则保存串行 23 | 24 | -------------------------------------------------------------------------------- /java/thread/security/happen-before.md: -------------------------------------------------------------------------------- 1 | # 先行先发送原则happen-before 2 | 先行发生原则(Happens-Before)是判断数据是否存在竞争、线程是否安全的主要依据。 3 | - 先行发生是Java内存,模型中定义的两项操作之间的**偏序关系**,如果操作A先行发生于操作B,那么操作A产生的影响能够被操作B观察到。 4 | 5 | ##一些结论 6 | 时间上的先后顺序对先行发生没有太大的关系,所以衡量并发安全问题的时候不要受到时间顺序的影响,一切以先行发生原则为准。 7 | - 一个操作“时间上的先发生”**不代表**这个操作先行发生 8 | 9 | - 一个操作先行发生也不代表这个操作在时间上是先发生的(虚拟机对代码的重排序的出现)。 -------------------------------------------------------------------------------- /java/thread/security/volatile.md: -------------------------------------------------------------------------------- 1 | # volatile关键字 2 | ##性质 3 | ###线程的可见性 4 | 用volatiole修饰的成员变量都强迫从主内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到主内存。 5 | - 这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。 6 | - 使用volatile修饰的修饰的变量会对多个线程可见,也就是说任何线程都可以看到被volatile修饰的变量的最终值。 7 | 8 | ###禁止重排序 9 | 1. 当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行 10 | 2. 在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行 11 | 12 | ###对于64位数据 13 | **不允许**读写操作分为两次32位数据进行 14 | ##指令重排序 15 | 一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。 16 | ###一个不会影响结果的重排序例子—单线程 17 | ```java 18 | int a = 10; //语句1 19 | int r = 2; //语句2 20 | a = a + 3; //语句3 21 | r = a*a; //语句4 22 | ``` 23 | 语句**不可能**的执行顺序:语句2==>语句1==>语句4==>语句3 24 | - 处理器在进行重排序时是会考虑指令之间的数据依赖性,如果一个指令Instruction 2必须用到Instruction 1的结果,那么处理器会保证Instruction 1会在Instruction 2之前执行 25 | 26 | ###影响结果的重排序例子—多线程 27 | ```java 28 | //线程1 29 | contex = loadContext(); //语句1 30 | inited = true; //语句2 31 | 32 | //线程2 33 | while(!inited){ 34 | sleep(1); 35 | } 36 | doSomethingWithConfig(contex); //语句3 37 | ``` 38 | 因为语句1与语句2**没有**数据依赖性,所以可能被重排序。 39 | - 如果线程1中语句2先发送,然后线程2会立即执行语句3,此时context没有被初始化,可能发生错误 40 | 41 | ##volatile不保证线程安全性 42 | 由于volatile只提供了可见性,并没有提供互斥性,volatile不能替代synchronized 43 | - 在多线程并发修改某个变量值时,依然会出现并发问题。 44 | 45 | ###volatile适用场景 46 | 一个线程修改被volatile修饰的变量,其他多个线程获取这个变量的值;当多个线程并发修改某个变量值时,必须使用synchronized来进行互斥同步。 47 | 48 | ##volatile变量与atomic变量有什么不同? 49 | 1. volatile变量可以确保**先行关系**,但是它不保证原子性 50 | - 例如:用volatile修饰count变量,那么count++操作不是原子性的 51 | 52 | 2. AtomicInteger类提供的atomic方法可以让这种操作具有原子性,例如:getAndIncrement()方法会原子性的进行增量操作,并把当前值加1 -------------------------------------------------------------------------------- /java/thread/thread-process.md: -------------------------------------------------------------------------------- 1 | # 线程与进程 2 | 3 | ## 关系 4 | 5 | * **进程**是资源分配的基本单位,**线程**是cpu调度,或者说是程序执行的最小单位 6 | 7 | * 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位 8 | 9 | * 线程自己基本上**不拥有系统资源**,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源 10 | 11 | * 一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行 12 | 13 | 14 | ## 进程的状态 15 | 16 | 基本状态有3种,即ready(就绪),running(运行),wait(等待) 17 | 18 | 19 | ## 多进程之间的通信 20 | 21 | - **管道( pipe )**:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系 22 | 23 | - **有名管道 (named pipe)** : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信 24 | 25 | - **信号量( semaphore )** : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段 26 | 27 | - **消息队列( message queue )** : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点 28 | 29 | - **信号 ( sinal )** : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生 30 | 31 | - **共享内存( shared memory )** :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信 32 | 33 | - **套接字( socket )** : 套接字也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信 34 | 35 | 36 | ## 多线程之间的通信 37 | 38 | - 方法1:通过访问共享变量的方式(注:需要处理同步问题) 39 | 40 | - 方法2:通过管道流 41 | 42 | ###两个线程间共享数据 43 | 44 | * 通过共享对象(用wait和notify方法) 45 | 46 | * 使用像阻塞队列这样并发的数据结构 47 | 48 | ###`notify` 和 `notifyAll`的区别 49 | 50 | * notify()方法不能唤醒某个具体的线程,所以只有一个线程在等待的时候它才有用武之地 51 | 52 | * notifyAll()唤醒所有线程并允许他们争夺锁确保了至少有一个线程能继续运行 53 | 54 | ####为什么wait, notify 和 notifyAll这些方法不在thread类里面? 55 | 56 | * 由于wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中因为锁属于对象 57 | 58 | * JAVA提供的锁是对象级的而不是线程级的 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /middleware/README.md: -------------------------------------------------------------------------------- 1 | # 中间件 相关知识体系 2 | 3 | 4 | 5 | 6 | 7 | * Redis 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /middleware/redis/README.md: -------------------------------------------------------------------------------- 1 | ## Redis面试刁难大全 {#activity-name} 2 | 3 | Redis有哪些数据结构? 4 | 5 | 字符串String、字典Hash、列表List、集合Set、有序集合SortedSet。 6 | 7 | 如果你是Redis中高级用户,还需要加上下面几种数据结构HyperLogLog、Geo、Pub/Sub。 8 | 9 | 如果你说还玩过Redis Module,像BloomFilter,RedisSearch,Redis-ML,面试官得眼睛就开始发亮了。 10 | 11 | 使用过Redis分布式锁么,它是什么回事? 12 | 13 | 先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。 14 | 15 | 这时候对方会告诉你说你回答得不错,然后接着问如果在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样? 16 | 17 | 这时候你要给予惊讶的反馈:唉,是喔,这个锁就永远得不到释放了。紧接着你需要抓一抓自己得脑袋,故作思考片刻,好像接下来的结果是你主动思考出来的,然后回答:我记得set指令有非常复杂的参数,这个应该是可以同时把setnx和expire合成一条指令来用的!对方这时会显露笑容,心里开始默念:摁,这小子还不错。 18 | 19 | 假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如果将它们全部找出来? 20 | 21 | 使用keys指令可以扫出指定模式的key列表。 22 | 23 | 对方接着追问:如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题? 24 | 25 | 这个时候你要回答redis关键的一个特性:redis的单线程的。keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。 26 | 27 | 使用过Redis做异步队列么,你是怎么用的? 28 | 29 | 一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。 30 | 31 | 如果对方追问可不可以不用sleep呢?list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。 32 | 33 | 如果对方追问能不能生产一次消费多次呢?使用pub/sub主题订阅者模式,可以实现1:N的消息队列。 34 | 35 | 如果对方追问pub/sub有什么缺点?在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。 36 | 37 | 如果对方追问redis如何实现延时队列?我估计现在你很想把面试官一棒打死如果你手上有一根棒球棍的话,怎么问的这么详细。但是你很克制,然后神态自若的回答道:使用sortedset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理。 38 | 39 | 到这里,面试官暗地里已经对你竖起了大拇指。但是他不知道的是此刻你却竖起了中指,在椅子背后。 40 | 41 | 如果有大量的key需要设置同一时间过期,一般需要注意什么? 42 | 43 | 如果大量的key过期时间设置的过于集中,到过期的那个时间点,redis可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一些。 44 | 45 | Redis如何做持久化的? 46 | 47 | bgsave做镜像全量持久化,aof做增量持久化。因为bgsave会耗费较长时间,不够实时,在停机的时候会导致大量丢失数据,所以需要aof来配合使用。在redis实例重启时,会使用bgsave持久化文件重新构建内存,再使用aof重放近期的操作指令来实现完整恢复重启之前的状态。 48 | 49 | 对方追问那如果突然机器掉电会怎样?取决于aof日志sync属性的配置,如果不要求性能,在每条写指令时都sync一下磁盘,就不会丢失数据。但是在高性能的要求下每次都sync是不现实的,一般都使用定时sync,比如1s1次,这个时候最多就会丢失1s的数据。 50 | 51 | 对方追问bgsave的原理是什么?你给出两个词汇就可以了,fork和cow。fork是指redis通过创建子进程来进行bgsave操作,cow指的是copy on write,子进程创建后,父子进程共享数据段,父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来。 52 | 53 | Pipeline有什么好处,为什么要用pipeline? 54 | 55 | 可以将多次IO往返的时间缩减为一次,前提是pipeline执行的指令之间没有因果相关性。使用redis-benchmark进行压测的时候可以发现影响redis的QPS峰值的一个重要因素是pipeline批次指令的数目。 56 | 57 | Redis的同步机制了解么? 58 | 59 | Redis可以使用主从同步,从从同步。第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer,待完成后将rdb文件全量同步到复制节点,复制节点接受完成后将rdb镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。 60 | 61 | 是否使用过Redis集群,集群的原理是什么? 62 | 63 | Redis Sentinal着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。 64 | 65 | Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。 66 | 67 | 参考:[https://mp.weixin.qq.com/s/507jyNbL4xCkxyW6Xk15Xg](https://mp.weixin.qq.com/s/507jyNbL4xCkxyW6Xk15Xg) 68 | 69 | -------------------------------------------------------------------------------- /pattern/README.md: -------------------------------------------------------------------------------- 1 | # 设计模式 2 | 3 | 4 | ## 创建型模式 5 | - Factory Method(工厂方法) 6 | - Abstract Factory(抽象工厂) 7 | - Builder(建造者) 8 | - Prototype(原型) 9 | - Singleton(单例) 10 | 11 | 12 | ## 结构型模式 13 | - Adapter Class/Object(适配器) 14 | - Bridge(桥接) 15 | - Composite(组合) 16 | - Decorator(装饰) 17 | - Facade(外观) 18 | - Flyweight(享元) 19 | - Proxy(代理) 20 | 21 | 22 | ## 行为型模式 23 | - Interpreter(解释器) 24 | - Template Method(模板方法) 25 | - Chain of Responsibility(责任链) 26 | - Command(命令) 27 | - Iterator(迭代器) 28 | - Mediator(中介者) 29 | - Memento(备忘录) 30 | - Observer(观察者) 31 | - State(状态) 32 | - Strategy(策略) 33 | - Visitor(访问者) 34 | 35 | 36 | > 设计模式全文参考 [Java之美[从菜鸟到高手演变]之设计模式](http://blog.csdn.net/zhangerqing/article/details/8194653) 37 | -------------------------------------------------------------------------------- /pattern/base/README.md: -------------------------------------------------------------------------------- 1 | ## 设计模式基础 2 | 3 | 4 | - 设计原则 5 | -------------------------------------------------------------------------------- /pattern/base/principles.md: -------------------------------------------------------------------------------- 1 | ## 设计模式的六大原则 2 | 3 | 4 | ### 开闭原则(Open Close Principle) 5 | 6 | 开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。 7 | 8 | ### 里氏代换原则(Liskov Substitution Principle) 9 | 10 | 里氏代换原则\(Liskov Substitution Principle LSP\)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科 11 | 12 | ### 依赖倒转原则(Dependence Inversion Principle) 13 | 14 | 这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。 15 | 16 | ### 接口隔离原则(Interface Segregation Principle) 17 | 18 | 这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。 19 | 20 | ### 迪米特法则(最少知道原则)(Demeter Principle) 21 | 22 | 为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。 23 | 24 | ### 合成复用原则(Composite Reuse Principle) 25 | 26 | 原则是尽量使用合成\/聚合的方式,而不是使用继承。 27 | 28 | -------------------------------------------------------------------------------- /pattern/behavioral/README.md: -------------------------------------------------------------------------------- 1 | ## 行为型模式 2 | 3 | 行为型模式(Behavioral Pattern)是对在不同的对象之间划分责任和算法的抽象化。 4 | 5 | 行为型模式不仅仅关注类和对象的结构,而且重点关注它们之间的相互作用。 6 | 7 | 通过行为型模式,可以更加清晰地划分类与对象的职责,并研究系统在运行时实例对象 之间的交互。在系统运行时,对象并不是孤立的,它们可以通过相互通信与协作完成某些复杂功能,一个对象在运行时也将影响到其他对象的运行。 8 | 9 | 行为型模式分为类行为型模式和对象行为型模式两种: 10 | 11 | - 类行为型模式:类的行为型模式使用继承关系在几个类之间分配行为,类行为型模式主要通过多态等方式来分配父类与子类的职责。 12 | - 对象行为型模式:对象的行为型模式则使用对象的聚合关联关系来分配行为,对象行为型模式主要是通过对象关联等方式来分配两个或多个类的职责。根据“合成复用原则”,系统中要尽量使用关联关系来取代继承关系,因此大部分行为型设计模式都属于对象行为型设计模式。 13 | 14 | -------------------------------------------------------------------------------- /pattern/behavioral/chain_of_responsibility.md: -------------------------------------------------------------------------------- 1 | # 责任链模式 2 | 3 | 接下来我们将要谈谈责任链模式,有多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链,请求在这条链上传递,直到某一对象决定处理该请求。但是发出者并不清楚到底最终那个对象会处理该请求,所以,责任链模式可以实现,在隐瞒客户端的情况下,对系统进行动态的调整。先看看关系图: 4 | 5 | ![](https://justdojava.gitbooks.io/it-interview/img/pattern/chain_of_responsibility.PNG) 6 | 7 | 8 | Abstracthandler类提供了get和set方法,方便MyHandle类设置和修改引用对象,MyHandle类是核心,实例化后生成一系列相互持有的对象,构成一条链。 9 | 10 | ```java 11 | public interface Handler { 12 | public void operator(); 13 | } 14 | ``` 15 | 16 | ```java 17 | public abstract class AbstractHandler { 18 | 19 | private Handler handler; 20 | 21 | public Handler getHandler() { 22 | return handler; 23 | } 24 | 25 | public void setHandler(Handler handler) { 26 | this.handler = handler; 27 | } 28 | 29 | } 30 | ``` 31 | 32 | ```java 33 | public class MyHandler extends AbstractHandler implements Handler { 34 | 35 | private String name; 36 | 37 | public MyHandler(String name) { 38 | this.name = name; 39 | } 40 | 41 | @Override 42 | public void operator() { 43 | System.out.println(name+"deal!"); 44 | if(getHandler()!=null){ 45 | getHandler().operator(); 46 | } 47 | } 48 | } 49 | ``` 50 | 51 | ```java 52 | public class Test { 53 | 54 | public static void main(String[] args) { 55 | MyHandler h1 = new MyHandler("h1"); 56 | MyHandler h2 = new MyHandler("h2"); 57 | MyHandler h3 = new MyHandler("h3"); 58 | 59 | h1.setHandler(h2); 60 | h2.setHandler(h3); 61 | 62 | h1.operator(); 63 | } 64 | } 65 | ``` 66 | 67 | 输出: 68 | h1deal! 69 | h2deal! 70 | h3deal! 71 | 此处强调一点就是,链接上的请求可以是一条链,可以是一个树,还可以是一个环,模式本身不约束这个,需要我们自己去实现,同时,在一个时刻,命令只允许由一个对象传给另一个对象,而不允许传给多个对象。 -------------------------------------------------------------------------------- /pattern/behavioral/command.md: -------------------------------------------------------------------------------- 1 | # 命令模式 2 | 3 | 命令模式很好理解,举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行。这个过程好在,三者相互解耦,任何一方都不用去依赖其他人,只需要做好自己的事儿就行,司令员要的是结果,不会去关注到底士兵是怎么实现的。我们看看关系图: 4 | 5 | ![](https://justdojava.gitbooks.io/it-interview/img/pattern/command.PNG) 6 | 7 | Invoker是调用者(司令员),Receiver是被调用者(士兵),MyCommand是命令,实现了Command接口,持有接收对象,看实现代码: 8 | 9 | ```java 10 | public interface Command { 11 | public void exe(); 12 | } 13 | ``` 14 | 15 | 16 | ```java 17 | public class MyCommand implements Command { 18 | 19 | private Receiver receiver; 20 | 21 | public MyCommand(Receiver receiver) { 22 | this.receiver = receiver; 23 | } 24 | 25 | @Override 26 | public void exe() { 27 | receiver.action(); 28 | } 29 | } 30 | ``` 31 | 32 | 33 | ```java 34 | public class Receiver { 35 | public void action(){ 36 | System.out.println("command received!"); 37 | } 38 | } 39 | ``` 40 | 41 | 42 | ```java 43 | public class Invoker { 44 | 45 | private Command command; 46 | 47 | public Invoker(Command command) { 48 | this.command = command; 49 | } 50 | 51 | public void action(){ 52 | command.exe(); 53 | } 54 | } 55 | ``` 56 | 57 | 58 | ```java 59 | public class Test { 60 | 61 | public static void main(String[] args) { 62 | Receiver receiver = new Receiver(); 63 | Command cmd = new MyCommand(receiver); 64 | Invoker invoker = new Invoker(cmd); 65 | invoker.action(); 66 | } 67 | } 68 | ``` 69 | 输出:command received! 70 | 这个很哈理解,命令模式的目的就是达到命令的发出者和执行者之间解耦,实现请求和执行分开,熟悉Struts的同学应该知道,Struts其实就是一种将请求和呈现分离的技术,其中必然涉及命令模式的思想! 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /pattern/behavioral/iterator.md: -------------------------------------------------------------------------------- 1 | # 迭代子模式 2 | 3 | 顾名思义,迭代器模式就是顺序访问聚集中的对象,一般来说,集合中非常常见,如果对集合类比较熟悉的话,理解本模式会十分轻松。这句话包含两层意思:一是需要遍历的对象,即聚集对象,二是迭代器对象,用于对聚集对象进行遍历访问。我们看下关系图: 4 | 5 | ![](https://justdojava.gitbooks.io/it-interview/img/pattern/iterator.PNG) 6 | 7 | 这个思路和我们常用的一模一样,MyCollection中定义了集合的一些操作,MyIterator中定义了一系列迭代操作,且持有Collection实例,我们来看看实现代码: 8 | 两个接口: 9 | 10 | ```java 11 | public interface Collection { 12 | 13 | public Iterator iterator(); 14 | 15 | /*取得集合元素*/ 16 | public Object get(int i); 17 | 18 | /*取得集合大小*/ 19 | public int size(); 20 | } 21 | ``` 22 | 23 | ```java 24 | public interface Iterator { 25 | //前移 26 | public Object previous(); 27 | 28 | //后移 29 | public Object next(); 30 | public boolean hasNext(); 31 | 32 | //取得第一个元素 33 | public Object first(); 34 | } 35 | ``` 36 | 37 | 两个实现: 38 | ```java 39 | public class MyCollection implements Collection { 40 | 41 | public String string[] = {"A","B","C","D","E"}; 42 | @Override 43 | public Iterator iterator() { 44 | return new MyIterator(this); 45 | } 46 | 47 | @Override 48 | public Object get(int i) { 49 | return string[i]; 50 | } 51 | 52 | @Override 53 | public int size() { 54 | return string.length; 55 | } 56 | } 57 | ``` 58 | 59 | ```java 60 | public class MyIterator implements Iterator { 61 | 62 | private Collection collection; 63 | private int pos = -1; 64 | 65 | public MyIterator(Collection collection){ 66 | this.collection = collection; 67 | } 68 | 69 | @Override 70 | public Object previous() { 71 | if(pos > 0){ 72 | pos--; 73 | } 74 | return collection.get(pos); 75 | } 76 | 77 | @Override 78 | public Object next() { 79 | if(pos vector = new Vector(); 61 | @Override 62 | public void add(Observer observer) { 63 | vector.add(observer); 64 | } 65 | 66 | @Override 67 | public void del(Observer observer) { 68 | vector.remove(observer); 69 | } 70 | 71 | @Override 72 | public void notifyObservers() { 73 | Enumeration enumo = vector.elements(); 74 | while(enumo.hasMoreElements()){ 75 | enumo.nextElement().update(); 76 | } 77 | } 78 | } 79 | ``` 80 | 81 | ```java 82 | public class MySubject extends AbstractSubject { 83 | 84 | @Override 85 | public void operation() { 86 | System.out.println("update self!"); 87 | notifyObservers(); 88 | } 89 | 90 | } 91 | ``` 92 | 测试类: 93 | 94 | ```java 95 | public class ObserverTest { 96 | 97 | public static void main(String[] args) { 98 | Subject sub = new MySubject(); 99 | sub.add(new Observer1()); 100 | sub.add(new Observer2()); 101 | 102 | sub.operation(); 103 | } 104 | 105 | } 106 | ``` 107 | 108 | 输出: 109 | ``` 110 | update self! 111 | observer1 has received! 112 | observer2 has received! 113 | ``` 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /pattern/behavioral/strategy.md: -------------------------------------------------------------------------------- 1 | # 策略模式 2 | 3 | 策略模式是对**算法的包装**,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理。 4 | 5 | 策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是:“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。 6 | 7 | 这个模式涉及到三个角色: 8 | 9 | - **环境(Context)**:持有一个Strategy的引用 10 | 11 | - **抽象策略(Strategy)**:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口 12 | 13 | - **具体策略(ConcreteStrategy)**:包装了相关的算法或行为 14 | 15 | ##源码 16 | ###Test 17 | ```java 18 | public class Test { 19 | public static void main(String[] args){ 20 | //创建策略对象 21 | Strategy strategy = new StraA(); 22 | 23 | //创建环境 24 | Context context = new Context(strategy); 25 | 26 | //调用方法 27 | context.useStrategy(); 28 | } 29 | } 30 | ``` 31 | ###Contex 32 | ```java 33 | public class Context { 34 | //持有一个具体的策略对象 35 | private Strategy strategy; 36 | 37 | //传入策略对象 38 | public Context(Strategy strategy){ 39 | this.strategy = strategy; 40 | } 41 | 42 | //策略方法 43 | public void useStrategy() { 44 | strategy.func(); 45 | } 46 | } 47 | ``` 48 | ###Strategy:接口 49 | ```java 50 | public interface Strategy { 51 | public void func(); 52 | } 53 | ``` 54 | ###具体策略:接口的实现 55 | ```java 56 | pulibc class StraA implements Strategy { 57 | @Override 58 | public void func(){ 59 | //...实现 60 | } 61 | } 62 | ``` 63 | 64 | ##与简单工厂模式区别 65 | 工厂模式实例化一个产品的操作是在服务端来做的,换句话说客户端传达给服务端的只是某种标识,服务端根据该标识实例化一个对象。 66 | 67 | 而策略模式的客户端传达给服务端的是一个实例,服务端只是将该实例拿过去在服务端的环境里执行该实例的方法 68 | 69 | 70 | 参考博文: 71 | 1. http://www.cnblogs.com/java-my-life/archive/2012/05/10/2491891.html 72 | 2. http://www.cnblogs.com/justinw/archive/2007/02/06/641414.html 73 | 3. http://xiewenbo.iteye.com/blog/1294207 74 | 4. http://lh-kevin.iteye.com/blog/1981574 -------------------------------------------------------------------------------- /pattern/behavioral/template_method.md: -------------------------------------------------------------------------------- 1 | # 模板方法模式 2 | 3 | 解释一下模板方法模式,就是指:一个抽象类中,有一个主方法,再定义1...n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用,先看个关系图: 4 | 5 | ![](https://justdojava.gitbooks.io/it-interview/img/pattern/template_method.PNG) 6 | 7 | 8 | 就是在AbstractCalculator类中定义一个主方法calculate,calculate()调用spilt()等,Plus和Minus分别继承AbstractCalculator类,通过对AbstractCalculator的调用实现对子类的调用,看下面的例子: 9 | 10 | ```java 11 | public abstract class AbstractCalculator { 12 | 13 | /*主方法,实现对本类其它方法的调用*/ 14 | public final int calculate(String exp,String opt){ 15 | int array[] = split(exp,opt); 16 | return calculate(array[0],array[1]); 17 | } 18 | 19 | /*被子类重写的方法*/ 20 | abstract public int calculate(int num1,int num2); 21 | 22 | public int[] split(String exp,String opt){ 23 | String array[] = exp.split(opt); 24 | int arrayInt[] = new int[2]; 25 | arrayInt[0] = Integer.parseInt(array[0]); 26 | arrayInt[1] = Integer.parseInt(array[1]); 27 | return arrayInt; 28 | } 29 | } 30 | ``` 31 | 32 | ```java 33 | public class Plus extends AbstractCalculator { 34 | 35 | @Override 36 | public int calculate(int num1,int num2) { 37 | return num1 + num2; 38 | } 39 | } 40 | ``` 41 | 42 | 测试类: 43 | 44 | ```java 45 | public class StrategyTest { 46 | 47 | public static void main(String[] args) { 48 | String exp = "8+8"; 49 | AbstractCalculator cal = new Plus(); 50 | int result = cal.calculate(exp, "\\+"); 51 | System.out.println(result); 52 | } 53 | } 54 | ``` 55 | 56 | 我跟踪下这个小程序的执行过程:首先将exp和"\\+"做参数,调用AbstractCalculator类里的calculate(String,String)方法,在calculate(String,String)里调用同类的split(),之后再调用calculate(int ,int)方法,从这个方法进入到子类中,执行完return num1 + num2后,将值返回到AbstractCalculator类,赋给result,打印出来。正好验证了我们开头的思路 57 | -------------------------------------------------------------------------------- /pattern/creational/README.md: -------------------------------------------------------------------------------- 1 | ## 创建型模式 2 | 3 | 创建型模式(Creational Pattern)对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离。为了使软件的结构更加清晰,外界对于这些对象只需要知道它们共同的接口,而不清楚其具体的实现细节,使整个系统的设计更加符合单一职责原则。 4 | 5 | 创建型模式在创建什么(What),由谁创建(Who),何时创建(When)等方面都为软件设计者提供了尽可能大的灵活性。创建型模式隐藏了类的实例的创建细节,通过隐藏对象如何被创建和组合在一起达到使整个系统独立的目的。 -------------------------------------------------------------------------------- /pattern/creational/builder.md: -------------------------------------------------------------------------------- 1 | ## 建造者模式 2 | 3 | 工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性,其实建造者模式就是前面抽象工厂模式和最后的Test结合起来得到的。我们看一下代码: 4 | 还和前面一样,一个Sender接口,两个实现类MailSender和SmsSender。最后,建造者类如下: 5 | 6 | 7 | ```java 8 | public class Builder { 9 | private List list = new ArrayList(); 10 | public void produceMailSender(int count){ 11 | for(int i=0; i 结构型模式可以分为类结构型模式和对象结构型模式: 7 | 8 | 类结构型模式关心类的组合,由多个类可以组合成一个更大的 9 | 系统,在类结构型模式中一般只存在继承关系和实现关系。 10 | - 对象结构型模式关心类与对象的组合,通过关联关系使得在一 个类中定义另一个类的实例对象,然后通过该对象调用其方法。 根据“合成复用原则”,在系统中尽量使用关联关系来替代继 承关系,因此大部分结构型模式都是对象结构型模式。 -------------------------------------------------------------------------------- /pattern/structural/adapter/README.md: -------------------------------------------------------------------------------- 1 | ## 适配器模式 2 | 3 | 适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。 4 | 5 | 6 | 讲了这么多,总结一下三种适配器模式的应用场景: 7 | 8 | - 类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。 9 | 10 | - 对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。 11 | 12 | - 接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。 -------------------------------------------------------------------------------- /pattern/structural/adapter/class.md: -------------------------------------------------------------------------------- 1 | ## 类的适配器模式 2 | 3 | 类图: 4 | ![](https://justdojava.gitbooks.io/it-interview/img/pattern/adapter_class.PNG) 5 | 6 | 核心思想就是:有一个Source类,拥有一个方法,待适配,目标接口时Targetable,通过Adapter类,将Source的功能扩展到Targetable里,看代码: 7 | 8 | ```java 9 | public class Source { 10 | public void method1() { 11 | System.out.println("this is original method!"); 12 | } 13 | } 14 | ``` 15 | 16 | ```java 17 | public interface Targetable { 18 | /* 与原类中的方法相同 */ 19 | public void method1(); 20 | /* 新类的方法 */ 21 | public void method2(); 22 | } 23 | ``` 24 | 25 | ```java 26 | public class Adapter extends Source implements Targetable { 27 | @Override 28 | public void method2() { 29 | System.out.println("this is the targetable method!"); 30 | } 31 | } 32 | ``` 33 | 34 | Adapter类继承Source类,实现Targetable接口,下面是测试类: 35 | 36 | ```java 37 | public class AdapterTest { 38 | 39 | public static void main(String[] args) { 40 | Targetable target = new Adapter(); 41 | target.method1(); 42 | target.method2(); 43 | } 44 | } 45 | ``` 46 | 47 | 输出: 48 | ``` 49 | this is original method! 50 | this is the targetable method! 51 | ``` 52 | 这样Targetable接口的实现类就具有了Source类的功能。 53 | 54 | -------------------------------------------------------------------------------- /pattern/structural/adapter/interface.md: -------------------------------------------------------------------------------- 1 | ## 接口的适配器模式 2 | 3 | 有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。看一下类图: 4 | 5 | ![](https://justdojava.gitbooks.io/it-interview/img/pattern/adapter_interface.PNG) 6 | 7 | 这个很好理解,在实际开发中,我们也常会遇到这种接口中定义了太多的方法,以致于有时我们在一些实现类中并不是都需要。看代码: 8 | 9 | ```java 10 | public interface Sourceable { 11 | 12 | public void method1(); 13 | public void method2(); 14 | } 15 | ``` 16 | 17 | 抽象类Wrapper2: 18 | 19 | ```java 20 | public abstract class Wrapper2 implements Sourceable{ 21 | 22 | public void method1(){} 23 | public void method2(){} 24 | } 25 | ``` 26 | 27 | ```java 28 | public class SourceSub1 extends Wrapper2 { 29 | public void method1(){ 30 | System.out.println("the sourceable interface's first Sub1!"); 31 | } 32 | } 33 | ``` 34 | 35 | ```java 36 | public class SourceSub2 extends Wrapper2 { 37 | public void method2(){ 38 | System.out.println("the sourceable interface's second Sub2!"); 39 | } 40 | } 41 | ``` 42 | 43 | ```java 44 | public class WrapperTest { 45 | 46 | public static void main(String[] args) { 47 | Sourceable source1 = new SourceSub1(); 48 | Sourceable source2 = new SourceSub2(); 49 | 50 | source1.method1(); 51 | source1.method2(); 52 | source2.method1(); 53 | source2.method2(); 54 | } 55 | } 56 | ``` 57 | 58 | 测试输出: 59 | 60 | ``` 61 | the sourceable interface's first Sub1! 62 | the sourceable interface's second Sub2! 63 | 64 | ``` 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /pattern/structural/adapter/object.md: -------------------------------------------------------------------------------- 1 | ## 对象的适配器模式 2 | 3 | 基本思路和类的适配器模式相同,只是将Adapter类作修改,这次不继承Source类,而是持有Source类的实例,以达到解决兼容性的问题。看图: 4 | 5 | 类图: 6 | ![](https://justdojava.gitbooks.io/it-interview/img/pattern/adapter_object.PNG) 7 | 8 | 只需要修改Adapter类的源码即可: 9 | 10 | ```java 11 | public class Wrapper implements Targetable { 12 | 13 | private Source source; 14 | 15 | public Wrapper(Source source){ 16 | super(); 17 | this.source = source; 18 | } 19 | @Override 20 | public void method2() { 21 | System.out.println("this is the targetable method!"); 22 | } 23 | 24 | @Override 25 | public void method1() { 26 | source.method1(); 27 | } 28 | } 29 | ``` 30 | 31 | 测试类: 32 | 33 | ```java 34 | public class AdapterTest { 35 | 36 | public static void main(String[] args) { 37 | Source source = new Source(); 38 | Targetable target = new Wrapper(source); 39 | target.method1(); 40 | target.method2(); 41 | } 42 | } 43 | ``` 44 | 45 | 输出与第一种一样,只是适配的方法不同而已。 46 | -------------------------------------------------------------------------------- /pattern/structural/bridge.md: -------------------------------------------------------------------------------- 1 | ## 桥接模式 2 | 3 | 桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化。桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化,像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。我们来看看关系图: 4 | 5 | ![](https://justdojava.gitbooks.io/it-interview/img/pattern/bridge1.PNG) 6 | 7 | 实现代码: 8 | 先定义接口: 9 | ```java 10 | public class Source { 11 | public void method1() { 12 | System.out.println("this is original method!"); 13 | } 14 | } 15 | ``` 16 | 17 | 分别定义两个实现类: 18 | ```java 19 | public class SourceSub1 implements Sourceable { 20 | 21 | @Override 22 | public void method() { 23 | System.out.println("this is the first sub!"); 24 | } 25 | } 26 | ``` 27 | 28 | ```java 29 | public class SourceSub2 implements Sourceable { 30 | 31 | @Override 32 | public void method() { 33 | System.out.println("this is the second sub!"); 34 | } 35 | } 36 | ``` 37 | 38 | 定义一个桥,持有Sourceable的一个实例: 39 | ```java 40 | public abstract class Bridge { 41 | private Sourceable source; 42 | 43 | public void method(){ 44 | source.method(); 45 | } 46 | 47 | public Sourceable getSource() { 48 | return source; 49 | } 50 | 51 | public void setSource(Sourceable source) { 52 | this.source = source; 53 | } 54 | } 55 | ``` 56 | ```java 57 | public class MyBridge extends Bridge { 58 | public void method(){ 59 | getSource().method(); 60 | } 61 | } 62 | ``` 63 | 64 | 测试类: 65 | 66 | ```java 67 | public class BridgeTest { 68 | 69 | public static void main(String[] args) { 70 | 71 | Bridge bridge = new MyBridge(); 72 | 73 | /*调用第一个对象*/ 74 | Sourceable source1 = new SourceSub1(); 75 | bridge.setSource(source1); 76 | bridge.method(); 77 | 78 | /*调用第二个对象*/ 79 | Sourceable source2 = new SourceSub2(); 80 | bridge.setSource(source2); 81 | bridge.method(); 82 | } 83 | } 84 | ``` 85 | 86 | output: 87 | 88 | ``` 89 | this is the first sub! 90 | this is the second sub! 91 | ``` 92 | 93 | 这样,就通过对Bridge类的调用,实现了对接口Sourceable的实现类SourceSub1和SourceSub2的调用。接下来我再画个图,大家就应该明白了,因为这个图是我们JDBC连接的原理,有数据库学习基础的,一结合就都懂了。 94 | 95 | 96 | ![](https://justdojava.gitbooks.io/it-interview/img/pattern/bridge2.PNG) -------------------------------------------------------------------------------- /pattern/structural/composite.md: -------------------------------------------------------------------------------- 1 | ## 组合模式(Composite) 2 | 3 | 组合模式有时又叫部分-整体模式在处理类似树形结构的问题时比较方便,看看关系图: 4 | 5 | ![](https://justdojava.gitbooks.io/it-interview/img/pattern/composite.PNG) 6 | 7 | 直接来看代码: 8 | 9 | ```java 10 | public class TreeNode { 11 | 12 | private String name; 13 | private TreeNode parent; 14 | private Vector children = new Vector(); 15 | 16 | public TreeNode(String name){ 17 | this.name = name; 18 | } 19 | 20 | public String getName() { 21 | return name; 22 | } 23 | 24 | public void setName(String name) { 25 | this.name = name; 26 | } 27 | 28 | public TreeNode getParent() { 29 | return parent; 30 | } 31 | 32 | public void setParent(TreeNode parent) { 33 | this.parent = parent; 34 | } 35 | 36 | //添加孩子节点 37 | public void add(TreeNode node){ 38 | children.add(node); 39 | } 40 | 41 | //删除孩子节点 42 | public void remove(TreeNode node){ 43 | children.remove(node); 44 | } 45 | 46 | //取得孩子节点 47 | public Enumeration getChildren(){ 48 | return children.elements(); 49 | } 50 | } 51 | ``` 52 | 53 | ```java 54 | public class Tree { 55 | 56 | TreeNode root = null; 57 | 58 | public Tree(String name) { 59 | root = new TreeNode(name); 60 | } 61 | 62 | public static void main(String[] args) { 63 | Tree tree = new Tree("A"); 64 | TreeNode nodeB = new TreeNode("B"); 65 | TreeNode nodeC = new TreeNode("C"); 66 | 67 | nodeB.add(nodeC); 68 | tree.root.add(nodeB); 69 | System.out.println("build the tree finished!"); 70 | } 71 | } 72 | ``` 73 | 74 | 使用场景:将多个对象组合在一起进行操作,常用于表示树形结构中,例如二叉树,数等。 75 | -------------------------------------------------------------------------------- /pattern/structural/decorator.md: -------------------------------------------------------------------------------- 1 | ## 装饰模式(Decorator) 2 | 3 | 顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例,关系图如下: 4 | 5 | ![](https://justdojava.gitbooks.io/it-interview/img/pattern/decorator.PNG) 6 | 7 | Source类是被装饰类,Decorator类是一个装饰类,可以为Source类动态的添加一些功能,代码如下: 8 | 9 | ```java 10 | public interface Sourceable { 11 | public void method(); 12 | } 13 | ``` 14 | 15 | ```java 16 | public class Source implements Sourceable { 17 | 18 | @Override 19 | public void method() { 20 | System.out.println("the original method!"); 21 | } 22 | } 23 | ``` 24 | 25 | ```java 26 | public class Decorator implements Sourceable { 27 | 28 | private Sourceable source; 29 | 30 | public Decorator(Sourceable source){ 31 | super(); 32 | this.source = source; 33 | } 34 | @Override 35 | public void method() { 36 | System.out.println("before decorator!"); 37 | source.method(); 38 | System.out.println("after decorator!"); 39 | } 40 | } 41 | ``` 42 | 43 | 测试类: 44 | 45 | ```java 46 | public class DecoratorTest { 47 | 48 | public static void main(String[] args) { 49 | Sourceable source = new Source(); 50 | Sourceable obj = new Decorator(source); 51 | obj.method(); 52 | } 53 | } 54 | ``` 55 | 56 | 输出: 57 | 58 | ``` 59 | before decorator! 60 | the original method! 61 | after decorator! 62 | ``` 63 | 64 | 装饰器模式的应用场景: 65 | - 1、需要扩展一个类的功能。 66 | - 2、动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。) 67 | 68 | **缺点:产生过多相似的对象,不易排错!** 69 | 70 | -------------------------------------------------------------------------------- /pattern/structural/facade.md: -------------------------------------------------------------------------------- 1 | ## 外观模式(Facade) 2 | 3 | 外观模式是为了解决类与类之家的依赖关系的,像spring一样,可以将类和类之间的关系配置到配置文件中,而外观模式就是将他们的关系放在一个Facade类中,降低了类类之间的耦合度,该模式中没有涉及到接口,看下类图:(我们以一个计算机的启动过程为例) 4 | 5 | ![](https://justdojava.gitbooks.io/it-interview/img/pattern/facade.PNG) 6 | 7 | 我们先看下实现类: 8 | 9 | ```java 10 | public class CPU { 11 | 12 | public void startup(){ 13 | System.out.println("cpu startup!"); 14 | } 15 | 16 | public void shutdown(){ 17 | System.out.println("cpu shutdown!"); 18 | } 19 | } 20 | ``` 21 | 22 | ```java 23 | public class Memory { 24 | 25 | public void startup(){ 26 | System.out.println("memory startup!"); 27 | } 28 | 29 | public void shutdown(){ 30 | System.out.println("memory shutdown!"); 31 | } 32 | } 33 | ``` 34 | 35 | ```java 36 | public class Disk { 37 | 38 | public void startup(){ 39 | System.out.println("disk startup!"); 40 | } 41 | 42 | public void shutdown(){ 43 | System.out.println("disk shutdown!"); 44 | } 45 | } 46 | ``` 47 | 48 | ```java 49 | public class Computer { 50 | private CPU cpu; 51 | private Memory memory; 52 | private Disk disk; 53 | 54 | public Computer(){ 55 | cpu = new CPU(); 56 | memory = new Memory(); 57 | disk = new Disk(); 58 | } 59 | 60 | public void startup(){ 61 | System.out.println("start the computer!"); 62 | cpu.startup(); 63 | memory.startup(); 64 | disk.startup(); 65 | System.out.println("start computer finished!"); 66 | } 67 | 68 | public void shutdown(){ 69 | System.out.println("begin to close the computer!"); 70 | cpu.shutdown(); 71 | memory.shutdown(); 72 | disk.shutdown(); 73 | System.out.println("computer closed!"); 74 | } 75 | } 76 | ``` 77 | 78 | User类如下: 79 | 80 | ```java 81 | public class User { 82 | 83 | public static void main(String[] args) { 84 | Computer computer = new Computer(); 85 | computer.startup(); 86 | computer.shutdown(); 87 | } 88 | } 89 | ``` 90 | 91 | 输出: 92 | 93 | ```java 94 | start the computer! 95 | cpu startup! 96 | memory startup! 97 | disk startup! 98 | start computer finished! 99 | begin to close the computer! 100 | cpu shutdown! 101 | memory shutdown! 102 | disk shutdown! 103 | computer closed! 104 | ``` 105 | 106 | 如果我们没有Computer类,那么,CPU、Memory、Disk他们之间将会相互持有实例,产生关系,这样会造成严重的依赖,修改一个类,可能会带来其他类的修改,这不是我们想要看到的,有了Computer类,他们之间的关系被放在了Computer类里,这样就起到了解耦的作用,这,就是外观模式! 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /pattern/structural/flyweight.md: -------------------------------------------------------------------------------- 1 | ## 享元模式(Flyweight) 2 | 3 | 享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。 4 | 5 | 6 | ![](https://justdojava.gitbooks.io/it-interview/img/pattern/flyweight.PNG) 7 | 8 | FlyWeightFactory负责创建和管理享元单元,当一个客户端请求时,工厂需要检查当前对象池中是否有符合条件的对象,如果有,就返回已经存在的对象,如果没有,则创建一个新对象,FlyWeight是超类。一提到共享池,我们很容易联想到Java里面的JDBC连接池,想想每个连接的特点,我们不难总结出:适用于作共享的一些个对象,他们有一些共有的属性,就拿数据库连接池来说,url、driverClassName、username、password及dbname,这些属性对于每个连接来说都是一样的,所以就适合用享元模式来处理,建一个工厂类,将上述类似属性作为内部数据,其它的作为外部数据,在方法调用时,当做参数传进来,这样就节省了空间,减少了实例的数量。 9 | 看个例子: 10 | 11 | ![](https://justdojava.gitbooks.io/it-interview/img/pattern/flyweight2.PNG) 12 | 13 | 看下数据库连接池的代码: 14 | 15 | ```java 16 | public class ConnectionPool { 17 | 18 | private Vector pool; 19 | 20 | /*公有属性*/ 21 | private String url = "jdbc:mysql://localhost:3306/test"; 22 | private String username = "root"; 23 | private String password = "root"; 24 | private String driverClassName = "com.mysql.jdbc.Driver"; 25 | 26 | private int poolSize = 100; 27 | private static ConnectionPool instance = null; 28 | Connection conn = null; 29 | 30 | /*构造方法,做一些初始化工作*/ 31 | private ConnectionPool() { 32 | pool = new Vector(poolSize); 33 | 34 | for (int i = 0; i < poolSize; i++) { 35 | try { 36 | Class.forName(driverClassName); 37 | conn = DriverManager.getConnection(url, username, password); 38 | pool.add(conn); 39 | } catch (ClassNotFoundException e) { 40 | e.printStackTrace(); 41 | } catch (SQLException e) { 42 | e.printStackTrace(); 43 | } 44 | } 45 | } 46 | 47 | /* 返回连接到连接池 */ 48 | public synchronized void release() { 49 | pool.add(conn); 50 | } 51 | 52 | /* 返回连接池中的一个数据库连接 */ 53 | public synchronized Connection getConnection() { 54 | if (pool.size() > 0) { 55 | Connection conn = pool.get(0); 56 | pool.remove(conn); 57 | return conn; 58 | } else { 59 | return null; 60 | } 61 | } 62 | } 63 | ``` 64 | 65 | 通过连接池的管理,实现了数据库连接的共享,不需要每一次都重新创建连接,节省了数据库重新创建的开销,提升了系统的性能!本章讲解了7种结构型模式,因为篇幅的问题,剩下的11种行为型模式,我们将另起篇章,敬请读者朋友们持续关注! 66 | -------------------------------------------------------------------------------- /pattern/structural/proxy.md: -------------------------------------------------------------------------------- 1 | ## 代理模式(Proxy) 2 | 3 | 其实每个模式名称就表明了该模式的作用,代理模式就是多一个代理类出来,替原对象进行一些操作,比如我们在租房子的时候回去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思。再如我们有的时候打官司,我们需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法。先来看看关系图: 4 | 5 | ![](https://justdojava.gitbooks.io/it-interview/img/pattern/proxy.PNG) 6 | 7 | 根据上文的阐述,代理模式就比较容易的理解了,我们看下代码: 8 | 9 | ```java 10 | public interface Sourceable { 11 | public void method(); 12 | } 13 | ``` 14 | 15 | ```java 16 | public class Source implements Sourceable { 17 | 18 | @Override 19 | public void method() { 20 | System.out.println("the original method!"); 21 | } 22 | } 23 | ``` 24 | 25 | ```java 26 | public class Proxy implements Sourceable { 27 | 28 | private Source source; 29 | public Proxy(){ 30 | super(); 31 | this.source = new Source(); 32 | } 33 | @Override 34 | public void method() { 35 | before(); 36 | source.method(); 37 | atfer(); 38 | } 39 | private void atfer() { 40 | System.out.println("after proxy!"); 41 | } 42 | private void before() { 43 | System.out.println("before proxy!"); 44 | } 45 | } 46 | ``` 47 | 48 | 测试类: 49 | 50 | ```java 51 | public class ProxyTest { 52 | 53 | public static void main(String[] args) { 54 | Sourceable source = new Proxy(); 55 | source.method(); 56 | } 57 | 58 | } 59 | ``` 60 | 61 | 输出: 62 | 63 | ``` 64 | before proxy! 65 | the original method! 66 | after proxy! 67 | ``` 68 | 69 | 70 | 代理模式的应用场景: 71 | 如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法: 72 | 1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。 73 | 2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。 74 | 使用代理模式,可以将功能划分的更加清晰,有助于后期维护! 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /pattern/structural/proxy/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justdojava/it-interviews/05ec6f382fb2eeb293f831e942ec02f7577b1ce3/pattern/structural/proxy/README.md --------------------------------------------------------------------------------