├── .gitignore ├── DesignPattern ├── explain.md ├── 从零开始单排学设计模式「UML类图」定级赛.md ├── 从零开始单排学设计模式「单一职责原则」黑铁 - 青铜 晋级赛.md ├── 从零开始单排学设计模式「策略模式」黑铁 II.md ├── 从零开始单排学设计模式「简单工厂设计模式」黑铁 III.md └── 从零开始单排学设计模式「装饰模式」黑铁 I.md ├── Java ├── HTTP API 认证授有哪些方法?.md ├── Java 技术栈中的技术点缩写.md ├── SQL编写规范.md ├── 工具 │ ├── 提取Excel文档中的中文 │ │ ├── GetChineseByExcel.java │ │ ├── README.md │ │ └── jxl-2.6.12.jar │ └── 提取文件中的中文 │ │ ├── GetChinese.java │ │ └── README.md └── 阅读JDK源码.md ├── README.md ├── images ├── Java开发者职业生涯要看的200+本书籍.jpg ├── README.md └── java_阅读JDK源码 │ ├── 640 (2).png │ ├── 640 (3).png │ ├── 640 (4).png │ ├── 640 (5).png │ ├── 640 (6).png │ ├── 640 (7).png │ ├── 640 (8).png │ ├── 640 (9).png │ ├── 640.png │ └── README.md ├── 学习书籍 └── Java 技术书籍大全.md ├── 手写实现 ├── JDK 动态代理 │ ├── Customer.java │ ├── GPClassLoader.java │ ├── GPInvocationHandler.java │ ├── GPMeipo.java │ ├── GPProxy.java │ ├── Person.java │ ├── README.md │ └── Test.java ├── Spring MVC │ └── README.md └── 简易版 tomcat │ ├── FindGirlServlet.java │ ├── HelloWorldServlet.java │ ├── MyRequest.java │ ├── MyResponse.java │ ├── MyServlet.java │ ├── MyTomcat.java │ ├── README.md │ ├── ServletMapping.java │ └── ServletMappingConfig.java ├── 文章推荐 └── Java后端优质文章推荐.md └── 面试题 ├── 100 期 Java 面试题汇总.md ├── 【美团】Java 岗 154 道面试题.md ├── 【美团】Java 岗 154 道面试题(2.0版).md └── 面试经历汇总.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | -------------------------------------------------------------------------------- /DesignPattern/explain.md: -------------------------------------------------------------------------------- 1 | ### 设计模式 2 | 3 | -------------------------------------------------------------------------------- /DesignPattern/从零开始单排学设计模式「UML类图」定级赛.md: -------------------------------------------------------------------------------- 1 | 阅读本文大概需要 3.5 分钟。 2 | 3 | 4 | 5 | 本篇是设计模式系列的开篇,虽然之前也写过相应的文章,但是因为种种原因后来断掉了,而且发现之前写的内容也很渣,不够系统。 6 | 7 | 8 | 9 | 所以现在打算重写,加上距离现在也有一段时间了,也算是自己的一个回顾吧! 10 | 11 | 12 | 13 | 学而时习之,不亦说乎。 14 | 15 | 16 | 17 | 从零开始单排学设计模式的国服排位之旅,今天正式开启! 18 | 19 | 20 | 21 | 目前段位:**定级赛** 22 | 23 | 24 | 25 | ![](https://mmbiz.qpic.cn/mmbiz_jpg/TeYk478W36Ba9cUE7U4f1yJOrEDGA3ItUR9ZGm37PpxiaqyjR2EKGpZ4ibga9z1Cich4cricianNG8ibBK7PSzWWBCkQ/640?wx_fmt=jpeg) 26 | 27 | 28 | 29 | 这篇文章来总结下UML类图,本来不打算讲UML类图的,因为我在学习设计模式的时候,一遇到有关UML的就会自动忽略,一看感觉就很复杂。 30 | 31 | 32 | 33 | 但是随着学习的深入,发现不掌握UML类图,对设计模式或者某一个框架没有整体的把控。所以与其逃避,不如敢于面对,今天就让我们一起来了解下什么是UML类图。 34 | 35 | 36 | 37 | Let's Go! 38 | 39 | 40 | 41 | **前言** 42 | 43 | 设计模式不是语法,是一种巧妙的写法,能把程序变的更加灵活。架构模式比设计模式大,架构模式是战略,而设计模式是战术。 44 | 45 | 46 | 47 | 设计模式分为3大类型:创建型,行为型,结构型,总共有23种。 48 | 49 | 50 | 51 | **UML类图** 52 | 53 | 类图描述系统中类的静态结构,它不仅定义系统中的类,描述类之间的联系,如关联、依赖、聚合等,还包括类的内部结构(类的属性和操作)。 54 | 55 | 56 | 57 | 类图描述的是静态关系,在系统的整个生命周期中都是有效的。 58 | 59 | 60 | 61 | 对象图是类图的实例,它们的不同之处在于对象图显示类图的多个对象实例,而不是实际的类。由于对象存在生命周期,所以对象图只能在系统某一时间存在。 62 | 63 | 64 | 65 | **UML基本图示法** 66 | 67 | 虚线箭头指向依赖; 68 | 69 | 实线箭头指向关联; 70 | 71 | 虚线三角指向接口; 72 | 73 | 实线三角指向父类; 74 | 75 | 空心菱形能分离而独立存在,是聚合; 76 | 77 | 实心菱形精密关联不可分,是组合; 78 | 79 | 80 | 81 | **![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3It9PwDf4wb2fcGNKbttYzYJFgsibJPmpGXk0KDnbqibZiaJlnCRIREkwYWw/640?wx_fmt=png)** 82 | 83 | 上面是UML的语法,在画类图的时候,清理类和类之间的关系是重点。 84 | 85 | 86 | 87 | 类的关系有泛化(Generalization)、实现(Realization)、依赖(Dependency)和关联(Association)。 88 | 89 | 90 | 91 | 其中关联又分为一般关联关系和聚合关系(Aggregation),合成关系(Composition)。 92 | 93 | 94 | 95 | **基本概念** 96 | 97 | 98 | 99 | 类图(Class Diagram):类图是面向对象系统建模中最常见和最重要的图,是定义其他图的基础。 100 | 101 | 102 | 103 | 类图的主要是用来显示系统中的类、接口以及它们之间的静态结构和关系的一种静态模型。 104 | 105 | 106 | 107 | 类图的3个基本组件:类名、属性、方法。 108 | 109 | 110 | 111 | **详细解析** 112 | 113 | 114 | 115 | 注:下面图片实例中的代码为C#代码,非Java代码! 116 | 117 | 118 | 119 | 继承关系 120 | 121 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3It1YeJxnJcgoUcY7Oia3H5icJQ6mo9kOxUesZaT5yk5DtOf3hBAP7cE22A/640?wx_fmt=png) 122 | 123 | 124 | 125 | 首先看到上图这个“动物”矩形框,它就代表一个类(Class)。 126 | 127 | 128 | 129 | 类图分三层 130 | 131 | 第一层显示类的名称,如果是抽象类,则就用斜体显示。 132 | 133 | 第二层是类的特性,通常就是字段和属性。 134 | 135 | 第三层是类的操作,通常是方法或行为。 136 | 137 | 138 | 139 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3It4icat5o3AH9HAEia8V4FWY2L7JEzpfhGs5ichuBHeI7JQmBAAPibGul7rA/640?wx_fmt=png) 140 | 141 | 142 | 143 | 在看到上图中的“飞翔”,它表示一个接口图,与类图的区别主要是顶端有<>显示。 144 | 145 | 146 | 147 | 第一行是接口名称,第二行是接口方法。 148 | 149 | 150 | 151 | 接口还有另一种表示方法,俗称棒棒糖表示法,就是唐老鸭类实现了“讲人话”的接口。 152 | 153 | 154 | 155 | 鸭子本来也有语言,只不过只有唐老鸭是能讲人话的鸭子。 156 | 157 | 158 | 159 | 注意动物、鸟、鸭、唐老鸭之间的关系符号,你就会发现它们都是继承的关系。 160 | 161 | 162 | 163 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3Itx6ibRonU1J8ricRzlqbic2KfpJOkeqmUMCRG8WQVziaWFhOAKWIY1KkoeQ/640?wx_fmt=png) 164 | 165 | 166 | 167 | 继承关系用空心三角形+实现来表示。 168 | 169 | 170 | 171 | 这里列举的几种鸟中,大雁是最能飞的,我让它实现了飞翔接口。 172 | 173 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3Itj6xe3gRFHmhIa6iaqXHS64d6JuWibFXyqTRUox6jPD07ZgibLzwTrF4qg/640?wx_fmt=png) 174 | 175 | 176 | 177 | 实现接口用空心三角形+虚线来表示。 178 | 179 | 180 | 181 | 在看下图中企鹅和气候两个类,企鹅是很特别的鸟,会游不会飞。更重要的是,它与气候有很大的关联。我们不去讨论为什么北极没有企鹅,为什么它们要每年长途跋涉。 182 | 183 | 184 | 185 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3It27NUf3rj5fxDOJkNjfdh0qGsmVkB8PDHkIKp6NFSojib7B7UnvgAdDw/640?wx_fmt=png) 186 | 187 | 188 | 189 | 总之,企鹅需要“知道”气候的变化,需要“了解”气候规律。 190 | 191 | 192 | 193 | 当一个类“知道”另一个类时,可以用关联(association)。 194 | 195 | 196 | 197 | 关联关系用实现箭头来表示。 198 | 199 | 200 | 201 | ![](https://mmbiz.qpic.cn/mmbiz_jpg/TeYk478W36Ba9cUE7U4f1yJOrEDGA3ItHxVz4ia6qMvNV5Uicib8Gib9SxVOndqZJYYeKbctBIWAI4ZElt6ialPPadg/640?wx_fmt=jpeg) 202 | 203 | ![](https://mmbiz.qpic.cn/mmbiz_jpg/TeYk478W36Ba9cUE7U4f1yJOrEDGA3It53ibr3B3D3P9XPJeXZDowiarzWIH105Z0fkAMnu8zpUdddy4ChHawnWw/640?wx_fmt=jpeg) 204 | 205 | 我们再来看上图中大眼与雁群这两个类,大雁是群居动物,每只大雁都属于一个雁群,一个雁群可以又很多只大雁。 206 | 207 | 208 | 209 | 所以它们之间就满足聚合(Aggregation)关系。 210 | 211 | 212 | 213 | **聚合表示一种弱的“拥有”关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分。** 214 | 215 | 216 | 217 | 聚合关系用实心的菱形+实线箭头来表示。 218 | 219 | 220 | 221 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3ItXMYJmibSiaoHHw7HUA0wWQvBmXTTBxtkBjQo4iazOGCEBkhbufyAiavhXA/640?wx_fmt=png) 222 | 223 | 224 | 225 | 合成(Composition,也有翻译成“组合”的)是一种强的“拥有”关系,体现了严格的部分和整体的关系,部分和整体的声明周期一样。 226 | 227 | 228 | 229 | 在这里鸟和其翅膀就是合成(组合)关系,因为它们是部分和整体的关系,并且翅膀和鸟的声明周期是相同的。 230 | 231 | 232 | 233 | 合成关系用实心的菱形+实现箭头来表示。 234 | 235 | 236 | 237 | 另外,你会注意到合成关系的连线两端还有一个数字“1”和数字“2”,这被称为基数。表明这一段的类可以有几个实例,很显然,一个鸟应该有两只翅膀。 238 | 239 | 240 | 241 | 如果一个类可能有无数个实例,则就用“n”来表示。关联关系、聚合关系也可以有基数的。 242 | 243 | 244 | 245 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3It8Nv3SSzF6V7aviaicbdiatyEWCd3dJYQ4ngD1768DLib0h73Pria50Kick2w/640?wx_fmt=png) 246 | 247 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3It2S254Bbvib6fDO2fCxBic66geSokrWW5PYAbX6DplJmZaNDEr1FgTYIQ/640?wx_fmt=png) 248 | 249 | 250 | 251 | 动物几大特征,比如有新陈代谢,能繁殖。而动物要有生命力,需要氧气、水以及食物等。也就是说,动物依赖于氧气和水。 252 | 253 | 254 | 255 | 他们之间是依赖关系(Dependency),用虚线箭头来表示。 256 | 257 | 258 | 259 | 260 | 261 | **结语** 262 | 263 | 编程是一门技术,更加是一门艺术! 264 | 265 | 266 | 267 | 不能只满足于写完代码运行结构正确就完事,时常考虑如何让代码更加简练,更加容易维护,容易扩展和复用,只有这样才能可以真正得到提高。 268 | 269 | 270 | 271 | 切身感悟:自己几个月之前写完的代码,现在再来回顾,你会发现这代码简直了...,一团糟,甚至怀疑这代码不是出自自己之手(绝对是自己写的)。想着手优化,进行改进,又怕本来没问题的功能,改出问题,影响使用,然后又进入恶性循环了。。。这种情况要切记,一定要尽可能的避免! 272 | 273 | 274 | 275 | 写出优雅的代码也是一种很爽的事情!!! 276 | 277 | 278 | 279 | -------------------------------------------------------------------------------- /DesignPattern/从零开始单排学设计模式「单一职责原则」黑铁 - 青铜 晋级赛.md: -------------------------------------------------------------------------------- 1 | 阅读本文大概需要 3.7 分钟。 2 | 3 | 4 | 5 | 本篇是设计模式系列的第五篇,虽然之前也写过相应的文章,但是因为种种原因后来断掉了,而且发现之前写的内容也很渣,不够系统。 6 | 7 | 8 | 9 | 所以现在打算重写,加上距离现在也有一段时间了,也算是自己的一个回顾吧! 10 | 11 | 12 | 13 | 学而时习之,不亦说乎。 14 | 15 | 16 | 17 | **推荐阅读:** 18 | 19 | [从零开始单排学设计模式「简单工厂设计模式」黑铁 III](http://mp.weixin.qq.com/s?__biz=MzUyNDkzNzczNQ==&mid=2247485515&idx=1&sn=ad61fd16a315fa15acca3074a0406dca&chksm=fa24f723cd537e35095ff4a2e7d150f63cfaa46378b588abbb7a4548d86ecc9806cee8f6ab57&scene=21#wechat_redirect) 20 | 21 | [从零开始单排学设计模式「策略模式」黑铁 II](http://mp.weixin.qq.com/s?__biz=MzUyNDkzNzczNQ==&mid=2247485617&idx=1&sn=f38fd9b9a63ac7ade48391a8a564b7c6&chksm=fa24f7d9cd537ecfa25a3e911be6754b01e8c6a25f96b3a523b3d75f578082d9ed12d800c4be&scene=21#wechat_redirect) 22 | 23 | [从零开始单排学设计模式「装饰模式」黑铁 I](http://mp.weixin.qq.com/s?__biz=MzUyNDkzNzczNQ==&mid=2247485712&idx=1&sn=a95cce42095e59581600a51a9c193b23&chksm=fa24f678cd537f6ef46c6a6ab5a60aee24e42c87947c222cf9ea8973878c26eb8e38496ec69d&scene=21#wechat_redirect) 24 | 25 | 26 | 27 | 目前段位:**黑铁 I - 青铜 **I****I****I 晋级赛**** 28 | 29 | 30 | 31 | ![](https://mmbiz.qpic.cn/mmbiz_jpg/TeYk478W36DgvLzdtmInibnGr4bwx063cWHTN703WPR26EtTebUjPfZdicvEMnC1t3A635sG4IzYyvmB2Ahuuf8g/640?wx_fmt=jpeg) 32 | 33 | 34 | 35 | Let's Go! 36 | 37 | 38 | 39 | **前言** 40 | 41 | 42 | 43 | 设计模式不是语法,是一种巧妙的写法,能把程序变的更加灵活。架构模式比设计模式大,架构模式是战略,而设计模式是战术。 44 | 45 | 46 | 47 | 设计模式(面向对象设计)中有6大原则,分别是:单一职责原则、里氏替换原则、依赖倒转原则、接口隔离原则、迪米特法则、开闭原则。 48 | 49 | 50 | 51 | **单一职责原则** 52 | 53 | 54 | 55 | **单一职责原则(Single Responsibility Principle, SRP):**一个类只负责一个功能领域中的相应职责,或者可以定义为,就一个类而言,应该只有一个引起它变化的原因**。** 56 | 57 | 58 | 59 | 通俗易懂的讲,就是:一个类不能太“累“!它是最简单的面向对象设计原则,用于控制类的粒度大小。 60 | 61 | 62 | 63 | 在软件系统中,一个类(大到模块,小到方法)承担的职责越多,它被复用的可能性就越小,而且一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责变化时,可能会影响其他职责的运作。 64 | 65 | 66 | 67 | 因此要将这些职责进行分离,将不同的职责封装在不同的类中,即将不同的变化原因封装在不同的类中,如果多个职责总是同时发生改变则可将它们封装在同一类中。 68 | 69 | 70 | 71 | **业务需求** 72 | 73 | 74 | 75 | 实现一个手机系统,具备拍照和播放音乐的功能。 76 | 77 | 78 | 79 | **代码实现** 80 | 81 | 82 | 83 | 既然要实现拍照和播放音乐的功能,那我们先定义两个功能接口。 84 | 85 | 86 | 87 | 照相接口 88 | 89 | ```java 90 | /** 91 | * 具有照相功能的接口 92 | * @author: LKP 93 | * @date: 2019/2/16 94 | */ 95 | public interface IPhotograph { 96 | 97 | void photograph(); 98 | } 99 | ``` 100 | 101 | 102 | 103 | 播放音乐接口 104 | 105 | ```java 106 | /** 107 | * 具有播放音乐功能的接口 108 | * @author: LKP 109 | * @date: 2019/2/16 110 | */ 111 | public interface IPlayMusic { 112 | 113 | void playMusic(); 114 | } 115 | ``` 116 | 117 | 118 | 119 | 不遵循单一原则的设计,播放音乐及拍照功能的改变都会引起变化。 120 | 121 | 122 | 123 | 手机类 124 | 125 | ```java 126 | /** 127 | * @author: LKP 128 | * @date: 2019/2/16 129 | */ 130 | public class MobilePhone implements IPhotograph, IPlayMusic { 131 | 132 | /** 133 | * 拍照 134 | */ 135 | @Override 136 | public void photograph() { 137 | System.out.println("拍照片"); 138 | } 139 | 140 | /** 141 | * 播放音乐 142 | */ 143 | @Override 144 | public void playMusic() { 145 | System.out.println("播放音乐"); 146 | } 147 | } 148 | ``` 149 | 150 | 151 | 152 | 客户端代码 153 | 154 | ```java 155 | /** 156 | * @author: LKP 157 | * @date: 2019/2/16 158 | */ 159 | public class Main { 160 | 161 | public static void main(String[] args) { 162 | 163 | IPlayMusic musicPlayer; 164 | IPhotograph photographer; 165 | 166 | MobilePhone phone = new MobilePhone(); 167 | musicPlayer = phone; 168 | photographer = phone; 169 | 170 | //播放音乐 171 | musicPlayer.playMusic(); 172 | //拍照 173 | photographer.photograph(); 174 | } 175 | } 176 | ``` 177 | 178 | 179 | 180 | 遵循单一原则的设计,引发改变的只有播放音乐功能的变化。 181 | 182 | 183 | 184 | 音乐播放器类 185 | 186 | ```java 187 | /** 188 | * 实现播放音乐功能的音乐播放器类 189 | * @author: LKP 190 | * @date: 2019/2/16 191 | */ 192 | public class MusicPlayer implements IPlayMusic { 193 | @Override 194 | public void playMusic() { 195 | System.out.println("播放音乐"); 196 | } 197 | } 198 | ``` 199 | 200 | 201 | 202 | 遵循单一原则的设计,引发改变的只有摄像功能的变化。 203 | 204 | 205 | 206 | 摄像机类 207 | 208 | ```java 209 | /** 210 | * 实现照相功能的摄像机类 211 | * @author: LKP 212 | * @date: 2019/2/16 213 | */ 214 | public class Carmera implements IPhotograph { 215 | @Override 216 | public void photograph() { 217 | System.out.println("拍照片"); 218 | } 219 | } 220 | ``` 221 | 222 | 223 | 224 | 客户端代码 225 | 226 | ```java 227 | /** 228 | * @author: LKP 229 | * @date: 2019/2/16 230 | */ 231 | public class Main { 232 | 233 | public static void main(String[] args) { 234 | 235 | IPlayMusic musicPlayer; 236 | IPhotograph photographer; 237 | //MobilePhone phone = new MobilePhone(); 238 | //musicPlayer = phone; 239 | //photographer = phone; 240 | musicPlayer = new MusicPlayer(); 241 | photographer = new Carmera(); 242 | //播放音乐 243 | musicPlayer.playMusic(); 244 | //拍照 245 | photographer.photograph(); 246 | } 247 | } 248 | ``` 249 | 250 | 251 | 252 | 糟糕的设计会造成什么样的后果呢? 253 | 254 | 255 | 256 | 让我们来设想一下,有一天我们的需求发生了变化(需求变化-程序员一生的敌人兼朋友),现有拥有了一部手机,有拍照的功能以及播放音乐的功能,满足了现有的需求。 257 | 258 | 259 | 260 | 突然有一天觉得简单的拍照功能已经不能满足了,现在需要能够拍摄高清图片的照相功能,那么怎么办呢?换手机呗,找一个支持高清拍照功能的手机。 261 | 262 | 263 | 264 | 那么好的,需求又变了,现在想要能播放高品质音乐的功能,但是新换的支持高清拍摄的手机的硬件不支持高品质音乐播放,好的,继续换手机,前提是还要支持拍摄高清照片。 265 | 266 | 267 | 268 | 相信现在已经能够看出一些端倪了,这两个职责无论哪一个发生了变化,你都要去改变手机,现在只有两个职责,夸张一点说,如果有十个职责呢?那么岂不是要天天换手机,要么就不满足需求变化。 269 | 270 | 271 | 272 | 既然我们看到了糟糕的设计,现在我们回到单一职责上,既然你的需求是拍照片和播放音乐,那么我给你一台相机还有一台音乐播放器,哪个功能需要改变你就换哪个,以后你要换的时候也不必去考虑其他功能,只需要关心引起你自己变化的原因。 273 | 274 | 275 | 276 | 如果拍照功能发生改变,我们就去改变照相机,播放音乐功能需要改变我们就去改变音乐播放器。 277 | 278 | 279 | 280 | 我们不需要去考虑播放高品质音乐是不是会对拍摄高清图片的功能造成影响。 281 | 282 | 283 | 284 | 我们一定要遵循单一职责原则吗? 285 | 286 | 287 | 288 | 在现有的需求上能做到当然可以去做,但是往往有的时候,需求不是在设计的时候发生改变,而是一定程度之后,你已经有了一定的代码量了,可能修改的开销很高,这个时候就仁者见仁智者见智。 289 | 290 | 291 | 292 | 就如上述,若是将手机类拆分,则影响了底层调用的实现,也需要修改,弱是调用的地方太多,那么修改的地方也会很多,若是发布了,改起来也不是很方便。 293 | 294 | 295 | 296 | 但是当然,也有一定的手法来做这件事情,比如手机类保留,让手机类拥有一个摄像机类对象和一个音乐播放器类对象,然后播放音乐方法则调用音乐播放器类实例的播放音乐功能,照相功能则调用摄像机类实例的照相功能。 297 | 298 | 299 | 300 | 这样可以在不影响原有的东西的基础上又遵循原则。 301 | 302 | 303 | 304 | **总结** 305 | 306 | 单一职责原则是实现**高内聚**、**低耦合**的指导方针,它是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,而发现类的多重职责需要设计人员具有较强的分析设计能力和相关实践经验。 307 | 308 | 309 | 310 | 遵循单一职责原则的优点: 311 | 312 | 1)降低类的复杂度,一个类只负责一项职责。 313 | 2)提高类的可读性,可维护性 314 | 3)降低变更引起的风险。 315 | -------------------------------------------------------------------------------- /DesignPattern/从零开始单排学设计模式「策略模式」黑铁 II.md: -------------------------------------------------------------------------------- 1 | 阅读本文大概需要 1.7 分钟。 2 | 3 | 4 | 5 | 本篇是设计模式系列的第三篇,虽然之前也写过相应的文章,但是因为种种原因后来断掉了,而且发现之前写的内容也很渣,不够系统。所以现在打算重写,加上距离现在也有一段时间了,也算是自己的一个回顾吧! 6 | 7 | 8 | 9 | 学而时习之,不亦说乎。 10 | 11 | 12 | 13 | **推荐阅读:** 14 | 15 | [从零开始单排学设计模式「UML类图」定级赛](http://mp.weixin.qq.com/s?__biz=MzUyNDkzNzczNQ==&mid=2247485514&idx=1&sn=dd33498d72c50d7facad6f14957f022a&chksm=fa24f722cd537e342f293ce08831d1e7bd31b412be743a2dfeaa0f8f60414e32b726e3478b90&scene=21#wechat_redirect) 16 | 17 | [从零开始单排学设计模式「简单工厂设计模式」黑铁III](http://mp.weixin.qq.com/s?__biz=MzUyNDkzNzczNQ==&mid=2247485515&idx=1&sn=ad61fd16a315fa15acca3074a0406dca&chksm=fa24f723cd537e35095ff4a2e7d150f63cfaa46378b588abbb7a4548d86ecc9806cee8f6ab57&scene=21#wechat_redirect) 18 | 19 | 20 | 21 | 目前段位:**黑铁 II** 22 | 23 | 24 | 25 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3IthTMOombgIog0M2B8VnxsAw6ggzMbNBrRKbIesUjiaWGX5oet94IU20w/640?wx_fmt=png) 26 | 27 | 28 | 29 | Let's Go! 30 | 31 | 32 | 33 | **前言** 34 | 35 | 36 | 37 | 设计模式不是语法,是一种巧妙的写法,能把程序变的更加灵活。架构模式比设计模式大,架构模式是战略,而设计模式是战术。 38 | 39 | 40 | 41 | 设计模式分为3大类型:创建型,行为型,结构型,总共有23种。 42 | 43 | 44 | 45 | **策略模式** 46 | 47 | 48 | 49 | 策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。 50 | 51 | 52 | 53 | 用一句话来说,就是:“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。 54 | 55 | 56 | 57 | 在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。 58 | 59 | 60 | 61 | 设计原则有很多,这里直说策略模式中使用到的,参看实例思考哪些地方有用到下面的设计模式:  62 | 1\. 封装变化(找出应用中可能需要变化之处,把它们独立出来,不要和哪些不需要变化的代码混在一起。)  63 | 2\. 针对接口,超类编程,而不是针对实现编程。  64 | 3\. 多用组合,少用继承。 65 | 66 | 67 | 68 | **业务需求** 69 | 70 | 71 | 72 | 如果让你设计一个商场收银软件,营业员根据客户所购买商品的单价和数量,向客户收费。 73 | 74 | 75 | 76 | **代码实现** 77 | 78 | 79 | 80 | 看到需求的你,对它进行了一个分析,只需要把数量乘以单价就可以得出总费用了,这可难不倒我,然后写出了最初版本。 81 | 82 | 83 | 84 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36BjupOrhxkPgvCBgL9BymJBHqianLr0lMYp9dANNCrRLS9D6qt7uHy9k82Vc5HEwTlibibAAtuOtk4Hw/640?wx_fmt=png) 85 | 86 | 87 | 88 | 很简单,直接声明方法然后就计算出结算的总价了。 89 | 90 | 91 | 92 | 当你准备提交代码的时候,万恶的产品找到你,给出了一个新版的需求。(暗地里画个圈圈咒诅产品...) 93 | 94 | 95 | 96 | **新的业务需求** 97 | 98 | 99 | 100 | 如果让你设计一个商场收银软件,营业员根据客户所购买商品的单价和数量,向客户收费。就是现在商场需要搞活动,会给顾客购买的商品进行打折,比如七五折,九五折等等。 101 | 102 | 103 | 104 | **代码实现** 105 | 106 | 107 | 108 | 这不是计算完总价格之后,在后面乘以一下折扣就可以了。 109 | 110 | 111 | 112 | 经过改版,新的代码如下: 113 | 114 | 115 | 116 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36BjupOrhxkPgvCBgL9BymJBCwTgt8iaUdqr4ibDiaUgZibktolQvVHgiab0AhAIMRJEmAyS2UibJU7wKfqw/640?wx_fmt=png) 117 | 118 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36BjupOrhxkPgvCBgL9BymJB2um62reFn946vYrc2cg00ShrhKvprWV5IVnxtjPEFncXHXW2tl3VRQ/640?wx_fmt=png) 119 | 120 | 121 | 122 | 还没有等你进行测试,产品就又找上门来了,我们可能后面会实现满300送100、满200送50....等等的很多活动,所以你程序需要设计灵活一点哈,不然到时候我的需求可以还会有变动的。 123 | 124 | 125 | 126 | 看到这里,没办法,还是得继续完善程序了,想偷个懒都没有办法,思考这种种得可能,你找寻这有没有能完美贴切这种需求得处理方法。 127 | 128 | 129 | 130 | 打一折和打九折只是形式的不同,抽象分析出来,所有的打折算法都是一样的,所以打折算法应该是一个类。冥思苦想下,你又写出了第三版程序: 131 | 132 | 133 | 134 | 这里抽离出来一个现金收费类,现金收取类的抽象方法,收取现金,参数为原价,返回当前价。 135 | 136 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36BjupOrhxkPgvCBgL9BymJBlOQULNNgQjdBOK5CsE4ibrTvqtEpODLd3pOVzzzBDibK87B03cBUB3Lg/640?wx_fmt=png) 137 | 138 | 139 | 140 | 正常收费子类,正常收费,原价返回。 141 | 142 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36BjupOrhxkPgvCBgL9BymJBhgKXPINQv9r7dbvcO9f8kIBMRZSTQQmjGj2Ourhm2XxbCnb9VyqpnQ/640?wx_fmt=png) 143 | 144 | 145 | 146 | 打折收费子类 147 | 148 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36BjupOrhxkPgvCBgL9BymJBjqsBasuVkbvqkCz1NHVUIic8Lk513uD9Acr2v0WW5d5ctlrHY9RWUyw/640?wx_fmt=png) 149 | 150 | 151 | 152 | 返利收费子类 153 | 154 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36BjupOrhxkPgvCBgL9BymJB3lOYdhjfRsdTfH19oRpLQUGSZ5AKRtGZ0vibpzAkMIIQRxjKD6kj84g/640?wx_fmt=png) 155 | 156 | 157 | 158 | CashContext类,通过构造方法,传入具体的收费策略。 159 | 160 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36BjupOrhxkPgvCBgL9BymJBukKTjrI9AMheKcqdXrI0QoD9fKFW3icfsIXw2BmSz5TzdeibL4vZ8pfQ/640?wx_fmt=png) 161 | 162 | 客户端主要代码 163 | 164 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36BjupOrhxkPgvCBgL9BymJBX8iaI8Vh4e9FUracxicRLbYKmnYCcicNhYiakQNdN6dwaEarNaaW7Gn89g/640?wx_fmt=png) 165 | 166 | 167 | 168 | 运行结果 169 | 170 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36BjupOrhxkPgvCBgL9BymJBqGHKHH60UW0iaWc1jb5ZLh9CxGrVicdZwLdDtwOWYNIgibyd8qq5cFGoA/640?wx_fmt=png) 171 | 172 | 173 | 174 | 现在就再也不怕了,如果再增加新的功能,只需要创建新的子类即可。 175 | 176 | 177 | 178 | **策略模式UML类图** 179 | 180 | 181 | 182 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Dfg1G11fMcctX10zvQmUKeaKfRtG8JnfWa4cBBLfyMQfsCVeia3HiageIqEPgvTsvwdqjgXGLA7XyA/640?wx_fmt=png) 183 | 184 | 185 | 186 | **总结** 187 | 188 | 189 | 190 | **意图**:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。 191 | 192 | 193 | 194 | **主要解决**:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。 195 | 196 | 197 | 198 | **何时使用**:一个系统有许多许多类,而区分它们的只是他们直接的行为。 199 | 200 | 201 | 202 | **如何解决**:将这些算法封装成一个一个的类,任意地替换。 203 | 204 | 205 | 206 | **关键代码**:实现同一个接口。 207 | 208 | 209 | 210 | **应用实例**:  211 | 212 |         1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。  213 | 214 |         2、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。  215 | 216 |     3、JAVA AWT 中的 LayoutManager。 217 | 218 | 219 | 220 | **优点**:  221 | 222 |         1、算法可以自由切换。 223 | 224 |         2、避免使用多重条件判断。  225 | 226 |         3、扩展性良好。 227 | 228 | 229 | 230 | **缺点**:  231 | 232 |         1、策略类会增多。  233 | 234 |         2、所有策略类都需要对外暴露。 235 | 236 | 237 | 238 | **使用场景**:  239 | 240 |         1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。  241 | 242 |         2、一个系统需要动态地在几种算法中选择一种。  243 | 244 |         3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。 245 | 246 | 247 | 248 | **注意事项**:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。 249 | -------------------------------------------------------------------------------- /DesignPattern/从零开始单排学设计模式「简单工厂设计模式」黑铁 III.md: -------------------------------------------------------------------------------- 1 | 阅读本文大概需要 2 分钟。 2 | 3 | 4 | 5 | 本篇是设计模式系列的第二篇,虽然之前也写过相应的文章,但是因为种种原因后来断掉了,而且发现之前写的内容也很渣,不够系统。所以现在打算重写,加上距离现在也有一段时间了,也算是自己的一个回顾吧! 6 | 7 | 8 | 9 | 学而时习之,不亦说乎。 10 | 11 | 12 | 13 | **推荐阅读:** 14 | 15 | [从零开始单排学设计模式「UML类图」定级赛](http://mp.weixin.qq.com/s?__biz=MzUyNDkzNzczNQ==&mid=2247485514&idx=1&sn=dd33498d72c50d7facad6f14957f022a&chksm=fa24f722cd537e342f293ce08831d1e7bd31b412be743a2dfeaa0f8f60414e32b726e3478b90&scene=21#wechat_redirect) 16 | 17 | 18 | 19 | 目前段位:**黑铁 III** 20 | 21 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3IthTMOombgIog0M2B8VnxsAw6ggzMbNBrRKbIesUjiaWGX5oet94IU20w/640?wx_fmt=png) 22 | 23 | 24 | 25 | Let's Go! 26 | 27 | 28 | 29 | **前言** 30 | 31 | 32 | 33 | 设计模式不是语法,是一种巧妙的写法,能把程序变的更加灵活。架构模式比设计模式大,架构模式是战略,而设计模式是战术。 34 | 35 | 36 | 37 | 设计模式分为3大类型:创建型,行为型,结构型,总共有23种。 38 | 39 | 40 | 41 | **简单工厂模式** 42 | 43 | 44 | 45 | 简单工厂模式设计模式属于创建型设计模式,但不属于23种设计模式范围内,属于23种设计模式中工厂方法的入门模式,又叫静态工厂方法模式。 46 | 47 | 48 | 49 | 简单工厂模式是一个工厂对象决定创建出哪一种产品类的实例。它的好处是隐藏对象创建,不用依赖对象,可以随意更换对象。 50 | 51 | 52 | 53 | 在工厂模式中,工厂类中的所有方法返回同一个抽象产品类,代码的升级和扩展只需要在这个工厂类中注入新的功能方法,返回抽象产品类即可,或是另外写接口,将这个抽象产品类强制转换成这个接口,让具体产品类去实现。 54 | 55 | 56 | 57 | **业务需求** 58 | 59 | 60 | 61 | 如果让你设计一个简单的计算器,实现两位数的简单加减乘除运算,你会怎么设计呢? 62 | 63 | 64 | 65 | **代码实现** 66 | 67 | 68 | 69 | 刚接到需求的你,绝对会发现,需求是如此so easy(很容易),刷刷刷的就完成了任务。 70 | 71 | 72 | 73 | 实现思路很简单,一个运算类,用来计算结果的,一个计算器类,用来实现交互并接收参数的。 74 | 75 | 76 | 77 | 写完的代码如下 78 | 79 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3ItqGiaL4iaDAjzM1QOL8BNibbU11icdG06UAKjp9qVC4jrY1c6IXibF7DZDZA/640?wx_fmt=png) 80 | 81 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3Itt8EZlXZHlbp28oNd1fd29xF11iclztib6FcDqsaUjLk9Tu5dfUjbGeYQ/640?wx_fmt=png) 82 | 83 | 我们来运行一遍,看下结果 84 | 85 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3ItCdNH09Yz1bllKDxdwOPl1xBwibQO0xCtfj3TCRPC0j5racLbPlSWC1A/640?wx_fmt=png) 86 | 87 | 是不是很简单,哈哈,我真是个天才(自恋一下),写完之后看着自己写的代码,总觉得似乎不妥。 88 | 89 | 90 | 91 | Java的特点是面向对象,而面向对象的三大特征是封装、继承和多态。我这里好像只用到了封装,将运算类进行一个简单封装,但是继承和多态却没有体现出来.....要是这样提交代码,被leader(领导)看到那岂不是要被拖出去枪毙,不行,还得再改改。 92 | 93 | 94 | 95 | 再次改版之后运算类Operation代码如下 96 | 97 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3It997AvrkNXiaiaqkcUpGYDT5bm6LPF0VpbeP75ibQE3AYENNNQd8nMXfUw/640?wx_fmt=png) 98 | 99 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3ItBoGE32N5zpib0coM8hURs9Sxw18ekDtzTZEt0t7GicjDzKeEpFDVn57w/640?wx_fmt=png) 100 | 101 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3ItjfNS3fjeBrWBoIZ2LWaeQCMG13xhna2IXN78DfHWibzIAgmQmUlmjMw/640?wx_fmt=png) 102 | 103 | (PS:这里仅仅是为了方便所以声明的是内部类,实际开发类似的程序时不要这样写,因为如果程序变复杂之后,这种编写方式不利于维护) 104 | 105 | 106 | 107 | 新创建了一个简单工厂类,用于实现业务逻辑。 108 | 109 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3ItwKd77UWlSVb7vOv1xh5cb0Sdw5eTPl543ayBc0zicCm8bJZVJnHHia8Q/640?wx_fmt=png) 110 | 111 | 计算器类改动不大 112 | 113 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3ItRblwsNlT5Yg8BB6qqURAWxOiasyrOoggciaaLyFybKIbGAlsrqUKLV8w/640?wx_fmt=png) 114 | 115 | 来试试新版的计算器,看一下结果 116 | 117 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3ItDTOibVKylDHsppOlpLXfDLuS4jAFfichGibRq3EqT7nJ8kPk8hUEUL80w/640?wx_fmt=png) 118 | 119 | 这样提交代码问题应该不大了。 120 | 121 | 122 | 123 | 新版的代码中,你只需要输入运算符号,工厂就实例化出合适的对象,通过多态,返回父类的方式实现了计算器的结果。同时,如果后续需要对程序维护,如要维护加法类,那么只需要提供新增方法的代码即可,不需要全部代码都提供,提高了整个项目的安全性。 124 | 125 | 126 | 127 | **简单工厂模式UML类图** 128 | 129 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3ItTQS4iaqE1G3X1ln1oAvpLRKGovSPIbyiaExOM5yVbjuvAic1lhAYKA07Q/640?wx_fmt=png) 130 | 131 | 132 | 133 | **总结** 134 | 135 | 工厂(Factory):用一个单独的类来做这个类创造实例的过程,这就是工厂。 136 | 137 | 138 | 139 | 简单工厂模式解决的就是对象创建问题。 140 | 141 | 142 | 143 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3ItoGDzYbtPXptMFdFaExBRzMLXeu0wYmcGtqIVnajQGd1A3OrMBUOSsQ/640?wx_fmt=png) 144 | 145 | 当我们已知的某些条件后,对类的选择,而这些类都是同一父类的子类,那么我们就可以使用简单工厂模式。 146 | -------------------------------------------------------------------------------- /DesignPattern/从零开始单排学设计模式「装饰模式」黑铁 I.md: -------------------------------------------------------------------------------- 1 | 阅读本文大概需要 3.6 分钟。 2 | 3 | 4 | 5 | 本篇是设计模式系列的第四篇,虽然之前也写过相应的文章,但是因为种种原因后来断掉了,而且发现之前写的内容也很渣,不够系统。 6 | 7 | 8 | 9 | 所以现在打算重写,加上距离现在也有一段时间了,也算是自己的一个回顾吧! 10 | 11 | 12 | 13 | 学而时习之,不亦说乎。 14 | 15 | 16 | 17 | **推荐阅读:** 18 | 19 | [从零开始单排学设计模式「UML类图」定级赛](http://mp.weixin.qq.com/s?__biz=MzUyNDkzNzczNQ==&mid=2247485514&idx=1&sn=dd33498d72c50d7facad6f14957f022a&chksm=fa24f722cd537e342f293ce08831d1e7bd31b412be743a2dfeaa0f8f60414e32b726e3478b90&scene=21#wechat_redirect) 20 | 21 | [从零开始单排学设计模式「简单工厂设计模式」黑铁 III](http://mp.weixin.qq.com/s?__biz=MzUyNDkzNzczNQ==&mid=2247485515&idx=1&sn=ad61fd16a315fa15acca3074a0406dca&chksm=fa24f723cd537e35095ff4a2e7d150f63cfaa46378b588abbb7a4548d86ecc9806cee8f6ab57&scene=21#wechat_redirect) 22 | 23 | [从零开始单排学设计模式「策略模式」黑铁 II](http://mp.weixin.qq.com/s?__biz=MzUyNDkzNzczNQ==&mid=2247485617&idx=1&sn=f38fd9b9a63ac7ade48391a8a564b7c6&chksm=fa24f7d9cd537ecfa25a3e911be6754b01e8c6a25f96b3a523b3d75f578082d9ed12d800c4be&scene=21#wechat_redirect) 24 | 25 | 26 | 27 | 目前段位:**黑铁 I** 28 | 29 | 30 | 31 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36Ba9cUE7U4f1yJOrEDGA3IthTMOombgIog0M2B8VnxsAw6ggzMbNBrRKbIesUjiaWGX5oet94IU20w/640?wx_fmt=png) 32 | 33 | 34 | 35 | Let's Go! 36 | 37 | 38 | 39 | **前言** 40 | 41 | 42 | 43 | 设计模式不是语法,是一种巧妙的写法,能把程序变的更加灵活。架构模式比设计模式大,架构模式是战略,而设计模式是战术。 44 | 45 | 46 | 47 | 设计模式分为3大类型:创建型,行为型,结构型,总共有23种。 48 | 49 | 50 | 51 | **装饰模式** 52 | 53 | 54 | 55 | 装饰模式(Decorator)指的是在不必改变类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。 56 | 57 | 58 | 59 | 这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。 60 | 61 | 62 | 63 | **业务需求** 64 | 65 | 66 | 67 | 公司接到一个任务,需要为某平台开发一个搭配不同服饰的小项目,比如类似QQ、网络游戏或论坛都有的Avatar系统(为了简化代码,直接使用控制台模拟)。 68 | 69 | 70 | 71 | **代码实现** 72 | 73 | 74 | 75 | 经过公司的慎重讨论(实际就几秒钟),开发这一个项目的重任,又当仁不让的被产品经理交给了我,我:脸上笑嘻嘻,心里MMP。发一下下的小牢骚,不过还是抓紧干活。 76 | 77 | 78 | 79 | 思索一下,该系统要为不同的人进行装扮,所以定义一个人的类,不用每次装扮其他人时修改该类的代码。 80 | 81 | 82 | 83 | 然后人身上要有很多的服饰,比如:大T恤、垮裤、鞋子等等,然后穿上之后,需要展示出来。所以这里的话,可以抽象出一个服饰的基类,然后各个具体的服饰都继承该基类即可。 84 | 85 | 86 | 87 | 代码如下: 88 | 89 | Person类 90 | 91 | ```java 92 | /** 93 | * @author: LKP 94 | * @date: 2019/2/16 95 | */ 96 | public class Person { 97 | 98 | private String name; 99 | public Person() { 100 | } 101 | 102 | public Person(String name) { 103 | this.name = name; 104 | } 105 | 106 | public void show() { 107 | System.out.println("装扮者:" + name); 108 | } 109 | } 110 | ``` 111 | 112 | 服装抽象类 113 | 114 | ```java 115 | /** 116 | * @author: LKP 117 | * @date: 2019/2/16 118 | */ 119 | public class Finery extends Person { 120 | 121 | protected Person component; 122 | /** 123 | * 打扮 124 | * @param component 125 | */ 126 | public void decorate(Person component){ 127 | this.component = component; 128 | } 129 | 130 | @Override 131 | public void show() { 132 | if(null != component){ 133 | component.show(); 134 | } 135 | } 136 | } 137 | ``` 138 | 139 | 具体服饰类 140 | 141 | ```java 142 | /** 143 | * @author: LKP 144 | * @date: 2019/2/16 145 | */ 146 | public class TShirts extends Finery { 147 | 148 | @Override 149 | public void show() { 150 | System.out.println("大T恤"); 151 | super.show(); 152 | } 153 | } 154 | 155 | class BigTrouser extends Finery { 156 | 157 | @Override 158 | public void show() { 159 | System.out.println("垮裤"); 160 | super.show(); 161 | } 162 | } 163 | 164 | class Sneakers extends Finery{ 165 | 166 | @Override 167 | public void show() { 168 | System.out.println("破球鞋"); 169 | super.show(); 170 | } 171 | } 172 | 173 | class LeatherShoes extends Finery{ 174 | 175 | @Override 176 | public void show() { 177 | System.out.println("皮鞋"); 178 | super.show(); 179 | } 180 | } 181 | 182 | class Tie extends Finery{ 183 | 184 | @Override 185 | public void show() { 186 | System.out.println("领带"); 187 | super.show(); 188 | } 189 | } 190 | 191 | class Suit extends Finery{ 192 | 193 | @Override 194 | public void show() { 195 | System.out.println("西装"); 196 | super.show(); 197 | } 198 | } 199 | ``` 200 | 201 | 这里内部类,只是为了较少代码量,实际开发中可不要偷懒,按实际来创建。 202 | 203 | 204 | 205 | 客户端代码 206 | 207 | ```java 208 | /** 209 | * @author: LKP 210 | * @date: 2019/2/16 211 | */ 212 | public class Main { 213 | 214 | public static void main(String[] args) { 215 | 216 | Person person = new Person("孤独键客"); 217 | 218 | System.out.println("第一种装扮:"); 219 | 220 | Finery tShirts = new TShirts(); 221 | Finery bigTrouser = new BigTrouser(); 222 | Finery sneakers = new Sneakers(); 223 | 224 | tShirts.show(); 225 | bigTrouser.show(); 226 | sneakers.show(); 227 | person.show(); 228 | 229 | System.out.println("\n第二种装扮:"); 230 | 231 | Finery suit = new Suit(); 232 | Finery tie = new Tie(); 233 | Finery leatherShoes = new LeatherShoes(); 234 | 235 | suit.show(); 236 | tie.show(); 237 | leatherShoes.show(); 238 | person.show(); 239 | } 240 | } 241 | ``` 242 | 243 | 244 | 245 | 代码简单搞定,接下来来看一下运行结果 246 | 247 | 248 | 249 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36AMsMtuwgjHeEjAfsP4zuibZTJVD1kIl59pB9Zibrv7ArC9HGuQ3mFgCrJqddCibVUouMseCd8bJU0Aw/640?wx_fmt=png) 250 | 251 | 搞定收工,审视一下,自我感觉还算不错,如果新装扮只需改变一下调用顺序即可,如果又新人物,只需重新new一个Person类就可以了。 252 | 253 | 254 | 255 | 接下里将项目提交上传,然后告诉leader一声,over,离下班时间还早,好像还可以做点其他的事情~。 256 | 257 | 258 | 259 | 正当你准备打开去干点其他事情,leader回复你了: 260 | 261 | 262 | 263 | leader:“你仔细看看这段代码,这样写意味着什么? 264 | 265 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36AMsMtuwgjHeEjAfsP4zuibZr64LHY9x78KzsJOp24BDsSMH1icSTyMYytliaoUr7AHlqlHs5ft0SpLA/640?wx_fmt=png) 266 | 267 | 你想象一下,是不是把‘大T恤’、‘垮裤’、‘破球鞋’、‘装扮者’一个一个词显示出来,是不是相当于你光着身子,一个一个把这些穿上,这可有点像脱衣舞哦~”。 268 | 269 | 270 | 271 | 我:“你意思是,这些应该都在内部组装完毕,然后在显示出来?”。 272 | 273 | 274 | 275 | leader:"宾果,而且还要按照正确的顺序串联起来控制,这里有点难度,修改好之后再给我"。 276 | 277 | 278 | 279 | 这似乎和某种设计模式有关,难道是建造者模式吗?不对,建造者模式要求建造的过程必须是稳定的,而这个穿搭的过程是不固定的,一个有个性的人又无数种方案。 280 | 281 | 282 | 283 | 经过一番查找,这恰恰最适合用装饰模式了。 284 | 285 | 286 | 287 | 我们修改一下具体的服饰类 288 | 289 | ```java 290 | /** 291 | * @author: LKP 292 | * @date: 2019/2/16 293 | */ 294 | public class TShirts extends Finery { 295 | 296 | @Override 297 | public void show() { 298 | System.out.println("大T恤"); 299 | super.show(); 300 | } 301 | } 302 | 303 | class BigTrouser extends Finery { 304 | 305 | @Override 306 | public void show() { 307 | System.out.println("垮裤"); 308 | super.show(); 309 | } 310 | } 311 | 312 | class Sneakers extends Finery{ 313 | 314 | @Override 315 | public void show() { 316 | System.out.println("破球鞋"); 317 | super.show(); 318 | } 319 | } 320 | 321 | class LeatherShoes extends Finery{ 322 | 323 | @Override 324 | public void show() { 325 | System.out.println("皮鞋"); 326 | super.show(); 327 | } 328 | } 329 | 330 | class Tie extends Finery{ 331 | 332 | @Override 333 | public void show() { 334 | System.out.println("领带"); 335 | super.show(); 336 | } 337 | } 338 | 339 | class Suit extends Finery{ 340 | 341 | @Override 342 | public void show() { 343 | System.out.println("西装"); 344 | super.show(); 345 | } 346 | } 347 | ``` 348 | 349 | 再修改一下客户端代码: 350 | 351 | ```java 352 | /** 353 | * @author: LKP 354 | * @date: 2019/2/16 355 | */ 356 | public class Main { 357 | 358 | public static void main(String[] args) { 359 | 360 | Person person = new Person("孤独键客"); 361 | System.out.println("第一种装扮:"); 362 | Sneakers sneakers = new Sneakers(); 363 | BigTrouser bigTrouser = new BigTrouser(); 364 | TShirts tShirts = new TShirts(); 365 | sneakers.decorate(person); 366 | bigTrouser.decorate(sneakers); 367 | tShirts.decorate(bigTrouser); 368 | tShirts.show(); 369 | System.out.println("第二种装扮:"); 370 | LeatherShoes leatherShoes = new LeatherShoes(); 371 | Tie tie = new Tie(); 372 | Suit suit = new Suit(); 373 | leatherShoes.decorate(person); 374 | tie.decorate(leatherShoes); 375 | suit.decorate(tie); 376 | suit.show(); 377 | } 378 | } 379 | ``` 380 | 381 | 第二版的程序写完了,来测试一下 382 | 383 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36AMsMtuwgjHeEjAfsP4zuibZdby1fymmYkqNmF6xtQQX3NwV4OQaKWbbMiaMzcLB9E5JrAyWSFf7hqQ/640?wx_fmt=png) 384 | 385 | 完美搞定,哈哈,我还可以换种装饰方式 386 | 387 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36AMsMtuwgjHeEjAfsP4zuibZzmPVf2vzqQ7RPctCApz2MeDaWBnoJACP4JR85D9SUfeMelHDpvHCwA/640?wx_fmt=png) 388 | 389 | 看下结果 390 | 391 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36AMsMtuwgjHeEjAfsP4zuibZyib6kHfsvQAZmCUQB44dlL951B2MfEzUGqibGDaXI5qRIf2LqLiboXX5A/640?wx_fmt=png) 392 | 393 | 光着膀子、打着领带、下身垮裤、脚上皮鞋,绝对的极具个性。 394 | 395 | 396 | 397 | 最后,完美搞定,提交代码~~~ 398 | 399 | 400 | 401 | **装饰模式UML类图** 402 | 403 | ![](https://mmbiz.qpic.cn/mmbiz_png/TeYk478W36AMsMtuwgjHeEjAfsP4zuibZUYvnT1u9EO3LLjPEnUJ2MGlUELGE9dialQN9fVgpWGYHrE1nZ31tkoA/640?wx_fmt=png) 404 | 405 | 406 | 407 | **总结** 408 | 409 | 410 | 411 | 来总结一下装饰模式: 412 | 413 | 414 | 415 | **主要解决**:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。 416 | 417 | 418 | 419 | **何时使用**:在不想增加很多子类的情况下扩展类。 420 | 421 | 422 | 423 | **如何解决**:将具体功能职责划分,同时继承装饰者模式。 424 | 425 | 426 | 427 | **关键代码**: 1、Component 类充当抽象角色,不应该具体实现。 2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。 428 | 429 | 430 | 431 | **应用实例**: 1、孙悟空有 72 变,当他变成"庙宇"后,他的根本还是一只猴子,但是他又有了庙宇的功能。 2、不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。 432 | 433 | 434 | 435 | **优点**:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。 436 | 437 | 438 | 439 | **缺点**:多层装饰比较复杂。 440 | 441 | 442 | 443 | **使用场景**: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。 444 | 445 | 446 | 447 | **注意事项**:可代替继承。 448 | -------------------------------------------------------------------------------- /Java/HTTP API 认证授有哪些方法?.md: -------------------------------------------------------------------------------- 1 | 我们知道,HTTP是无状态的,所以,当我们需要获得用户是否在登录的状态时,我们需要检查用户的登录状态,一般来说,用户的登录成功后,服务器会发一个登录凭证(又被叫作Token),就像你去访问某个公司,在前台被认证过合法后,这个公司的前台会给你的一个访客卡一样,之后,你在这个公司内去到哪都用这个访客卡来开门,而不再校验你是哪一个人。 2 | 3 | 在计算机的世界里,这个登录凭证的相关数据会放在两种地方,一个地方在用户端,以Cookie的方式(一般不会放在浏览器的Local Storage,因为这很容易出现登录凭证被XSS攻击),另一个地方是放在服务器端,又叫Session的方式(SessonID存于Cookie)。 4 | 5 | 但是,这个世界还是比较复杂的,除了用户访问,还有用户委托的第三方的应用,还有企业和企业间的调用,这里,我想把业内常用的一些API认证技术相对系统地总结归纳一下,这样可以让大家更为全面的了解这些技术。 6 | 7 | 方法如下: 8 | 9 | 1. HTTP Basic(它是一个非常传统的API认证技术,也是一个比较简单的技术) 10 | 11 | 2. Digest Access(中文成“HTTP摘要认证”,最初被定义在了RFC 2069文档中) 12 | 13 | 3. App Secret Key + HMAC 14 | 15 | 4. JWT - JSON Web Tokens 16 | 17 | 5. OAuth 1.0 18 | 19 | 6. OAuth 2.0 20 | 21 | 22 | 摘录自:https://coolshell.cn/articles/19395.html 23 | -------------------------------------------------------------------------------- /Java/Java 技术栈中的技术点缩写.md: -------------------------------------------------------------------------------- 1 | 你有在面试过程中,面试官说出技术缩写时,一脸懵逼的感受吗? 2 | 3 | ### 事物的 ACID 4 | 5 | 事务具有四个特征:原子性( Atomicity )、一致性( Consistency )、隔离性( Isolation )和持续性( Durability )。这四个特性简称为 ACID 特性。 6 | 7 | 参考:[面试问烂的 MySQL 四种隔离级别,看完吊打面试官!](https://zhuanlan.zhihu.com/p/76743929) 8 | 9 | **待续...** 10 | 11 | -------------------------------------------------------------------------------- /Java/SQL编写规范.md: -------------------------------------------------------------------------------- 1 | SQL,一些有意思的TIPS,希望大家有收获。 2 | 3 | 一、一些常见的SQL实践 4 | 5 | (1)负向条件查询不能使用索引。 6 | 7 | select * from order where status!=0 and stauts!=1 8 | 9 | not in/not exists都不是好习惯。 10 | 11 | 可以优化为in查询: 12 | 13 | select * from order where status in(2,3) 14 | 15 | (2)前导模糊查询不能使用索引。 16 | 17 | select * from order where desc like '%XX' 18 | 19 | 而非前导模糊查询则可以: 20 | 21 | select * from order where desc like 'XX%' 22 | 23 | (3)数据区分度不大的字段不宜使用索引。 24 | 25 | select * from user where sex=1 26 | 27 | 原因:性别只有男,女,每次过滤掉的数据很少,不宜使用索引。 28 | 29 | 经验上,能过滤80%数据时就可以使用索引。对于订单状态,如果状态值很少,不宜使用索引,如果状态值很多,能够过滤大量数据,则应该建立索引。 30 | 31 | (4)在属性上进行计算不能命中索引。 32 | 33 | select * from order where YEAR(date) < = '2020' 34 | 35 | 即使date上建立了索引,也会全表扫描,可优化为值计算: 36 | 37 | select * from order where date < = CURDATE() 38 | 39 | 或者: 40 | 41 | select * from order where date < = '2020-01-01' 42 | 43 | 二、并非周知的SQL实践 44 | 45 | (5)如果业务大部分是单条查询,使用Hash索引性能更好,例如用户中心。 46 | 47 | select * from user where uid=? 48 | 49 | select * from user where login_name=? 50 | 51 | 原因: 52 | 53 | B-Tree索引的时间复杂度是O(log(n)); 54 | 55 | Hash索引的时间复杂度是O(1)。 56 | 57 | (6)允许为null的列,查询有潜在大坑。 58 | 59 | 单列索引不存null值,复合索引不存全为null的值,如果列允许为null,可能会得到“不符合预期”的结果集。 60 | 61 | select * from user where name != 'shenjian' 62 | 63 | 如果name允许为null,索引不存储null值,结果集中不会包含这些记录。 64 | 65 | 所以,请使用not null约束以及默认值。 66 | 67 | (7)复合索引最左前缀,并不是指SQL语句的where顺序要和复合索引一致。 68 | 69 | 用户中心建立了(login_name, passwd)的复合索引 70 | 71 | select * from user where login_name=? and passwd=? 72 | 73 | select * from user where passwd=? and login_name=? 74 | 75 | 都能够命中索引。 76 | 77 | select * from user where login_name=? 78 | 79 | 也能命中索引,满足复合索引最左前缀。 80 | 81 | select * from user where passwd=? 82 | 83 | 不能命中索引,不满足复合索引最左前缀。 84 | 85 | (8)使用ENUM而不是字符串。 86 | 87 | ENUM保存的是TINYINT,别在枚举中搞一些“中国”“北京”“技术部”这样的字符串,字符串空间又大,效率又低。 88 | 89 | 三、小众但有用的SQL实践 90 | 91 | (9)如果明确知道只有一条结果返回,limit 1能够提高效率。 92 | 93 | select * from user where login_name=? 94 | 95 | 可以优化为: 96 | 97 | select * from user where login_name=? limit 1 98 | 99 | 原因: 100 | 101 | 你知道只有一条结果,但数据库并不知道,明确告诉它,让它主动停止游标移动。 102 | 103 | (10)把计算放到业务层而不是数据库层,除了节省数据的CPU,还有意想不到的查询缓存优化效果。 104 | 105 | select * from order where date < = CURDATE() 106 | 107 | 这不是一个好的SQL实践,应该优化为: 108 | 109 | $curDate = date('Y-m-d'); 110 | $res = mysql_query( 111 | 'select * from order where date < = $curDate' 112 | ); 113 | 114 | 画外音:不要问我这是什么语言。 115 | 116 | 原因:释放了数据库的CPU。多次调用,传入的SQL相同,才可以利用查询缓存。 117 | 118 | (11)强制类型转换会全表扫描 119 | 120 | select * from user where phone=13800001234 121 | 122 | 你以为会命中phone索引么?大错特错了!!! 123 | 124 | 末了,再加一条,不要使用select *(潜台词,文章的SQL都不合格 =_=),只返回需要的列,能够大大的节省数据传输量,与数据库的内存使用量哟。 125 | 126 | 以上来源:架构师之路 127 | 128 | -------------------------------------------------------------------------------- /Java/工具/提取Excel文档中的中文/GetChineseByExcel.java: -------------------------------------------------------------------------------- 1 | package com.example.lkp; 2 | 3 | import jxl.Cell; 4 | import jxl.Sheet; 5 | import jxl.Workbook; 6 | 7 | import java.io.*; 8 | import java.util.HashMap; 9 | import java.util.HashSet; 10 | import java.util.Map; 11 | import java.util.Set; 12 | import java.util.regex.Matcher; 13 | import java.util.regex.Pattern; 14 | 15 | /** 16 | * @author: LKP 17 | * @date: 2020/8/11 18 | */ 19 | public class GetChineseByExcel { 20 | 21 | //目标文件 22 | private static final String TARGET_FILE_PATH = "C:\\Users\\Administrator\\Desktop\\解析中文\\中文.txt"; 23 | //源文件 24 | private static final String SOURCE_FILE_PATH = "C:\\Users\\Administrator\\Desktop\\解析中文\\新建文件夹\\"; 25 | //是否允许重复中文 26 | private static final Boolean IS_REPEAT = false; 27 | //是否打印中文所在目录 28 | private static final Boolean IS_PRINT_SOURCE = false; 29 | //全局容器 30 | private static Map map = new HashMap(); 31 | 32 | 33 | public static void main(String[] args) throws IOException { 34 | File sourceFile = new File(SOURCE_FILE_PATH); 35 | scanFile(sourceFile); 36 | print(map); 37 | } 38 | 39 | private static FileWriter fileWriter; 40 | private static PrintWriter printWriter = null; 41 | 42 | static { 43 | try { 44 | fileWriter = new FileWriter(TARGET_FILE_PATH, true); 45 | } catch (IOException e) { 46 | e.printStackTrace(); 47 | } 48 | printWriter = new PrintWriter(fileWriter); 49 | } 50 | 51 | /** 52 | * 输出方法 53 | * 54 | * @param map 55 | */ 56 | private static void print(Map map) { 57 | try { 58 | 59 | } catch (Exception e) { 60 | e.printStackTrace(); 61 | } finally { 62 | printWriter.flush(); 63 | printWriter.close(); 64 | } 65 | 66 | } 67 | 68 | 69 | public static void collect(File file) { 70 | collect(file, IS_REPEAT, IS_PRINT_SOURCE); 71 | } 72 | 73 | 74 | /** 75 | * 将中文收集起来 76 | * Collect. 77 | * 78 | * @param file the file 79 | * @param isRepeat the is repeat 80 | * @param isPrintsource the is printsource 81 | */ 82 | public static void collect(File file, Boolean isRepeat, Boolean isPrintsource) { 83 | String fileName = file.getName(); 84 | try { 85 | printWriter.println("-----------------" + fileName + "-------开始------------"); 86 | FileInputStream fis = new FileInputStream(file); 87 | Workbook rwb = Workbook.getWorkbook(file); 88 | Sheet[] sheet = rwb.getSheets(); 89 | for (int i = 0; i < sheet.length; i++) { 90 | Sheet rs = rwb.getSheet(i); 91 | for (int j = 0; j < rs.getRows(); j++) { 92 | Cell[] cells = rs.getRow(j); 93 | for (int k = 0; k < cells.length; k++) { 94 | filterChineseReturnSet(cells[k].getContents()); 95 | } 96 | } 97 | } 98 | fis.close(); 99 | printWriter.println("-----------------" + fileName + "-------结束------------"); 100 | System.out.println("成功:" + fileName); 101 | } catch (Exception e) { 102 | e.printStackTrace(); 103 | System.out.println(">>>>>>>>>>>>>>>>>>>>>> Excel 名字:" + fileName); 104 | } 105 | } 106 | 107 | //正则表达式中文匹配 108 | private static Set filterChineseReturnSet(String paramValue) throws UnsupportedEncodingException { 109 | String regex = "([\u4e00-\u9fa5]+)"; 110 | Matcher matcher = Pattern.compile(regex).matcher(paramValue); 111 | Set chineseStr = new HashSet<>(10); 112 | while (matcher.find()) { 113 | String str = matcher.group(0); 114 | if (!map.containsKey(str)) { 115 | map.put(str, str); 116 | printWriter.println(str); 117 | } 118 | } 119 | return chineseStr; 120 | } 121 | 122 | 123 | /** 124 | * 递归扫描 125 | * Scan file. 126 | * 127 | * @param file the file 128 | */ 129 | public static void scanFile(File file) { 130 | 131 | if (file.isDirectory()) { 132 | File[] subFiles = file.listFiles(); 133 | if (subFiles.length > 0) { 134 | for (File subFile : subFiles) { 135 | scanFile(subFile); 136 | } 137 | } 138 | } else if (file.isFile()) { 139 | collect(file); 140 | } 141 | 142 | 143 | } 144 | 145 | public static boolean isNotBlank(String str) { 146 | return !isBlank(str); 147 | } 148 | 149 | public static boolean isBlank(String str) { 150 | int strLen; 151 | if (str != null && (strLen = str.length()) != 0) { 152 | for (int i = 0; i < strLen; ++i) { 153 | if (!Character.isWhitespace(str.charAt(i))) { 154 | return false; 155 | } 156 | } 157 | 158 | return true; 159 | } else { 160 | return true; 161 | } 162 | } 163 | 164 | 165 | } 166 | -------------------------------------------------------------------------------- /Java/工具/提取Excel文档中的中文/README.md: -------------------------------------------------------------------------------- 1 | ### 提取Excel文档中的中文 2 | 3 | jxl-2.6.12.jar 是程序所需的jar包,同java源文件一起下载下来,运行前导入到项目中即可 4 | -------------------------------------------------------------------------------- /Java/工具/提取Excel文档中的中文/jxl-2.6.12.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyq946/JavaBible/737e03bfdea2b54d2d733c1e8ba528f256acecda/Java/工具/提取Excel文档中的中文/jxl-2.6.12.jar -------------------------------------------------------------------------------- /Java/工具/提取文件中的中文/GetChinese.java: -------------------------------------------------------------------------------- 1 | package com.example.lkp; 2 | 3 | import java.io.*; 4 | import java.text.MessageFormat; 5 | import java.util.*; 6 | import java.util.regex.Matcher; 7 | import java.util.regex.Pattern; 8 | 9 | /** 10 | * 获取中文 11 | * 12 | * @author: LKP 13 | * @date: 2020/8/10 14 | */ 15 | public class GetChinese { 16 | 17 | /** 18 | * 目标文件 19 | */ 20 | private static final String TARGET_FILE_PATH = "C:\\Users\\Administrator\\Desktop\\解析中文\\中文.txt"; 21 | /** 22 | * 源文件 23 | */ 24 | private static final String SOURCE_FILE_PATH = "C:\\Users\\Administrator\\Desktop\\解析中文\\副本js"; 25 | /** 26 | * 是否允许重复中文 27 | */ 28 | private static final Boolean IS_REPEAT = false; 29 | /** 30 | * 是否打印中文所在目录 31 | */ 32 | private static final Boolean IS_PRINT_SOURCE = false; 33 | /** 34 | * 全局容器 35 | */ 36 | private static Map map = new HashMap<>(); 37 | 38 | public static void main(String[] args) { 39 | File sourceFile = new File(SOURCE_FILE_PATH); 40 | scanFile(sourceFile); 41 | print(map); 42 | } 43 | 44 | /** 45 | * 输出方法 46 | * 47 | * @param map 48 | */ 49 | private static void print(Map map) { 50 | FileWriter fileWriter; 51 | PrintWriter printWriter = null; 52 | try { 53 | fileWriter = new FileWriter(TARGET_FILE_PATH, true); 54 | printWriter = new PrintWriter(fileWriter); 55 | for (Map.Entry entry : map.entrySet()) { 56 | printWriter.println(entry.getKey()); 57 | } 58 | } catch (Exception e) { 59 | e.printStackTrace(); 60 | } finally { 61 | if (printWriter != null) { 62 | printWriter.flush(); 63 | } 64 | if (printWriter != null) { 65 | printWriter.close(); 66 | } 67 | } 68 | } 69 | 70 | public static void collect(File file) { 71 | collect(file, IS_REPEAT, IS_PRINT_SOURCE); 72 | } 73 | 74 | /** 75 | * 将中文收集起来 76 | * Collect. 77 | * 78 | * @param file the file 79 | * @param isRepeat the is repeat 80 | * @param isPrintSource the is print source 81 | */ 82 | public static void collect(File file, Boolean isRepeat, Boolean isPrintSource) { 83 | try { 84 | FileReader fileReader = new FileReader(file); 85 | BufferedReader bufferedReader = new BufferedReader(fileReader); 86 | String content; 87 | String fileName = file.getAbsolutePath().substring(SOURCE_FILE_PATH.length()); 88 | while ((content = bufferedReader.readLine()) != null) { 89 | String result = filterChinese(content); 90 | if (GetChinese.isNotBlank(result)) { 91 | String nameAndPath; 92 | if (isPrintSource) { 93 | nameAndPath = MessageFormat.format("{0}={1}", result, fileName); 94 | } else { 95 | nameAndPath = MessageFormat.format("{0}={1}", result, ""); 96 | } 97 | if (!map.containsKey(nameAndPath)) { 98 | map.put(nameAndPath, nameAndPath); 99 | } 100 | } 101 | } 102 | } catch (Exception e) { 103 | e.printStackTrace(); 104 | } 105 | 106 | } 107 | 108 | /** 109 | * 正则表达式中文匹配 110 | * 111 | * @param paramValue 112 | * @return 113 | */ 114 | private static String filterChinese(String paramValue) { 115 | String regex = "([\u4e00-\u9fa5]+)"; 116 | StringBuffer stringBuffer = new StringBuffer(); 117 | Matcher matcher = Pattern.compile(regex).matcher(paramValue); 118 | while (matcher.find()) { 119 | String str = matcher.group(0); 120 | str = str.replaceAll("\\s+ |“|\\[|‘|《| *|", "").trim(); 121 | stringBuffer.append(str); 122 | } 123 | String content = new String(stringBuffer); 124 | if (GetChinese.isNotBlank(content)) { 125 | return content; 126 | } 127 | return null; 128 | } 129 | 130 | /** 131 | * 递归扫描 132 | * Scan file. 133 | * 134 | * @param file the file 135 | */ 136 | public static void scanFile(File file) { 137 | if (file.isDirectory()) { 138 | File[] subFiles = file.listFiles(); 139 | if (subFiles.length > 0) { 140 | for (File subFile : subFiles) { 141 | scanFile(subFile); 142 | } 143 | } 144 | } else if (file.isFile()) { 145 | collect(file); 146 | } 147 | } 148 | 149 | private static boolean isNotBlank(String str) { 150 | return !isBlank(str); 151 | } 152 | 153 | private static boolean isBlank(String str) { 154 | int strLen; 155 | if (str != null && (strLen = str.length()) != 0) { 156 | for (int i = 0; i < strLen; ++i) { 157 | if (!Character.isWhitespace(str.charAt(i))) { 158 | return false; 159 | } 160 | } 161 | 162 | return true; 163 | } else { 164 | return true; 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /Java/工具/提取文件中的中文/README.md: -------------------------------------------------------------------------------- 1 | ### 提取文件中的中文,直接可以哪来用。 2 | -------------------------------------------------------------------------------- /Java/阅读JDK源码.md: -------------------------------------------------------------------------------- 1 | 为什么要读JDK源码 2 | 3 | 当然不是为了装,毕竟谁没事找事虐自己 ... 4 | 5 | 1、面试跑不掉。现在只要面试Java相关的岗位,肯定或多或少会会涉及JDK源码相关的问题。 6 | 7 | 2、弄懂原理才不慌。我们作为JDK的使用者,虽然说天天用得很开心,但是有时候遇到问题还是得跟到底层源码去看看,才能帮助我们更好的弄懂原理, 8 | 9 | 3、学习优秀的代码、思想和模式。JDK毕竟是一个优秀的代码库,我们天天用,源码也就在里面,作为一个有志向的程序员,读一读源码也能让我们吸取到更多优秀的思想和模式。 10 | 11 | 4、睡前催眠。额 …… 不过的确有效(滑稽)。 12 | 13 | 源码难吗? 14 | 15 | 废话,当然有难度啦,不然我也不会到现在都还没看完,而且看了也经常忘,哭唧唧... 16 | 17 | 毕竟像JDK这种源码,和我们平常练手写小例子、写业务代码不一样,人家毕竟是 类库,为了性能、稳定性、通用性,扩展性等因素考虑,加入了很多辅助代码、泛型、以及一些设计模式上的考量,所以看起来肯定没有那么轻松,没办法一眼看穿它。 18 | 19 | 所以这玩意儿肯定是一个长期的过程,我个人建议(包括我自己也是这样),有时候遇到一些问题,可以针对性地把某些组件或者某个部分的源码,跟到底层去看看,然后做点笔记,写点注释啥的,这样慢慢就能渗透到很多的内容了。 20 | 21 | 但是我们一定要有足够的信心,我坚信代码人家都写出来了,我就不信我看不懂! 22 | 23 | 源码该怎么看 24 | 25 | 1、方法一:按需阅读。如果对某个组件、语法或者特性感兴趣,或者遇到什么问题疑惑,可以有针对性地跟到底层源码按需查看,这也是一种比较高效,能快速树立信心的阅读方式。 26 | 27 | 2、方法二:系统化阅读。具体阅读内容和顺序建议下文详述。 28 | 29 | 3、多调试:如果仅仅靠眼睛看,然后脑补画面调试还是比较吃力的,最好还是借助IDE动手调试起来,走两步就知道了。 30 | 31 | 4、别光读,记得读完留下点什么。我觉得看了多少不重要,重要的是能输出多少,多总结、归纳,写注释,记笔记 32 | 33 | 所以下文准备搭建一个Java源码的阅读和调试环境,建议人手一个,每当心血来潮时、遇到问题时、碰到疑惑时、闲得无聊时都可以打开工程看一看源码,做做笔记和注释。 34 | 搭建源码阅读调试环境 35 | 我个人觉得看源码这个事情还是应该单独搞一个Java工程,源码放里面,测试代码也放里面,集中调试,集中看代码,集中写注释比较方便一些。 36 | 37 | 1、创建源码阅读项目 38 | 39 | 选择最普通的Java基础项目即可: 40 | 41 | ![图片1](https://mmbiz.qpic.cn/mmbiz_png/xq9PqibkVAzo7kPxibSNTFvENnsVHicic8GLbIempkFwZJ5HyWjLICzqcFzHpUF2VJAib0u8hcLSfzUB7RrzuPIWTUQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 42 | 43 | 2、创建两个目录 44 | 45 | 分别为: 46 | 47 | ![图片2](https://mmbiz.qpic.cn/mmbiz_png/xq9PqibkVAzo7kPxibSNTFvENnsVHicic8GLMNK24iclt4jQHPw5AHILdcncc3XJCaxNH2reOIOQ89HfZzNiaXWTRb4w/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 48 | 49 | source:稍后放置JDK源码进去 50 | test:放置测试代码,里面还可以按需要建立层级子目录 51 | 52 | 3、导入JDK源码 53 | 54 | 有很多小伙伴问JDK的源码在哪里呢? 55 | 56 | 远在天边,仅在眼前,其实在的JDK安装目录下就能找到。 57 | 58 | JDK安装目录下有一个名为src.zip压缩包,这正是JDK源码! 59 | 60 | ![图片3](https://mmbiz.qpic.cn/mmbiz_png/xq9PqibkVAzo7kPxibSNTFvENnsVHicic8GLg9LdibiaqGbBiaNbTvACmicYKrhGq8oYqIa3pTmZZBNnibfEibnlt7CWwIcQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 61 | 62 | 将其解压后拷贝到上面项目的source目录下,这样JDK源码就导入好了。 63 | 64 | ![图片4](https://mmbiz.qpic.cn/mmbiz_png/xq9PqibkVAzo7kPxibSNTFvENnsVHicic8GL3N9qDS4WmfBDe4yLicU9pGw7JxickhNmtWiaxFZwic7aIDB4Q8IRzGLf4g/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 65 | 66 | 有些小伙伴会有疑问,为什么要将JDK源码导一份放到这个项目里?其实主要原因还是方便我们在源码里阅读、调试、以及做笔记和注释。 67 | 68 | 至于这份JDK源码怎么用上,下文继续阐述。 69 | 70 | 4、调试并运行 71 | 72 | 我们可以在test目录里去随意编写一段测试代码。 73 | 74 | 比如我这里就以HashMap为例,在test目录下创建一个子目录hashmap,然后在里面创建一个测试主入口文件Test.java,随意放上一段测试代码: 75 | 76 | ``` java 77 | public static void main( String[] args ) { 78 | 79 | 80 | Map hashMap = new HashMap<>(); 81 | 82 | hashMap.put( "k1", 0.1 ); 83 | hashMap.put( "k2", 0.2 ); 84 | hashMap.put( "k3", 0.3 ); 85 | hashMap.put( "k4", 0.4 ); 86 | 87 | for ( Map.Entry entry : hashMap.entrySet() ) { 88 | System.out.println( entry.getKey() +":" + entry.getValue()); 89 | } 90 | 91 | } 92 | ``` 93 | 然后启动调试即可。 94 | 95 | 不过接下来会有几个问题需要一一去解决。 96 | 97 | 问题一:启动调试时Build报错,提示系统资源不足 98 | 99 | ![图片5](https://mmbiz.qpic.cn/mmbiz_png/xq9PqibkVAzo7kPxibSNTFvENnsVHicic8GL4PQnPYm6aSCc5WOiaN6SOuOkc4PibyUGVmfiaVELrkZNQdMY4oPBF82GQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 100 | 101 | 解决方法: 加大Build process heap size。 102 | 103 | 设置方法:Preferences --> Build,Execution,Deployment --> Compiler,将默认700的数值加大,比如我这里设置为1700: 104 | 105 | ![图片6](https://mmbiz.qpic.cn/mmbiz_png/xq9PqibkVAzo7kPxibSNTFvENnsVHicic8GLlz1uZyjCKJChpbh88w5W0esric66PcDBDja279XKGvPjkZpYAX6JKlw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 106 | 107 | 问题二:想从外层代码F7单步调试进入JDK源码内部,结果发现进不去 108 | 109 | 这是因为调试时,JDK源码受保护,一般单步调试不让进,但是可以设置。 110 | 111 | 解决方法:: 112 | 113 | Preferences --> Build,Execution,Deployment --> Debugger --> Stepping 114 | 115 | ![图片7](https://mmbiz.qpic.cn/mmbiz_png/xq9PqibkVAzo7kPxibSNTFvENnsVHicic8GLctOS0sOsdETgaQ1G1R5NUb5hgeK2akXsAmUib3MzRM315ONqnVJia1zg/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 116 | 117 | 问题三:如何对JDK源码做注释? 118 | 119 | 调试进入JDK源码以后,发现不能进行注释,每个文件上都有一个小锁的图标,这是因为现在关联的源码并不是我们项目里刚拷进去的源码,而是JDK安装目录下的src.zip只读压缩包。 120 | 121 | 解决办法: 重新关联JDK源码路径为本项目路径下的这一份JDK源码。 122 | 123 | ![图片8](https://mmbiz.qpic.cn/mmbiz_png/xq9PqibkVAzo7kPxibSNTFvENnsVHicic8GLqvFY7DeZhQhhicj1HnqiaPlNx4WIjWbZxAukTjPzvI8VbicUA2kN2Sx7g/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 124 | 125 | 这样一来,我们就可以愉快地对JDK源码调试以及做注释了。 126 | 127 | 源码结构和阅读顺序 128 | JDK源码毕竟太庞大了,所有都看不太现实,我们还是愿意根据日常使用和面试考察的频繁度来挖取重要的内容先看一看。 129 | 130 | 如果自己没有特别的规划,可以按照如下所示的建议阅读顺序往下进行: 131 | 132 | ![图片9](https://mmbiz.qpic.cn/mmbiz_png/xq9PqibkVAzo7kPxibSNTFvENnsVHicic8GL6cf9rhibvKXVAkvBGJEenticDfYfQKkyVh6W7nkw1bsu4kpxIAdkXMbw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 133 | 134 | 具体的内容简介如下: 135 | 136 | 1、java.lang 137 | 138 | 这里面其实就是Java的基本语法,比如各种基本包装类型(Integer、Long、Double等)、基本类(Object,Class,Enum,Exception,Thread)等等... 139 | 140 | 2、java.lang.annotation 141 | 142 | 包含Java注解基本元素相关的源码 143 | 144 | 3、java.lang.reflect 145 | 146 | 包含Java反射基本元素相关的代码 147 | 148 | 4、java.util 149 | 150 | 这里面放的都是Java的基本工具,最典型和常用的就是各种容器和集合(List、Map、Set) 151 | 152 | 5、java.util.concurrent 153 | 154 | 大名鼎鼎的JUC包,里面包含了Java并发和多线程编程相关的代码 155 | 156 | 6、java.util.function +java.util.stream 157 | 158 | 包含Java函数式编程的常见接口和代码 159 | 160 | 7、java.io 161 | 162 | 包含Java传统I/O相关的源码,主要是面向字节和流的I/O 163 | 164 | 8、java.nio 165 | 166 | 包含Java非阻塞I/O相关的源码,主要是面向缓冲、通道以及选择器的I/O 167 | 168 | 9、java.time 169 | 170 | 包含Java新日期和期间相关的代码,最典型的当属LocalDateTime、DateTimeFormatter等 171 | 172 | 10、java.math 173 | 174 | 主要包含一些高精度运算的支持数据类 175 | 176 | 11、java.math 177 | 178 | 主要包含一些高精度运算的支持数据类 179 | 180 | 12、java.net 181 | 182 | 主要包含Java网络通信(典型的如:Socket通信)相关的源代码。 183 | 184 | 共勉 185 | 看源码这东西不能急,慢一点才能更快!共勉。 186 | 187 | 每天进步一点点,Peace! 188 | 189 | 190 | 来源: CodeSheep 191 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JavaBible 2 | 3 | 【Java学习+面试宝典】 一份涵盖大部分Java程序员所需要掌握的核心知识。 4 | 5 | # 目录 6 | 7 | + Java 8 | + 面试题 9 | + 文章推荐 10 | + 设计模式 11 | + 学习书籍 12 | 13 | # Java

14 | 15 | + HTTP API 认证授有哪些方法? 16 | + Java 技术栈中的技术点缩写 17 | 18 | # 面试题

19 | 20 | + 【美团】Java 岗 154 道面试题(2.0版) 21 | 22 | + 100 期 Java 面试题汇总 23 | 24 | # 文章推荐

25 | 26 | + Java后端优质文章推荐 27 | 28 | # 设计模式

29 | 30 | + 从零开始单排学设计模式「UML类图」定级赛 31 | + 从零开始单排学设计模式「简单工厂设计模式」黑铁 III 32 | + 从零开始单排学设计模式「策略模式」黑铁 II 33 | + 从零开始单排学设计模式「装饰模式」黑铁 I 34 | + 从零开始单排学设计模式「单一职责原则」黑铁 - 青铜 晋级赛 35 | 36 | # 学习书籍

37 | 38 | + Java 技术书籍大全 39 | 40 | 41 | -------------------------------------------------------------------------------- /images/Java开发者职业生涯要看的200+本书籍.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyq946/JavaBible/737e03bfdea2b54d2d733c1e8ba528f256acecda/images/Java开发者职业生涯要看的200+本书籍.jpg -------------------------------------------------------------------------------- /images/README.md: -------------------------------------------------------------------------------- 1 | 存储图片 2 | -------------------------------------------------------------------------------- /images/java_阅读JDK源码/640 (2).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyq946/JavaBible/737e03bfdea2b54d2d733c1e8ba528f256acecda/images/java_阅读JDK源码/640 (2).png -------------------------------------------------------------------------------- /images/java_阅读JDK源码/640 (3).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyq946/JavaBible/737e03bfdea2b54d2d733c1e8ba528f256acecda/images/java_阅读JDK源码/640 (3).png -------------------------------------------------------------------------------- /images/java_阅读JDK源码/640 (4).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyq946/JavaBible/737e03bfdea2b54d2d733c1e8ba528f256acecda/images/java_阅读JDK源码/640 (4).png -------------------------------------------------------------------------------- /images/java_阅读JDK源码/640 (5).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyq946/JavaBible/737e03bfdea2b54d2d733c1e8ba528f256acecda/images/java_阅读JDK源码/640 (5).png -------------------------------------------------------------------------------- /images/java_阅读JDK源码/640 (6).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyq946/JavaBible/737e03bfdea2b54d2d733c1e8ba528f256acecda/images/java_阅读JDK源码/640 (6).png -------------------------------------------------------------------------------- /images/java_阅读JDK源码/640 (7).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyq946/JavaBible/737e03bfdea2b54d2d733c1e8ba528f256acecda/images/java_阅读JDK源码/640 (7).png -------------------------------------------------------------------------------- /images/java_阅读JDK源码/640 (8).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyq946/JavaBible/737e03bfdea2b54d2d733c1e8ba528f256acecda/images/java_阅读JDK源码/640 (8).png -------------------------------------------------------------------------------- /images/java_阅读JDK源码/640 (9).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyq946/JavaBible/737e03bfdea2b54d2d733c1e8ba528f256acecda/images/java_阅读JDK源码/640 (9).png -------------------------------------------------------------------------------- /images/java_阅读JDK源码/640.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyq946/JavaBible/737e03bfdea2b54d2d733c1e8ba528f256acecda/images/java_阅读JDK源码/640.png -------------------------------------------------------------------------------- /images/java_阅读JDK源码/README.md: -------------------------------------------------------------------------------- 1 | Java目录下【阅读JDK源码.md】文件图片 2 | -------------------------------------------------------------------------------- /手写实现/JDK 动态代理/Customer.java: -------------------------------------------------------------------------------- 1 | package com.lkp.demo.test.two.five.three.gp; 2 | 3 | 4 | /** 5 | * @author: LKP 6 | * @date: 2020/3/11 7 | */ 8 | public class Customer implements Person { 9 | 10 | @Override 11 | public void findLove() { 12 | System.out.println("高富帅"); 13 | System.out.println("身高180cm"); 14 | System.out.println("有6块腹肌"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /手写实现/JDK 动态代理/GPClassLoader.java: -------------------------------------------------------------------------------- 1 | package com.lkp.demo.test.two.five.three.gp; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | 7 | /** 8 | * @author: LKP 9 | * @date: 2020/3/11 10 | */ 11 | public class GPClassLoader extends ClassLoader { 12 | 13 | private File classPathFile; 14 | 15 | public GPClassLoader(){ 16 | String classPath = GPClassLoader.class.getResource("").getPath(); 17 | this.classPathFile = new File(classPath); 18 | } 19 | 20 | @Override 21 | protected Class findClass(String name){ 22 | String className = GPClassLoader.class.getPackage().getName() + "." + name; 23 | 24 | if(classPathFile != null){ 25 | File classFile = new File(classPathFile, name.replace("\\.", "/") + ".class"); 26 | if(classFile.exists()){ 27 | FileInputStream in = null; 28 | ByteArrayOutputStream out = null; 29 | 30 | try { 31 | in = new FileInputStream(classFile); 32 | out = new ByteArrayOutputStream(); 33 | byte[] buff = new byte[1024]; 34 | int len; 35 | while((len = in.read(buff)) != -1){ 36 | out.write(buff, 0, len); 37 | } 38 | return defineClass(className, out.toByteArray(), 0, out.size()); 39 | } catch (Exception e) { 40 | e.printStackTrace(); 41 | } finally { 42 | if(null != in){ 43 | try { 44 | in.close(); 45 | } catch (Exception e) { 46 | e.printStackTrace(); 47 | } 48 | } 49 | if(null != out){ 50 | try { 51 | out.close(); 52 | } catch (Exception e) { 53 | e.printStackTrace(); 54 | } 55 | } 56 | } 57 | } 58 | } 59 | return null; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /手写实现/JDK 动态代理/GPInvocationHandler.java: -------------------------------------------------------------------------------- 1 | package com.lkp.demo.test.two.five.three.gp; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | /** 6 | * @author: LKP 7 | * @date: 2020/3/11 8 | */ 9 | public interface GPInvocationHandler { 10 | 11 | Object invoke(Object proxy, Method method, Object[] args) throws Throwable; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /手写实现/JDK 动态代理/GPMeipo.java: -------------------------------------------------------------------------------- 1 | package com.lkp.demo.test.two.five.three.gp; 2 | 3 | import java.lang.reflect.Method; 4 | import java.lang.reflect.Proxy; 5 | 6 | /** 7 | * @author: LKP 8 | * @date: 2020/3/11 9 | */ 10 | public class GPMeipo implements GPInvocationHandler { 11 | 12 | /** 13 | * 被代理的对象 14 | */ 15 | private Object target; 16 | 17 | public Object getInstance(Object target){ 18 | this.target = target; 19 | 20 | Class clazz = target.getClass(); 21 | return GPProxy.newProxyInstance(new GPClassLoader(), clazz.getInterfaces(), this); 22 | } 23 | 24 | @Override 25 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 26 | before(); 27 | Object obj = method.invoke(target, args); 28 | after(); 29 | return obj; 30 | } 31 | 32 | public void before(){ 33 | System.out.println("我是媒婆,我要给你找对象,现在已经确定你的需求"); 34 | System.out.println("开始物色"); 35 | } 36 | 37 | public void after(){ 38 | System.out.println("如果合适的话,就准备办事"); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /手写实现/JDK 动态代理/GPProxy.java: -------------------------------------------------------------------------------- 1 | package com.lkp.demo.test.two.five.three.gp; 2 | 3 | import javax.tools.JavaCompiler; 4 | import javax.tools.StandardJavaFileManager; 5 | import javax.tools.ToolProvider; 6 | import java.io.File; 7 | import java.io.FileWriter; 8 | import java.lang.reflect.Constructor; 9 | import java.lang.reflect.Method; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | /** 14 | * 用来生成原代码的工具类 15 | * 16 | * @author: LKP 17 | * @date: 2020/3/11 18 | */ 19 | public class GPProxy { 20 | 21 | public static final String ln = "\r\n"; 22 | 23 | public static Object newProxyInstance(GPClassLoader classLoader, Class[] interfaces 24 | , GPInvocationHandler invocationHandler) { 25 | try { 26 | // 动态生成源代码.java文件 27 | String src = generateSrc(interfaces); 28 | 29 | // java文件输出磁盘 30 | String filePath = GPProxy.class.getResource("").getPath(); 31 | File file = new File(filePath + "$Proxy0.java"); 32 | FileWriter fw = new FileWriter(file); 33 | fw.write(src); 34 | fw.flush(); 35 | fw.close(); 36 | 37 | // 把生成的.java文件编译成.class文件 38 | JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 39 | StandardJavaFileManager manage = compiler.getStandardFileManager(null 40 | , null, null); 41 | Iterable iterable = manage.getJavaFileObjects(file); 42 | 43 | JavaCompiler.CompilationTask task = compiler.getTask(null, manage, null 44 | , null ,null, iterable); 45 | task.call(); 46 | manage.close(); 47 | 48 | // 把编译生成的.class文件加载到JVM中 49 | Class proxyClass = classLoader.findClass("$Proxy0"); 50 | Constructor constructor = proxyClass.getConstructor(GPInvocationHandler.class); 51 | file.delete(); 52 | 53 | // 返回字节码重组以后的新的代理对象 54 | return constructor.newInstance(invocationHandler); 55 | } catch (Exception e) { 56 | e.printStackTrace(); 57 | } 58 | return null; 59 | } 60 | 61 | private static String generateSrc(Class[] interfaces) { 62 | StringBuilder sb = new StringBuilder(); 63 | sb.append("package com.lkp.demo.test.two.five.three.gp;" + ln); 64 | sb.append("import com.lkp.demo.test.two.five.three.two.macth.maker.Person;" + ln); 65 | sb.append("import java.lang.reflect.*;" + ln); 66 | sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln); 67 | sb.append("GPInvocationHandler invocationHandler;" + ln); 68 | sb.append("public $Proxy0(GPInvocationHandler invocationHandler){ " + ln); 69 | sb.append("this.invocationHandler = invocationHandler;" + ln); 70 | sb.append("}" + ln); 71 | for (Method method : interfaces[0].getMethods()) { 72 | Class[] params = method.getParameterTypes(); 73 | 74 | StringBuilder paramNames = new StringBuilder(); 75 | StringBuilder paramValues = new StringBuilder(); 76 | StringBuilder paramClasses = new StringBuilder(); 77 | 78 | for (int i = 0; i < params.length; i++) { 79 | Class clazz = params[i]; 80 | String type = clazz.getName(); 81 | String paramName = toLowerFirstCase(clazz.getSimpleName()); 82 | paramNames.append(type + " " + paramName); 83 | paramValues.append(paramName); 84 | paramClasses.append(clazz.getName() + ".class"); 85 | 86 | if(i > 0 && i < params.length - 1){ 87 | paramNames.append(","); 88 | paramValues.append(","); 89 | paramClasses.append(","); 90 | } 91 | } 92 | 93 | sb.append("public " + method.getReturnType().getName() + " " + method.getName() 94 | + "(" + paramNames.toString() + ") {" + ln); 95 | sb.append("try{"+ln); 96 | sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" 97 | + method.getName() + "\", new Class[]{" + paramClasses.toString() + "});" + ln); 98 | sb.append((hasReturnValue(method.getReturnType()) ? "return " : "") + 99 | getCaseCode("this.invocationHandler.invoke(this, m, new Object[]{" 100 | + paramValues +"})", method.getReturnType()) + ";" + ln); 101 | sb.append("}catch(Error _ex){ }"); 102 | sb.append("catch(Throwable e){" + ln); 103 | sb.append("throw new UndeclaredThrowableException(e);" + ln); 104 | sb.append("}"); 105 | sb.append(getReturnEmptyCode(method.getReturnType())); 106 | sb.append("}"); 107 | } 108 | sb.append("}"+ln); 109 | return sb.toString(); 110 | } 111 | 112 | private static Map mappings = new HashMap<>(); 113 | 114 | static { 115 | mappings.put(int.class, Integer.class); 116 | } 117 | 118 | private static String getReturnEmptyCode(Class returnClass){ 119 | if(mappings.containsKey(returnClass)){ 120 | return "return 0;"; 121 | }else if(returnClass == void.class) { 122 | return ""; 123 | }else{ 124 | return "return null;"; 125 | } 126 | } 127 | 128 | private static String getCaseCode(String code, Class returnClass){ 129 | if(mappings.containsKey(returnClass)){ 130 | return "((" + mappings.get(returnClass).getName() + ")" + code + ")." 131 | + returnClass.getSimpleName() + "Value()"; 132 | } 133 | return code; 134 | } 135 | 136 | private static boolean hasReturnValue(Class clazz){ 137 | return clazz != void.class; 138 | } 139 | 140 | private static String toLowerFirstCase(String src){ 141 | char[] chars = src.toCharArray(); 142 | chars[0] += 32; 143 | return String.valueOf(chars); 144 | 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /手写实现/JDK 动态代理/Person.java: -------------------------------------------------------------------------------- 1 | package com.lkp.demo.test.two.five.three.gp; 2 | 3 | /** 4 | * @author: LKP 5 | * @date: 2020/3/11 6 | */ 7 | public interface Person { 8 | 9 | void findLove(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /手写实现/JDK 动态代理/README.md: -------------------------------------------------------------------------------- 1 | 提示:将java类文件放置到com.lkp.demo.test.two.five.three.gp;包下面 2 | 3 | ### JDK 动态代理是怎么进行的? 4 | 5 | JDK 动态代理采用字节重组,重新生成对象来代替原始对象,以达到动态地代替的目的。 6 | 7 | ### JDK 动态代理生成对象步骤: 8 | 1) 获取被代理对象的引用,并且获取它的所有接口,反射获取。 9 | 2) JDK动态代理类重新生成一个新的类,同时新的类要实现被代理类实现的所有接口。 10 | 3) 动态生成Java代码,新加的业务逻辑方法由一定的逻辑代码调用(在代码中体现) 11 | 4) 编译新生成的Java代码.class文件。 12 | 5) 重新加载到JVM中运行。 13 | 14 | ### 生成的 $Proxy0.class 代码 15 | 16 | ```java 17 | package com.lkp.demo.test.two.five.three.gp; 18 | 19 | import com.lkp.demo.test.two.five.three.two.macth.maker.Person; 20 | import java.lang.reflect.Method; 21 | import java.lang.reflect.UndeclaredThrowableException; 22 | 23 | public class $Proxy0 implements Person { 24 | GPInvocationHandler invocationHandler; 25 | 26 | public $Proxy0(GPInvocationHandler var1) { 27 | this.invocationHandler = var1; 28 | } 29 | 30 | public void findLove() { 31 | try { 32 | Method var1 = Person.class.getMethod("findLove"); 33 | this.invocationHandler.invoke(this, var1, new Object[0]); 34 | } catch (Error var2) { 35 | ; 36 | } catch (Throwable var3) { 37 | throw new UndeclaredThrowableException(var3); 38 | } 39 | 40 | } 41 | } 42 | ``` 43 | -------------------------------------------------------------------------------- /手写实现/JDK 动态代理/Test.java: -------------------------------------------------------------------------------- 1 | package com.lkp.demo.test.two.five.three.gp; 2 | 3 | /** 4 | * @author: LKP 5 | * @date: 2020/3/11 6 | */ 7 | public class Test { 8 | 9 | public static void main(String[] args) { 10 | try { 11 | Person obj = (Person) new GPMeipo().getInstance(new Customer()); 12 | System.out.println(obj.getClass()); 13 | obj.findLove(); 14 | } catch (Exception e) { 15 | e.printStackTrace(); 16 | } 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /手写实现/Spring MVC/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /手写实现/简易版 tomcat/FindGirlServlet.java: -------------------------------------------------------------------------------- 1 | package com.lkp.demo.tomcat; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * @author: LKP 7 | * @date: 2020/4/18 8 | */ 9 | public class FindGirlServlet extends MyServlet { 10 | 11 | @Override 12 | public void doGet(MyRequest request, MyResponse response) { 13 | try { 14 | response.write("get girl..."); 15 | } catch (IOException e) { 16 | e.printStackTrace(); 17 | } 18 | } 19 | 20 | @Override 21 | public void doPost(MyRequest request, MyResponse response) { 22 | try { 23 | response.write("post girl..."); 24 | } catch (IOException e) { 25 | e.printStackTrace(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /手写实现/简易版 tomcat/HelloWorldServlet.java: -------------------------------------------------------------------------------- 1 | package com.lkp.demo.tomcat; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * @author: LKP 7 | * @date: 2020/4/18 8 | */ 9 | public class HelloWorldServlet extends MyServlet { 10 | 11 | @Override 12 | public void doGet(MyRequest request, MyResponse response) { 13 | try { 14 | response.write("get world..."); 15 | } catch (IOException e) { 16 | e.printStackTrace(); 17 | } 18 | } 19 | 20 | @Override 21 | public void doPost(MyRequest request, MyResponse response) { 22 | try { 23 | response.write("post world..."); 24 | } catch (IOException e) { 25 | e.printStackTrace(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /手写实现/简易版 tomcat/MyRequest.java: -------------------------------------------------------------------------------- 1 | package com.lkp.demo.tomcat; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | 6 | /** 7 | * @author: LKP 8 | * @date: 2020/4/18 9 | */ 10 | public class MyRequest { 11 | 12 | private String url; 13 | private String method; 14 | 15 | public MyRequest(InputStream inputStream) throws IOException { 16 | String httpRequest = ""; 17 | byte[] httpRequestBytes = new byte[1024]; 18 | int length = 0; 19 | if((length = inputStream.read(httpRequestBytes)) > 0){ 20 | httpRequest = new String(httpRequestBytes, 0, length); 21 | } 22 | 23 | // HTTP 请求协议 24 | // GET /favicon.ico HTTP/1.1 25 | // Accept: */* 26 | // Accept-Encoding: gzip, deflate 27 | // User-Agent: Mozilla/5.0(Windows NT 6.1; WOW64; Trident/7.0; rv:11.0)like Gecko 28 | // Host: localhost:8080 29 | // Connection: Keep-Alive 30 | String httpHead = httpRequest.split("\n")[0]; 31 | System.out.println("httpHead:"+httpHead); 32 | url = httpHead.split("\\s")[1]; 33 | method = httpHead.split("\\s")[0]; 34 | System.out.println(this); 35 | } 36 | 37 | public String getMethod(){ 38 | return method; 39 | } 40 | 41 | public String getUrl() { 42 | return url; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /手写实现/简易版 tomcat/MyResponse.java: -------------------------------------------------------------------------------- 1 | package com.lkp.demo.tomcat; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | 6 | /** 7 | * @author: LKP 8 | * @date: 2020/4/18 9 | */ 10 | public class MyResponse { 11 | 12 | private OutputStream outputStream; 13 | 14 | public MyResponse(OutputStream outputStream) { 15 | this.outputStream = outputStream; 16 | } 17 | 18 | public void write(String content) throws IOException { 19 | // HTTP 相应协议 20 | // HTTP/1.1 200 OK 21 | // Content-Type: text/html 22 | // 23 | 24 | StringBuffer httpResponse = new StringBuffer(); 25 | httpResponse.append("HTTP/1.1 200 OK\n") 26 | .append("Content-type: text/html\n") 27 | .append("\r\n") 28 | .append("") 29 | .append(content) 30 | .append(""); 31 | 32 | outputStream.write(httpResponse.toString().getBytes()); 33 | outputStream.close(); 34 | } 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /手写实现/简易版 tomcat/MyServlet.java: -------------------------------------------------------------------------------- 1 | package com.lkp.demo.tomcat; 2 | 3 | import org.omg.CORBA.RepositoryIdHelper; 4 | 5 | import java.lang.annotation.Repeatable; 6 | 7 | /** 8 | * @author: LKP 9 | * @date: 2020/4/18 10 | */ 11 | public abstract class MyServlet { 12 | 13 | public abstract void doGet(MyRequest request, MyResponse response); 14 | 15 | public abstract void doPost(MyRequest request, MyResponse response); 16 | 17 | private static final String METHOD_POST = "POST"; 18 | private static final String METHOD_GET = "GET"; 19 | 20 | public void service(MyRequest request, MyResponse response){ 21 | String method = request.getMethod(); 22 | if(METHOD_POST.equalsIgnoreCase(method)){ 23 | doPost(request, response); 24 | }else if(METHOD_GET.equalsIgnoreCase(method)){ 25 | doGet(request, response); 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /手写实现/简易版 tomcat/MyTomcat.java: -------------------------------------------------------------------------------- 1 | package com.lkp.demo.tomcat; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.net.ServerSocket; 7 | import java.net.Socket; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | /** 12 | * @author: LKP 13 | * @date: 2020/4/18 14 | */ 15 | public class MyTomcat { 16 | 17 | private int port = 8080; 18 | 19 | private Map urlServletMap = new HashMap(); 20 | 21 | public MyTomcat(int port) { 22 | this.port = port; 23 | } 24 | 25 | public void start(){ 26 | // 初始化 URL 与对应处理的 servlet 的关系 27 | initServletMapping(); 28 | 29 | ServerSocket serverSocket = null; 30 | try { 31 | serverSocket = new ServerSocket(port); 32 | System.out.println("MyTomcat is start..."); 33 | 34 | while(true){ 35 | Socket socket = serverSocket.accept(); 36 | InputStream inputStream = socket.getInputStream(); 37 | OutputStream outputStream = socket.getOutputStream(); 38 | 39 | MyRequest request = new MyRequest(inputStream); 40 | MyResponse response = new MyResponse(outputStream); 41 | 42 | // 请求分发 43 | dispatch(request, response); 44 | 45 | socket.close(); 46 | } 47 | 48 | } catch (Exception e) { 49 | e.printStackTrace(); 50 | } finally { 51 | if(serverSocket != null){ 52 | try { 53 | serverSocket.close(); 54 | } catch (IOException e) { 55 | e.printStackTrace(); 56 | } 57 | } 58 | } 59 | } 60 | 61 | /** 62 | * 初始化servlet映射 63 | */ 64 | private void initServletMapping(){ 65 | for (ServletMapping servletMapping : ServletMappingConfig.servletMappingList) { 66 | urlServletMap.put(servletMapping.getUrl(), servletMapping.getClazz()); 67 | } 68 | } 69 | 70 | /** 71 | * 请求分发 72 | * @param request 73 | * @param response 74 | */ 75 | private void dispatch(MyRequest request, MyResponse response){ 76 | String clazz = urlServletMap.get(request.getUrl()); 77 | if(clazz == null){ 78 | return; 79 | } 80 | // 反射 81 | try { 82 | Class myServletClass = (Class) Class.forName(clazz); 83 | MyServlet myServlet = myServletClass.newInstance(); 84 | myServlet.service(request, response); 85 | } catch (Exception e) { 86 | e.printStackTrace(); 87 | } 88 | } 89 | 90 | public static void main(String[] args){ 91 | MyTomcat tomcat = new MyTomcat(8080); 92 | tomcat.start(); 93 | } 94 | } 95 | 96 | -------------------------------------------------------------------------------- /手写实现/简易版 tomcat/README.md: -------------------------------------------------------------------------------- 1 | 简单实现了 tomcat 的核心原理。 2 | -------------------------------------------------------------------------------- /手写实现/简易版 tomcat/ServletMapping.java: -------------------------------------------------------------------------------- 1 | package com.lkp.demo.tomcat; 2 | 3 | /** 4 | * @author: LKP 5 | * @date: 2020/4/18 6 | */ 7 | public class ServletMapping { 8 | 9 | private String servletName; 10 | private String url; 11 | private String clazz; 12 | 13 | public ServletMapping(String servletName, String url, String clazz) { 14 | this.servletName = servletName; 15 | this.url = url; 16 | this.clazz = clazz; 17 | } 18 | 19 | public String getServletName() { 20 | return servletName; 21 | } 22 | 23 | public void setServletName(String servletName) { 24 | this.servletName = servletName; 25 | } 26 | 27 | public String getUrl() { 28 | return url; 29 | } 30 | 31 | public void setUrl(String url) { 32 | this.url = url; 33 | } 34 | 35 | public String getClazz() { 36 | return clazz; 37 | } 38 | 39 | public void setClazz(String clazz) { 40 | this.clazz = clazz; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /手写实现/简易版 tomcat/ServletMappingConfig.java: -------------------------------------------------------------------------------- 1 | package com.lkp.demo.tomcat; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * @author: LKP 8 | * @date: 2020/4/18 9 | */ 10 | public class ServletMappingConfig { 11 | 12 | public static List servletMappingList = new ArrayList<>(5); 13 | 14 | static{ 15 | servletMappingList.add(new ServletMapping("findGirlServlet", "/girl", "com.lkp.demo.tomcat.FindGirlServlet")); 16 | servletMappingList.add(new ServletMapping("helloWorldServlet", "/world", "com.lkp.demo.tomcat" + 17 | ".HelloWorldServlet")); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /文章推荐/Java后端优质文章推荐.md: -------------------------------------------------------------------------------- 1 | ## Java 后端 2 | 3 | ### JDK 4 | 5 | + [JDK1.8 新特性(全)](https://blog.csdn.net/qq_29411737/article/details/80835658) 6 | 7 | ### 多线程 8 | 9 | + [Java多线程-线程池ThreadPoolExecutor构造方法和规则](https://blog.csdn.net/qq_25806863/article/details/71126867) 10 | 11 | ### 数据结构 12 | 13 | + [红黑树 Java实现](https://www.cnblogs.com/zedosu/p/6635034.html) 14 | 15 | ### 锁 16 | 17 | + [轻松学习java可重入锁(ReentrantLock)的实现原理](https://blog.csdn.net/yanyan19880509/article/details/52345422/) 18 | -------------------------------------------------------------------------------- /面试题/100 期 Java 面试题汇总.md: -------------------------------------------------------------------------------- 1 | ## 1-10期 2 | 3 | 【01期】Spring,SpringMVC,SpringBoot,SpringCloud有什么区别和联系? 4 | 5 | 【02期】你能说说Spring框架中Bean的生命周期吗? 6 | 7 | 【03期】如何决定使用 HashMap 还是 TreeMap? 8 | 9 | 【04期】分库分表之后,id 主键如何处理? 10 | 11 | 【05期】消息队列中,如何保证消息的顺序性? 12 | 13 | 【06期】单例模式有几种写法? 14 | 15 | 【07期】Redis中是如何实现分布式锁的? 16 | 17 | 【08期】说说Object类下面有几种方法呢? 18 | 19 | 【09期】说说hashCode() 和 equals() 之间的关系? 20 | 21 | 【10期】Redis 面试常见问答 22 | 23 | ## 11-20期 24 | 25 | 【11期】分布式系统接口,如何避免表单的重复提交? 26 | 27 | 【12期】谈谈项目中单点登录的实现原理? 28 | 29 | 【13期】谈谈 Redis 的过期策略 30 | 31 | 【14期】你能说说进程与线程的区别吗 32 | 33 | 【15期】谈谈这几个常见的多线程面试题 34 | 35 | 【16期】你能谈谈HashMap怎样解决hash冲突吗 36 | 37 | 【17期】什么情况用ArrayList or LinkedList呢? 38 | 39 | 【18期】Java序列化与反序列化三连问:是什么?为什么要?如何做? 40 | 41 | 【19期】为什么Java线程没有Running状态? 42 | 43 | 【20期】你知道为什么HashMap是线程不安全的吗? 44 | 45 | ## 21-30期 46 | 47 | 【21期】你能说说Java中Comparable和Comparator的区别吗 48 | 49 | 【22期】为什么需要消息队列?使用消息队列有什么好处? 50 | 51 | 【23期】请你谈谈关于IO同步、异步、阻塞、非阻塞的区别 52 | 53 | 【24期】请你谈谈单例模式的优缺点,注意事项,使用场景 54 | 55 | 【25期】这三道常见的面试题,你有被问过吗? 56 | 57 | 【26期】如何判断一个对象是否存活?(或者GC对象的判定方法)? 58 | 59 | 【27期】Dubbo面试八连问,这些你都能答上来吗? 60 | 61 | 【28期】ZooKeeper面试那些事儿 62 | 63 | 【29期】Java集合框架 10 连问,你有被问过吗? 64 | 65 | 【30期】说一下HashMap的实现原理? 66 | 67 | ## 31-40期 68 | 69 | 【31期】了解什么是 redis 的雪崩、穿透和击穿?redis 崩溃之后会怎么样?应对措施是什么 70 | 71 | 【32期】你知道Redis的字符串是怎么实现的吗? 72 | 73 | 【33期】分别谈谈联合索引生效和失效的条件 74 | 75 | 【34期】谈谈为什么要拆分数据库?有哪些方法? 76 | 77 | 【35期】谈谈你对Java线程之间通信方式的理解 78 | 79 | 【36期】说说 如何停止一个正在运行的线程? 80 | 81 | 【37期】请你详细说说类加载流程,类加载机制及自定义类加载器 82 | 83 | 【38期】一份tcp、http面试指南,常考点都给你了 84 | 85 | 【39期】Mybatis面试18问,你想知道的都在这里了! 86 | 87 | 【40期】说一下线程池内部工作原理 88 | 89 | ## 41-50期 90 | 91 | 【41期】盘点那些必问的数据结构算法题之链表 92 | 93 | 【42期】盘点那些必问的数据结构算法题之二叉堆 94 | 95 | 【43期】盘点那些必问的数据结构算法题之二叉树基础 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /面试题/【美团】Java 岗 154 道面试题.md: -------------------------------------------------------------------------------- 1 | # 目录 2 | 3 | ## Java集合22题 4 | 5 | 1. ArrayList 和 Vector 的区别。 6 | 7 | 2. 说说 ArrayList,Vector,LinkedList 的存储性能和特性。 8 | 9 | 3. 快速失败 (fail-fast) 和安全失败 (fail-safe) 的区别是什么? 10 | 11 | 4. HashMap 的数据结构。 12 | 13 | 5. HashMap 的工作原理是什么? 14 | 15 | 6. Hashmap 什么时候进行扩容呢? 16 | 17 | 7. List、Map、Set 三个接口,存取元素时,各有什么特点? 18 | 19 | 8. Set 里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用 == 还是 equals()? 它们有何区别? 20 | 21 | 9. 两个对象值相同 (x.equals(y) == true),但却可有不同的 hash code,这句话对不对? 22 | 23 | 10. Heap 和 Stack 有什么区别。 24 | 25 | 11. Java 集合类框架的基本接口有哪些? 26 | 27 | 12. HashSet 和 TreeSet 有什么区别? 28 | 29 | 13. HashSet 的底层实现是什么? 30 | 31 | 14. LinkedHashMap 的实现原理? 32 | 33 | 15. 为什么集合类没有实现 Cloneable 和 Serializable 接口? 34 | 35 | 16. 什么是迭代器 (Iterator)? 36 | 37 | 17. Iterator 和 ListIterator 的区别是什么? 38 | 39 | 18. 数组 (Array) 和列表 (ArrayList) 有什么区别?什么时候应该使用 Array 而不是 ArrayList? 40 | 41 | 19. Java 集合类框架的最佳实践有哪些? 42 | 43 | 20. Set 里的元素是不能重复的,那么用什么方法来区分重复与否呢?是用 == 还是 equals()?它们有何区别? 44 | 45 | 21. Comparable 和 Comparator 接口是干什么的?列出它们的区别。 46 | 47 | 22. Collection 和 Collections 的区别。 48 | 49 | 50 | ## JVM与调优21题 51 | 52 | 1. Java 类加载过程? 53 | 54 | 2. 描述一下 JVM 加载 Class 文件的原理机制? 55 | 56 | 3. Java 内存分配。 57 | 58 | 4. GC 是什么? 为什么要有 GC? 59 | 60 | 5. 简述 Java 垃圾回收机制 61 | 62 | 6. 如何判断一个对象是否存活?(或者 GC 对象的判定方法) 63 | 64 | 7. 垃圾回收的优点和原理。并考虑 2 种回收机制 65 | 66 | 8. 垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收? 67 | 68 | 9. Java 中会存在内存泄漏吗,请简单描述 69 | 70 | 10. 深拷贝和浅拷贝。 71 | 72 | 11. System.gc() 和 Runtime.gc() 会做什么事情? 73 | 74 | 12. finalize() 方法什么时候被调用?析构函数 (finalization) 的目的是什么? 75 | 76 | 13. 如果对象的引用被置为 null,垃圾收集器是否会立即释放对象占用的内存? 77 | 78 | 14. 什么是分布式垃圾回收(DGC)?它是如何工作的? 79 | 80 | 15. 串行(serial)收集器和吞吐量(throughput)收集器的区别是什么? 81 | 82 | 16. 在 Java 中,对象什么时候可以被垃圾回收? 83 | 84 | 17. 简述 Java 内存分配与回收策率以及 Minor GC 和 Major GC。 85 | 86 | 18. JVM 的永久代中会发生垃圾回收么? 87 | 88 | 19. Java 中垃圾收集的方法有哪些? 89 | 90 | 20. 什么是类加载器,类加载器有哪些? 91 | 92 | 21. 类加载器双亲委派模型机制? 93 | 94 | 95 | ## 并发编程28题 96 | 97 | 1. Synchronized 用过吗,其原理是什么? 98 | 99 | 2. 你刚才提到获取对象的锁,这个“锁”到底是什么?如何确定对象的锁? 100 | 101 | 3. 什么是可重入性,为什么说 Synchronized 是可重入锁? 102 | 103 | 4. JVM 对 Java 的原生锁做了哪些优化?48 104 | 105 | 5. 为什么说 Synchronized 是非公平锁?49 106 | 107 | 6. 什么是锁消除和锁粗化?49 108 | 109 | 7. 为什么说 Synchronized 是一个悲观锁?乐观锁的实现原理又是什么?什么是 CAS,它有什么特性? 110 | 111 | 8. 乐观锁一定就是好的吗? 112 | 113 | 9. 跟 Synchronized 相比,可重入锁 ReentrantLock 其实现原理有什么不同? 114 | 115 | 10. 那么请谈谈 AQS 框架是怎么回事儿? 116 | 117 | 11. 请尽可能详尽地对比下 Synchronized 和 ReentrantLock 的异同。 118 | 119 | 12. ReentrantLock 是如何实现可重入性的? 120 | 121 | 13. 除了 ReetrantLock,你还接触过 JUC 中的哪些并发工具? 122 | 123 | 14. 请谈谈 ReadWriteLock 和 StampedLock。 124 | 125 | 15. 如何让 Java 的线程彼此同步?你了解过哪些同步器?请分别介绍下。 126 | 127 | 16. CyclicBarrier 和 CountDownLatch 看起来很相似,请对比下呢? 128 | 129 | 17. Java 线程池相关问题 130 | 131 | 18. Java 中的线程池是如何实现的? 132 | 133 | 19. 创建线程池的几个核心构造参数? 134 | 135 | 20. 线程池中的线程是怎么创建的?是一开始就随着线程池的启动创建好的吗? 136 | 137 | 21. 既然提到可以通过配置不同参数创建出不同的线程池,那么 Java 中默认实现好的线程池又有哪些呢?请比较它们的异同 138 | 139 | 22. 如何在 Java 线程池中提交线程? 140 | 141 | 23. 什么是 Java 的内存模型,Java 中各个线程是怎么彼此看到对方的变量的? 142 | 143 | 24. 请谈谈 volatile 有什么特点,为什么它能保证变量对所有线程的可见性? 144 | 145 | 25. 既然 volatile 能够保证线程间的变量可见性,是不是就意味着基于 volatile 变量的运算就是并发安全的? 146 | 147 | 26. 请对比下 volatile 对比 Synchronized 的异同。 148 | 149 | 27. 请谈谈 ThreadLocal 是怎么解决并发安全的? 150 | 151 | 28. 很多人都说要慎用 ThreadLocal,谈谈你的理解,使用 ThreadLocal 需要注意些什么? 152 | 153 | 154 | ## Spring 25题 155 | 156 | 1. 什么是 Spring 框架?Spring 框架有哪些主要模块? 157 | 158 | 2. 使用 Spring 框架能带来哪些好处? 159 | 160 | 3. 什么是控制反转(IOC)?什么是依赖注入? 161 | 162 | 4. 请解释下 Spring 框架中的 IoC? 163 | 164 | 5. BeanFactory 和 ApplicationContext 有什么区别? 165 | 166 | 6. Spring 有几种配置方式? 167 | 168 | 7. 如何用基于 XML 配置的方式配置 Spring? 169 | 170 | 8. 如何用基于 Java 配置的方式配置 Spring? 171 | 172 | 9. 怎样用注解的方式配置 Spring? 173 | 174 | 10. 请解释 Spring Bean 的生命周期? 175 | 176 | 11. Spring Bean 的作用域之间有什么区别? 177 | 178 | 12. 什么是 Spring inner beans? 179 | 180 | 13. Spring 框架中的单例 Beans 是线程安全的么? 181 | 182 | 14. 请举例说明如何在 Spring 中注入一个 Java Collection? 183 | 184 | 15. 如何向 Spring Bean 中注入一个 Java.util.Properties? 185 | 186 | 16. 请解释 Spring Bean 的自动装配? 187 | 188 | 17. 请解释自动装配模式的区别? 189 | 190 | 18. 如何开启基于注解的自动装配? 191 | 192 | 19. 请举例解释@Required 注解? 193 | 194 | 20. 请举例解释@Autowired 注解? 195 | 196 | 21. 请举例说明@Qualifier 注解? 197 | 198 | 22. 构造方法注入和设值注入有什么区别? 199 | 200 | 23. Spring 框架中有哪些不同类型的事件? 201 | 202 | 24. FileSystemResource 和 ClassPathResource 有何区别? 203 | 204 | 25. Spring 框架中都用到了哪些设计模式? 205 | 206 | 207 | ## 设计模式 10题 208 | 209 | 1. 请列举出在 JDK 中几个常用的设计模式? 210 | 211 | 2. 什么是设计模式?你是否在你的代码里面使用过任何设计模式? 212 | 213 | 3. Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式 214 | 215 | 4. 在 Java 中,什么叫观察者设计模式(observer design pattern)? 216 | 217 | 5. 使用工厂模式最主要的好处是什么?在哪里使用? 218 | 219 | 6. 举一个用 Java 实现的装饰模式(decorator design pattern)?它是作用于对象层次还是类 220 | 221 | 层次? 222 | 223 | 7. 在 Java 中,为什么不允许从静态方法中访问非静态变量? 224 | 225 | 8. 设计一个 ATM 机,请说出你的设计思路? 226 | 227 | 9. 在 Java 中,什么时候用重载,什么时候用重写? 228 | 229 | 10. 举例说明什么情况下会更倾向于使用抽象类而不是接口 230 | 231 | 232 | ## SpringBoot 22题 233 | 234 | 1. 什么是 Spring Boot? 235 | 236 | 2. Spring Boot 有哪些优点? 237 | 238 | 3. 什么是 JavaConfig? 239 | 240 | 4. 如何重新加载 Spring Boot 上的更改,而无需重新启动服务器? 241 | 242 | 5. Spring Boot 中的监视器是什么? 243 | 244 | 6. 如何在 Spring Boot 中禁用 Actuator 端点安全性? 245 | 246 | 7. 如何在自定义端口上运行 Spring Boot 应用程序? 247 | 248 | 8. 什么是 YAML? 249 | 250 | 9. 如何实现 Spring Boot 应用程序的安全性? 251 | 252 | 10. 如何集成 Spring Boot 和 ActiveMQ? 253 | 254 | 11. 如何使用 Spring Boot 实现分页和排序? 255 | 256 | 12. 什么是 Swagger?你用 Spring Boot 实现了它吗? 257 | 258 | 13. 什么是 Spring Profiles? 259 | 260 | 14. 什么是 Spring Batch? 261 | 262 | 15. 什么是 FreeMarker 模板? 263 | 264 | 16. 如何使用 Spring Boot 实现异常处理? 265 | 266 | 17. 您使用了哪些 starter maven 依赖项? 267 | 268 | 18. 什么是 CSRF 攻击? 269 | 270 | 19. 什么是 WebSockets? 271 | 272 | 20. 什么是 AOP? 273 | 274 | 21. 什么是 Apache Kafka? 275 | 276 | 22. 我们如何监视所有 Spring Boot 微服务? 277 | 278 | 279 | ## Netty10题 280 | 281 | 1. BIO、NIO和AIO的区别? 282 | 283 | 2. NIO的组成? 284 | 285 | 3. Netty的特点? 286 | 287 | 4. Netty的线程模型? 288 | 289 | 5. TCP 粘包/拆包的原因及解决方法? 290 | 291 | 6. 了解哪几种序列化协议? 292 | 293 | 7. 如何选择序列化协议? 294 | 295 | 8. Netty的零拷贝实现? 296 | 297 | 9. Netty的高性能表现在哪些方面? 298 | 299 | 10. NIOEventLoopGroup源码? 300 | 301 | 302 | ## Redis 16题 303 | 304 | 1. 什么是redis? 305 | 306 | 2. Reids的特点 307 | 308 | 4. Redis支持的数据类型 309 | 310 | 5. Redis是单进程单线程的 311 | 312 | 6. 虚拟内存 313 | 314 | 7. Redis锁 315 | 316 | 8. 读写分离模型 317 | 318 | 9. 数据分片模型 319 | 320 | 10. Redis的回收策略 321 | 322 | 11. 使用Redis有哪些好处? 323 | 324 | 12. redis相比memcached有哪些优势? 325 | 326 | 13. redis常见性能问题和解决方案 327 | 328 | 14. MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据245 329 | 330 | 15. Memcache与Redis的区别都有哪些? 331 | 332 | 16. Redis 常见的性能问题都有哪些?如何解决? 333 | 334 | 17. Redis 最适合的场景 335 | 336 | # 解析 337 | 338 | ## Java集合22题 339 | 340 | ###

1、ArrayList 和 Vector 的区别。

341 | 342 | | ArrayList | Vector | 343 | |-------|-------| 344 | | 1、实现原理:采用动态对象数组实现,默认构造方法创建了一个空数组 | 1、实现原理:采用动态对象数组实现,默认构造创建了一个大小为10的对象数组 | 345 | | 2、第一次添加元素,扩展容量为 10,之后的扩充算法:原来数组大小+原来数组的一半 | 2、扩充的算法:当增量为0时,扩充为原来大小的 2 倍,当增量大于 0 时,扩充为原来大小+增量 | 346 | | 3、不适合进行删除或插入操作 | 3、不适合删除或插入操作 | 347 | | 4、为了防止数组动态扩充次数过多,建议创建 ArrayList 时,给定初始容量 | 4、为了防止数组动态扩充次数过多,建议创建 Vector 时,给定初始容量 | 348 | | 5、多线程中使用不安全,适合在单线程访问时使用,效率较高 | 5、线程安全,适合在多线程访问时使用,效率较低 | 349 | 350 | 351 | ###

2、说说 ArrayList,Vector, LinkedList 的存储性能和特性。

352 | 353 | ArrayList 采用的是数组形式来保存对象的,这种方式将对象放在连续的位置中,所以最大的缺点就是插入删除时非常麻烦。 354 | 355 | LinkedList 采用的将对象存放在独立的空间中,而且在每个空间中还保存下一个链接的索引,但是缺点就是查找非常麻烦,要丛第一个索引开始。 356 | 357 | ArrayList 和 Vector 都是用数组方式存储数据,此数组元素数要大于实际的存储空间以便进行元素增加和插入操作,他们都允许直接用序号索引元素,但是插入数据元素涉及到元素移动等内存操作,所以索引数据快而插入数据慢。 358 | 359 | Vector 使用了 synchronized 方法(线程安全),因此 Vector 是线程安全的容器,但性能上比 ArrayList 要差些。(Vector 是 Java 中的遗留容器) 360 | 361 | LinkedList 使用双向链表方式存储数据(将内存中零散的内存单元通过附加的引用关联起来,形成一个可以按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,内存的利用率更高),按序号索引数据需要前向或后向遍历数据,所以索引数据慢,插入数据时只需要记录前后项即可,所以插入的速度快。 362 | 363 | 364 | ###

3、快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?

365 | 366 | **快速失败(fail-fast)** 367 | 368 | 在使用迭代器对集合对象进行遍历的时候,如果 A 线程正在对集合进行遍历,此时 B 线程对集合进行修改(增加、删除、修改),或者 A 线程在遍历过程中对集合进行修改,都会导致 A 线程抛出 ConcurrentModificationException 异常。 369 | 370 | **安全失败(fail-safe)** 371 | 372 | 采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。 373 | 374 | 由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,故不会抛 ConcurrentModificationException 异常。 375 | 376 | **区别** 377 | 378 | Iterator 的安全失败是基于对底层集合做拷贝,因此,它不受源集合上修改的影响。 379 | 380 | java.util 包下面的所有的集合类都是快速失败的,而 java.util.concurrent 包下面的所有的类都是安全失败的。 381 | 382 | 快速失败的迭代器会抛出 ConcurrentModificationException 异常,而安全失败的迭代器永远不会抛出这样的异常。 383 | 384 | 385 | ###

4、HashMap 的数据结构。

386 | 387 | **HashMap 的底层实现** 388 | 389 | **JDK1.8 之前** 390 | 391 | JDK1.8 之前 HashMap 的底层是 **数组和链表** 结合在一起使用也就是 链表散列。 392 | 393 | HashMap 通过 key 的 HashCode 经过扰动函数处理后得到 hash 值,然后通过(n-1)& hash 判断当前元素存放的位置(这里 n 指的是数组的长度),如果当前位置存在元素的话,就判断元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。 394 | 395 | 所谓扰动函数指的就是 HashMap 的 hash 方法。 396 | 397 | 使用 hash 方法也就是扰动函数是为了防止一些实现较差的 hashCode()方法,换句话说就是使用扰动函数之后可以减少碰撞。 398 | 399 | JDK1.8 HashMa p的 hash 源码: 400 | 401 | ```java 402 | static final int hash(Object key) { 403 | int h; 404 | // key.hashCode():返回散列值也就是hashcode 405 | // ^ :按位异或 406 | // >>>:无符号右移,忽略符号位,空位都以0补齐 407 | return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); 408 | } 409 | ``` 410 | 411 | JDK1.8 的 hash 方法相比于 JDK1.7 hash 方法更加简化,但是原理不变。 412 | 413 | 对比一下 JDK1.7 的 HashMap 的 hash 方法源码: 414 | 415 | ```java 416 | static int hash(int h) { 417 | // This function ensures that hashCodes that differ only by 418 | // constant multiples at each bit position have a bounded 419 | // number of collisions (approximately 8 at default load factor). 420 | 421 | h ^= (h >>> 20) ^ (h >>> 12); 422 | return h ^ (h >>> 7) ^ (h >>> 4); 423 | } 424 | ``` 425 | 426 | 相比于 JDK1.8 的 hash 方法,JDK1.7 的 hash 方法的性能会少差一点点,因为毕竟扰动了 4 次。 427 | 428 | 所谓“拉链法”就是:将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。 429 | 430 | ![1.7HashMap 数据结构演示图](https://mmbiz.qpic.cn/mmbiz_png/ABIWtj6YasQXzyRD5vxDpofr6eKevUxYUvia1ribkJKBo5msWTzUAZgrDcibA2gyUKqpOiaucLIvKxzzPQrf4aH26A/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 431 | 432 | **JDK1.8 之后** 433 | 相比于之前的版本,JDK1.8 之后在解决哈希冲突时有了较大的变化,当链表长度大于阈(yu 四声)值(默认为 8)时,将链表转换为红黑树,以减少搜索时间。 434 | 435 | ![1.8HashMap 数据结构演示图](https://mmbiz.qpic.cn/mmbiz_png/ABIWtj6YasQXzyRD5vxDpofr6eKevUxYUQpxaamTYUBUO77RjMavbVEUfCo8fzrRuVSicFrydHiaMFm4v3EIKbMw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 436 | 437 | > TreeMap、TreeSet 以及 JDK1.8 之后的 HashMap 底层都用了红黑树。红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线程结构。 438 | 439 | 440 | ###

5、HashMap 的工作原理是什么?

441 | 442 | HashMap 是基于 Hashing(散列法)的原理,以键值对(key-value)的形式存储元素的,我们使用put(key, value)存储对象到HashMap中,使用 get(key)从 HashMap 中获取对象。 443 | 444 | mark:**HashMap 的键值对也叫作 Entry,而每个 Entry 都是存储在数组当中,因此这个数组就是 HashMap 的主干。** 445 | 446 | 它需要一个 hash 函数,使用 HashCode() 和 equals() 方法来向集合/从集合添加和检索元素。 447 | 448 | 它需要一个 hash 函数,使用 hashCode() 和 equals() 方法来向集合/从集合添加和检索元素。 449 | 450 | 当调用 put() 方法的时候,HashMap会计算 key 的 hash 值,然后把键值对存储在集合中合适的索引上。如果 key 已经存在了,value 会被更新成新值。 451 | 452 | HashMap 数组中的每一个元素的初始值都是 NULL。 453 | 454 | ![HashMap 底层图](https://mmbiz.qpic.cn/mmbiz_png/ABIWtj6YasQXzyRD5vxDpofr6eKevUxY6VqqnKiaPFzAY7lzTR0ibJ0ZYH8FBgaJibvkZuLje5R02tfkycx9hBCSQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 455 | 456 | HashMap 的一些重要的特性是它的容量(capacity),负载因子(load factor)和扩容极限(threshold resizing)。 457 | 458 | **1、Put 方法的实现原理** 459 | 460 | HaspMap 的一种重要的方法是 put() 方法,当我们调用 put() 方法时,比如 hashMap.put("Java",0),此时要插入一个 Key 值为“Java”的元素,这时首先需要一个 Hash 函数来确定这个 Entry 的插入位置,设为 index,即 index = hash("Java"),假设求出的 index 值为 2,那么这个 Entry 就会插入到数组索引为 2 的位置。 461 | 462 | ![HashMap底层实现图](https://mmbiz.qpic.cn/mmbiz_png/ABIWtj6YasQXzyRD5vxDpofr6eKevUxYwhC0MG4ffIuQyDrTGD3DQicjJSA7stCs5dibictrrdKSQz4K0cyJJXLEA/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 463 | 464 | 但是 HaspMap 的长度肯定是有限的,当插入的 Entry 越来越多时,不同的 Key 值通过哈希函数算出来的 index 值肯定会有冲突,此时就可以利用链表来解决。 465 | 466 | 其实HaspMap数组的每一个元素不止是一个 Entry 对象,也是一个链表的头节点,每一个 Entry 对象通过 Next 指针指向下一个 Entry 对象,这样,当新的 Entry 的 hash 值与之前的存在冲突时,只需要插入到对应点链表即可。 467 | 468 | ![HashMap 底层实现](https://mmbiz.qpic.cn/mmbiz_png/ABIWtj6YasQXzyRD5vxDpofr6eKevUxYK3mAjodJUHmppFic15HKss9mg2BpLNRvR001PiaPyiaRvbpBpKVKfSMkA/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 469 | 470 | 需要注意的是,新来的 Entry 节点采用的是“头插法”,而不是直接插入在链表的尾部,这是因为 HashMap 的发明者认为,新插入的节点被查找的可能性更大。 471 | 472 | **2、Get 方法的实现原理** 473 | 474 | get() 方法用来根据 Key 值来查找对应点 Value,当调用 get() 方法时,比如 hashMap.get("apple"),这时同样要对 Key 值做一次 Hash 映射,算出其对应的 index 值,即 index = hash("apple")。 475 | 476 | 前面说到的可能存在 Hash 冲突,同一个位置可能存在多个 Entry,这时就要从对应链表的头节点开始,一个个向下查找,直到找到对应的 Key 值,这样就获得到了所要查找的键值对。 477 | 478 | 例如假设我们要找的 Key 值是"apple": 479 | 480 | ![HashMap 底层实现](https://mmbiz.qpic.cn/mmbiz_png/ABIWtj6YasQXzyRD5vxDpofr6eKevUxYIqoia9pPQUB85E9UYA75j9vHIL2OGVg6qTN3EyaxCdL6nABWbs74tEw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 481 | 482 | 第一步,算出 Key值“apple”的 hash 值,假设为 2。 483 | 484 | 第二步,在数组中查找索引为2的位置,此时找到头节点为 Entry6,Entry6 的 Key 值是 banana,不是我们要找的值。 485 | 486 | 第三步,查找 Entry6 的 Next 节点,这里为 Entry1,它的 Key 值为 apple,是我们要查找的值,这样就找到了对应的键值对,结束。 487 | 488 | 489 | ###

6、HashMap 什么时候进行扩容呢?

490 | 491 | 当 HashMap 中的元素个数超过 数组大小 * loadFactor 时,就会进行数组扩容,loadFactor 的默认值为 0.75。 492 | 493 | 也就是说,默认情况下,数组大小为 16,那么当 HashMap 中元素个数超过 16*0.75=12 的时候,就把数组的大小扩展为 2*16=32,即扩大一倍。 494 | 495 | 然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知 HashMap 中元素的个数,那么预设元素的个数能够有效的提高 HashMap 的性能。 496 | 497 | 比如说,我们有 1000 个元素 new HashMap(1000),但是理论上来讲 new HashMap(1024) 更合适,不过上面已经说过,即使是 1000,HashMap 也自动会将其设置为 1024。 498 | 499 | 但是 new HashMap(1024) 还不是更合适的,因为 0.75*1000 < 1000,也就是说为了让 0.75 * size > 1000,我们必须这样 new HashMap(2048) 才最合适,既考虑了 & 的问题,也避免了 resize 的问题。 500 | 501 | **resize:原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是 resize** 502 | 503 | 504 | ###

7、List、Map、Set 三个接口,存取元素时,各有什么特点?

505 | 506 | List 与 Set 都是单列元素的集合,它们有一个功共同的父接口 Collection。 507 | 508 | **Set 里面不允许有重复的元素** 509 | 510 | 存元素:add 方法有一个 boolean 的返回值,当集合中没有某个元素,此时add方法可成功加入该元素时,则返回 true;当集合含有与某个元素 equals 相等的元素时,此时 add 方法无法加入该元素,返回结果为 false。 511 | 512 | 取元素:没法说取第几个,只能以 Iterator 接口取得所有的元素,再逐一遍历各个元素。 513 | 514 | **List 表示有先后顺序的集合** 515 | 516 | 存元素:多次调用 add(Object) 方法时,每次加入的对象按先来后到的顺序排序,也可以插队,即调用 add(int index,Object) 方法,就可以指定当前对象在集合中的存放位置。 517 | 518 | 取元素: 519 | 520 | 1. Iterator 接口取得所有,逐一遍历各个元素。 521 | 522 | 2. 调用 get(index i) 来明确说明取第几个。 523 | 524 | **Map 是双列的集合** 525 | 526 | 存放用 put 方法:put(obj key,obj value),每次存储时,要存储一对 key/value,不能存储重复的 key,这个重复的规则也是按 equals 比较相等。 527 | 528 | 取元素: 529 | 530 | 1. 用 get(Object key) 方法根据 key 获得相应的 value。 531 | 532 | 2. 也可以获得所有的 key 的集合,还可以获得所有的 value 的集合。 533 | 534 | 3. 还可以获得key和value组合成的 Map.Entry 对象的集合。 535 | 536 | 537 | * List 以特定次序来持有元素,可有重复元素。 538 | 539 | * Set 无法拥有重复元素,内部排序。 540 | 541 | * Map 保存 key-value 值,value 可多值。 542 | 543 | 544 | ###

8、Set 里的元素是不能重复的,那么用什么方法来区分重复与否呢?是用 == 还是 equals()?它们有何区别?

545 | 546 | Set 里的元素是不能重复的,元素重复与否是使用 equals() 方法进行判断的。 547 | 548 | equals() 和 == 方法决定引用值是否指向同一对象,equals() 在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。 549 | 550 | **equals()和 == 的区别** 551 | 552 | == 操作符专门用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的数值是否相同,要比较两个基本类型的数据或两个引用变量是否相等,只能用 == 操作符。 553 | 554 | 如果一个变量指向的数据是对象类型的,那么,这时候涉及了两块内存, 对象本身占用一块内存(堆内存),变量也占用一块内存。 555 | 556 | 例如 Objet obj = new Object();变量 obj 是一个内存,new Object()是另一个内存,此时,变量 obj 所对应的内存中存储的数值就是对象占用的那块内存的首地址。 557 | 558 | 对于指向对象类型的变量,如果要比较两个变量是否指向同一个对象,即要看这两个变量所对应的内存中的数值是否相等,这时候就需要用==操作符进行比较。 559 | 560 | equals 方法是用于比较两个独立对象的内容是否相同,就好比去比较两个人的长相是否相同,它比较的两个对象是独立的。 561 | 562 | **总结** 563 | 564 | **==** 565 | 566 | 基本类型:比较的是值是否相同 567 | 568 | 引用类型:比较的是地址值是否相同 569 | 570 | **equals()** 571 | 572 | 引用类型:默认情况下,比较的是地址值,可进行重写,比较的是对象的成员变量值是否相同。 573 | 574 | 575 | ###

9、两个对象值相同(x.equals(y) == true),但却可有不同的 hash code,这句话对不对?

576 | 577 | 不对,如果两个对象 x 和 y 满足 x.equals(y) == true,它们的哈希码(hashCode)应当相同。 578 | 579 | Java 对于 equals 方法和 hashCode 方法是这样规定的: 580 | 581 | 1)如果两个对象相同(equals 方法返回 true),那么它们的 hashCode 值一定要相同; 582 | 583 | 2)如果两个对象的 hashCode 相同,它们并不一定相同。 584 | 585 | 当然,你未必要按照要求去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现在 Set 集合中,同时增加新元素的效率也会大大下降(对于是哟个哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能极具下降)。 586 | 587 | 首先 equals 方法必须满足: 588 | 589 | + 自反性(x.equals(x) 必须返回 true) 590 | + 对称性(x.equals(y) 返回 true 时,y.equals(x) 也必须返回 true) 591 | + 传递性(x.equals(y) 和 y.equals(z) 都返回 true 时,x.equals(z) 也必须返回 true) 592 | + 一致性(当 x 和 y 引用的对象信息没有被修改时,多次调用 x.equals(y)应该得到同样的返回值),而且对于任何非 null 值得引用 x,x.equals(null)必须返回false。 593 | 594 | 实现高质量得 equals 方法得诀窍包括: 595 | 596 | 1. 使用 == 操作符检查 “参数是否为这个对象得引用”; 597 | 598 | 2. 使用 instanceof 操作符检查 “参数是否为正确得类型”; 599 | 600 | 3. 对于类中得关键属性,检查参数传入对象的属性是否与之相匹配; 601 | 602 | 4. 编写完 equals 方法后,问自己它是否满足对称性、传递性、一致性; 603 | 604 | 5. 重写 equals 时总是要重写 hashCode; 605 | 606 | 6. 不要将 equals 方法参数中的 Object 对象替换为其他的类型,在重写时不要忘记 @Override 注解。 607 | 608 | 609 | ###

10、Heap 和 Stack 有什么区别

610 | 611 | **堆栈的概念:堆栈是两种数据结构。** 612 | 613 | 堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。 614 | 615 | 在单片机应用中,堆栈是个特殊的存储区,主要功能是暂时存放数据和地址,通常用来保护断点和现场。 616 | 617 | 要点: 618 | 619 | + 堆,队列优先,先进先出(FIFO - first in first out)。 620 | 621 | + 栈,先进后出(FILO - first in last out)。 622 | 623 | **Java中栈和堆的区别** 624 | 625 | 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。 626 | 627 | 在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。 628 | 629 | 当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。 630 | 631 | 堆内存用来存放由new创建的对象和数组,在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。 632 | 633 | 在堆中产生了一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。 634 | 635 | 引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。 636 | 637 | 638 | ###

11、Java 集合类框架的基本接口有哪些?

639 | 640 | 总共有两大接口:Collection 和Map ,一个元素集合,一个是键值对集合; 641 | 642 | 其中List和Set接口继承了Collection接口,一个是有序元素集合,一个是无序元素集合; 643 | 644 | 而ArrayList和 LinkedList 实现了List接口,HashSet实现了Set接口,这几个都比较常用; 645 | 646 | HashMap 和HashTable实现了Map接口,并且HashTable是线程安全的,但是HashMap性能更好; 647 | 648 | Java集合类里最基本的接口有: 649 | 650 | + Collection:单列集合的根接口 651 | 652 | + List:元素有序 可重复 653 | 654 | + ArrayList:类似一个长度可变的数组 。适合查询,不适合增删 655 | 656 | + LinkedList:底层是双向循环链表。适合增删,不适合查询。 657 | 658 | + Set:元素无序,不可重复 659 | 660 | + HashSet:根据对象的哈希值确定元素在集合中的位置 661 | 662 | + TreeSet: 以二叉树的方式存储元素,实现了对集合中的元素排序 663 | 664 | + Map:双列集合的根接口,用于存储具有键(key)、值(value)映射关系的元素。 665 | 666 | + HashMap:用于存储键值映射关系,不能出现重复的键key 667 | 668 | + TreeMap:用来存储键值映射关系,不能出现重复的键key,所有的键按照二叉树的方式排列 669 | 670 | 671 | ###

12、HashSet 和 TreeSet 有什么区别?

672 | 673 | **相同点**:单例集合,数据不可重复 674 | 675 | **不同点1**:底层使用的储存数据结构不同: 676 | 677 | 1、Hashset底层使用的是HashMap哈希表结构储存 678 | 679 | 2、而Treeset底层用的是TreeMap树结构储存。 680 | 681 | **不同点2**:储存的数据保存唯一方式不用。 682 | 683 | 1、Hashset是通过复写hashCode()方法和equals()方法来保证的。 684 | 685 | 2、而Treeset是通过Compareable接口的compareto方法来保证的。 686 | 687 | **不同点3**:hashset无序 Treeset有序 688 | 689 | **储存原理** 690 | 691 | hashset:底层数据结构是哈希表,本质就是哈希值储存。通过判断元素的hashcode方法和equals方法来保证元素的唯一性。当哈希值不同时就直接进行储存。如果相同,会判断一次equals方式是否返回为true ,如果是true 则视为用的同一个元素,不用再储存。如果是false,这俄格相同哈希值不同内容的元素会放在同一个桶里(当哈希表中有一个桶结构,每一个捅都有一个哈希值)。 692 | 693 | Treeset:底层数据结构式一个二叉树,可以对set集合中的元素进行排序,这种结构,可以提高排序性能。根据比较方法的返回值决定的,只要返回的是0,就代表元素重复。 694 | 695 | 696 | ###

13、HashSet 的底层实现是什么?

697 | 698 | 众所周知,HashSet 里面存储的元素都具有无序性,标识唯一性,HashSet 里面大多数的内容都是在 HashMap 的基础上进行修改的。 699 | 700 | ![HashSet源码](https://mmbiz.qpic.cn/mmbiz_png/ABIWtj6YasS8ZKbnvGm61XaWOyWZpxn9ZOibhOr3W0mD27BURnSicQLcibWXhiaEwlrF6lsS6NJqvHiaZvxIiaVlVYDw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 701 | 702 | 上图可以看出,HashSet 是通过将相应的内容存储在了一个 HashMap 里的 key 中,然后再去读取的。 703 | 704 | 为了保证 HashSet 里面的数据唯一性,这里将 HashSet 存放的元素作为了 HashMap 里面唯一的 Key 变量,value 部分用了一个 PERSENT 对象来存储,也就是源码里面的这一句内容: 705 | 706 | ```java 707 | private static final Object PRESENT = new Object(); 708 | ``` 709 | 710 | 其实 HashSet 底层的很多部分都是引用了 HashMap 来进行实现的。 711 | 712 | 713 | ###

14、LinkedHashMap 的实现原理?

714 | 715 | LinkedHashMap 是 Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键,但不保证映射的顺序,特别是它不保证该顺序恒久不变。 716 | 717 | LinkedHashMap 实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。 718 | 719 | 注意,此实现不是同步的。如果多个线程同时访问链接的哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须保持外部同步。 720 | 721 | 对于 LinkedHashMap 而言,它继承与 HashMap、底层使用哈希表与双向链表来保存所有元素。其基本操作与父类 HashMap 相似,它通过重写父类相关的方法,来实现自己的链接列表特性。 722 | 723 | 来看一个简单的 LinkedHashMap 程序: 724 | 725 | ![LinkedHashMap程序](https://mmbiz.qpic.cn/mmbiz_png/ABIWtj6YasQ7x28FWiaVVTVV52xgrW2ibbPEGVFpQBTjduxPQt4f5CEAskBZ3v2VIGoGykEJpV07iamBzJIVnK4sw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 726 | 727 | 运行结果是: 728 | 729 | ![运行结果](https://mmbiz.qpic.cn/mmbiz_png/ABIWtj6YasQ7x28FWiaVVTVV52xgrW2ibbSNBwQs8NXhXHaCj752HbQBYJf0hFMnxogib0XfgdFm0Q18wo20mEcNQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 730 | 731 | 我们可以观察到,和 HashMap 的运行结果不同,LinkedHashMap 的迭代输出的结果保持了插入顺序。 732 | 733 | 是什么样的结构使得 LinkedHashMap 具有如此特性呢? 734 | 735 | 我们还是一样的看看 LinkedHashMap 的内部结构,对它有一个感性的认识: 736 | 737 | ![LinkedHashMap 内部结构](https://mmbiz.qpic.cn/mmbiz_jpg/ABIWtj6YasQ7x28FWiaVVTVV52xgrW2ibbvuQESWAKua7PO3tlg07lOhfhF8K63Bv6Ban68TxzDqYEibbs0sicfYxA/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 738 | 739 | 740 | ###

15、为什么集合类没有实现 Cloneable 和 Serializable 接口?

741 | 742 | 克隆(cloning)或者序列化(serialization)的语义和含义是跟具体的实现相关的。因此应该由集合类的具体实现类来决定如何被克隆或者序列化。 743 | 744 | 1)什么是克隆? 745 | 746 | 克隆是把一个对象里面的属性值,复制给另外一个对象,而不是对象引用的复制。 747 | 748 | 2)实现 Serializable 序列化的作用 749 | 750 | 1、将对象的状态保存在存储媒体中,以便在重写时创建出完全相同的副本 751 | 752 | 2、按值将对象从一个应用程序域发向另一个应用程序域 753 | 754 | 实现 Serializable 接口的作用就是可以把对象存到字节流,然后可以恢复。 755 | 756 | 所以对象没有序列化,怎么能在网络传输呢?**要网络传输就得转为字节流**,在分布式应用中,你就得实现序列化。如果不需要分布式引用,那就没必要实现序列化。 757 | 758 | 759 | ###

16、什么是迭代器 (Iterator)?

760 | 761 | Iterator接口提供了很多对集合元素进行迭代的方法,每一个集合类都包括了可以返回迭代器实例的迭代方法。 762 | 763 | 迭代器可以在迭代过程中删除底层集合的元素,但是不可以直接调用集合的remove(Object obj)删除,可以通过迭代器的remove()方法删除。 764 | 765 | 766 | ###

17、Iterator 和 ListIterator 的区别是什么?

767 | 768 | Iterator和ListIterator是Java三个游标中的两个,都是由Java.UTIL包中的集合框架定义的。 769 | 770 | **Iterator是什么?** 771 | 772 | Iterator代表迭代器,是Collection框架中的一个接口;用于遍历集合元素。 773 | 774 | 它允许逐个迭代集合中的每个元素,从集合中获取元素或从集合中删除元素;但无法使用Iterator修改集合中的任何元素。 775 | 776 | Iterator有一个iterator()方法,它会将迭代器返回到集合的开头。一旦得到一个集合开头的迭代器,然后遍历集合中的元素,就建立一个循环,每次循环迭代时调用hasNext()。 777 | 778 | hasNext()如果返回true,则表示集合中存在下一个元素;如果返回false,则表示遍历所有元素。然后在循环内部,可以使用next()获取集合中的每个元素。next()方法返回集合的下一个元素。 779 | 780 | 缺点: 781 | 782 | ● 使用Iterator,就只能向前移动集合。 783 | ● 使用Iterator,就无法操纵或修改集合中的元素。 784 | 785 | **ListIterator是什么?** 786 | 787 | ListIterator是Collection框架中的一个接口;是用于扩展Iterator接口的。 788 | 789 | 使用ListIterator,可以向前和向后遍历集合的元素。还可以添加、删除或修改集合中的任何元素。简而言之,我们可以说它消除了Iterator的缺点。 790 | 791 | ListIterator的方法如下: 792 | 793 | ● hasNext():如果返回true,则确认集合中有更多元素。 794 | 795 | ● next():返回列表的下一个元素。 796 | 797 | ● nextIndex():返回列表中下一个元素的索引。 798 | 799 | ● hasPrevious():如果集合中有相反的元素,则返回true。 800 | 801 | ● previous():返回集合中的上一个元素。 802 | 803 | ● previousIndex():返回集合中上一个元素的索引。 804 | 805 | ● remove():从集合中删除元素。 806 | 807 | ● set():修改集合中的元素。 808 | 809 | ● add():在集合中添加新元素。 810 | 811 | 812 | **Iterator和ListIterator之间的主要区别** 813 | 814 | 1、遍历 815 | 816 | 使用Iterator,可以遍历所有集合,如Map,List,Set;但只能在向前方向上遍历集合中的元素。 817 | 818 | 使用ListIterator,只能遍历List实现的对象,但可以向前和向后遍历集合中的元素。 819 | 820 | 2、添加元素 821 | 822 | Iterator无法向集合中添加元素;而,ListIteror可以向集合添加元素。 823 | 824 | 3、修改元素 825 | 826 | Iterator无法修改集合中的元素;而,ListIterator可以使用set()修改集合中的元素。 827 | 828 | 4、索引 829 | 830 | Iterator无法获取集合中元素的索引;而,使用ListIterator,可以获取集合中元素的索引。 831 | 832 | 833 | ###

18、数组 (Array) 和列表 (ArrayList) 有什么区别?什么时候应该使用 Array 而不是 ArrayList?

834 | 835 | **Array**:它是数组,申明数组的时候就要初始化并确定长度,长度不可变,而且它只能存储同一类型的数据,比如申明为String类型的数组,那么它只能存储S听类型数据。 836 | 837 | **ArrayList**:它是一个集合,需要先申明,然后再添加数据,长度是根据内容的多少而改变的,ArrayList可以存放不同类型的数据,在存储基本类型数据的时候要使用基本数据类型的包装类。 838 | 839 | **区别**:当能确定长度并且数据类型一致的时候就可以用数组,其他时候使用ArrayList。 840 | 841 | 842 | ###

19、Java 集合类框架的最佳实践有哪些?

843 | 844 | ![Java集合框架](https://mmbiz.qpic.cn/mmbiz_png/ABIWtj6YasRokhQ5FrqQz2xDzQOudWUvBHSdSic6LDkLFRBq0RJicXkUn4brN21djQNX02c4VnaNehd5YU8ibzKDw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 845 | 846 | 847 | ###

20、Set 里的元素是不能重复的,那么用什么方法来区分重复与否呢?是用 == 还是 equals()?它们有何区别?

848 | 849 | Set 里的元素是不能重复的,元素重复与否是使用 equals() 方法进行判断的。 850 | 851 | equals() 和 == 方法决定引用值是否指向同一对象 equals() 在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。 852 | 853 | 854 | ###

21、Comparable 和 Comparator 接口是干什么的?列出它们的区别。

855 | 856 | Comparable 和 Comparator 都是用来实现集合中元素的比较、排序的。 857 | 858 | **Comparable** 859 | 860 | Comparable 是在集合内部定义的方法实现的排序,位于 java.lang 下。它是一个对象本身就已经支持自比较所需要实现的接口,如 String、Integer 自己就实现了 Comparable 接口,可完成比较大小操作。 861 | 862 | 自定义类要在加入 list 容器中后能够排序,也可以实现 Comparable 接口,用在 Collections 类的 sort 方法排序时若不指定 Comparator,那就以自然排序排列。 863 | 864 | 所谓自然顺序就是实现 Comparable 接口设定的排序方式。 865 | 866 | **Comparator** 867 | 868 | Comparator 是在集合外部实现的排序,位于 java.util 下。它是一个专门的比较器,当这个对象不支持自比较或者自比较函数不能满足要求时,可写一个比较器来完成两个对象之间大小的比较。 869 | 870 | Comparator 体现了一种策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。 871 | 872 | **区别** 873 | 874 | 总而言之 Comparable 是自己完成比较,Comparator 是外部程序实现比较。 875 | 876 | 877 | ###

22、Collection 和 Collections 的区别。

878 | 879 | **Collection 与 Collections 的根本区别是:** 880 | 881 | 1、Collection 是一个集合接口。它提供了对接好对象进行基本操作的通用接口方法。Collection 接口在 Java 类库中有很多具体的实现。Collection 接口的意义是为各种具体的集合提供了最大化的统一操作方式。 882 | 883 | ![java 集合架构图](https://mmbiz.qpic.cn/mmbiz_png/ABIWtj6YasRokhQ5FrqQz2xDzQOudWUvC1vScTZyyqLseEMLOGkozCWxibtPic3BibjsLBLA8apYUOboiaON3kK8FQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 884 | 885 | 2、Collections 是一个包装类。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于 Java 的 Collection 框架。 886 | 887 | ![Collection 类](https://mmbiz.qpic.cn/mmbiz_png/ABIWtj6YasRokhQ5FrqQz2xDzQOudWUvprFQbEUpq4ia6fZ7O5UibPFugZ1bA0H3dp1kbmUtsiarU2vic9qhjeFvlw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 888 | 889 | Collections 是一个包装类,Collection 表示一组对象,这些对象也成为 Collection 的元素。一些 Collection 允许有重复的元素,而另一些则不允许,一些 Collection 是有序的,而另一些则是无需的。 890 | 891 | ![Collections 各个集合实现图](https://mmbiz.qpic.cn/mmbiz_png/ABIWtj6YasRokhQ5FrqQz2xDzQOudWUvQ4jbCFkdhgbBWXIiaJAZxsg5wVZO8fiagApbhLGicRriaEoxIC0cHez26A/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 892 | 893 | 894 | ## JVM与调优21题 895 | 896 | ###

1、Java 类加载过程?

897 | 898 | 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现这个类的初始化。 899 | 900 | **1、加载** 901 | 902 | 加载,是指Java虚拟机查找字节流(查找.class文件),并且根据字节流创建java.lang.Class对象的过程。 903 | 904 | 这个过程,将类的.class文件中的二进制数据读入内存,放在运行时区域的方法区内。 905 | 906 | 然后在堆中创建java.lang.Class对象,用来封装类在方法区的数据结构。 907 | 908 | 类加载阶段: 909 | 910 | (1)Java虚拟机将.class文件读入内存,并为之创建一个Class对象。 911 | 912 | (2)任何类被使用时系统都会为其创建一个且仅有一个Class对象。 913 | 914 | (3)这个Class对象描述了这个类创建出来的对象的所有信息,比如有哪些构造方法,都有哪些成员方法,都有哪些成员变量等。 915 | 916 | Student类加载过程图示: 917 | 918 | ![Student类加载过程图示](https://mmbiz.qpic.cn/mmbiz_png/ABIWtj6YasSVzKjWxXibrakcTGAI1ONgpKjYzVzic18bCs3Hdh4IXWT3v9EN7qUHFWK8orBxbdn2nbbkE134iaTQQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 919 | 920 | **2、链接** 921 | 922 | 链接包括验证、准备以及解析三个阶段。 923 | 924 | (1)验证阶段。主要的目的是确保被加载的类(.class文件的字节流)满足Java虚拟机规范,不会造成安全错误。 925 | 926 | (2)准备阶段。负责为类的静态成员分配内存,并设置默认初始值。 927 | 928 | (3)解析阶段。将类的二进制数据中的符号引用替换为直接引用。 929 | 930 | 说明: 931 | 932 | 符号引用。即一个字符串,但是这个字符串给出了一些能够唯一性识别一个方法,一个变量,一个类的相关信息。 933 | 934 | 直接引用。可以理解为一个内存地址,或者一个偏移量。比如类方法,类变量的直接引用是指向方法区的指针;而实例方法,实例变量的直接引用则是从实例的头指针开始算起到这个实例变量位置的偏移量。 935 | 936 | 举个例子来说,现在调用方法hello(),这个方法的地址是0xaabbccdd,那么hello就是符号引用,0xaabbccdd就是直接引用。 937 | 938 | 在解析阶段,虚拟机会把所有的类名,方法名,字段名这些符号引用替换为具体的内存地址或偏移量,也就是直接引用。 939 | 940 | **3、初始化** 941 | 942 | 初始化,则是为标记为常量值的字段赋值的过程。换句话说,只对static修饰的变量或语句块进行初始化。 943 | 944 | 如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。 945 | 946 | 如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行。 947 | 948 | 949 | ###

2、描述一下 JVM 加载 Class 文件的原理机制?

950 | 951 | Java中的所有类,都需要由类加载器装载到JVM中才能运行。 952 | 953 | 类加载器本身也是一个类,而它的工作就是把class文件从硬盘读取到内存中。 954 | 955 | 在写程序的时候,我们几乎不需要关心类的加载,因为这些都是隐式装载的,除非我们有特殊的用法,像是反射,就需要显式的加载所需要的类。 956 | 957 | 类装载方式,有两种: 958 | 959 | 1、隐式装载, 程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中; 960 | 961 | 2、显式装载, 通过class.forname()等方法,显式加载需要的类。 962 | 963 | Java类的加载是动态的,它并不会一次性将所有类全部加载后再运行,而是保证程序运行的基础类(像是基类)完全加载到jvm中,至于其他类,则在需要的时候才加载。这当然就是为了节省内存开销。 964 | 965 | Java的类加载器有三个,对应Java的三种类: 966 | 967 | Bootstrap Loader:启动类加载器,是虚拟机自身的一部分。负责将存放在\lib目录中的类库加载到虚拟机中。其无法被Java程序直接引用。 负责加载系统类 (指的是内置类,像是String,对应于C#中的System类和C/C++标准库中的类) 968 | 969 | ExtClassLoader: 负责加载扩展类(就是继承类和实现类) 970 | 971 | AppClassLoader:负责加载用户类路径(ClassPath)上所指定的类库(程序员自定义的类) 972 | 973 | JVM中,类的加载是由类加载器(ClassLoader)和它的子类来实现的,Java中的类加载器是一个重要的Java运行时系统组件,它负责在运行时查找和装入类文件中的类。 974 | 975 | 由于Java的跨平台性,经过编译的Java源程序并不是一个可执行程序,而是一个或多个类文件。 976 | 977 | 当Java程序需要使用某个类时,JVM会确保这个类已经被加载、连接(验证、准备和解析)和初始化。 978 | 979 | 类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的Class对象。加载完成后,Class对象还不完整,所以此时的类还不可用。 980 | 981 | 当类被加载后就进入连接阶段,这一阶段包括: 982 | 983 | 验证:为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。 984 | 985 | 准备:为静态变量分配内存并设置默认的初始值。 986 | 987 | 解析:将符号引用替换为直接引用。 988 | 989 | 最后JVM对类进行初始化,包括:1)如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;2)如果类中存在初始化语句,就依次执行这些初始化语句。 990 | 991 | 类的加载是由类加载器完成的,类加载器包括:启动类加载器(BootStrap)、扩展类加载器(Extension)、应用程序类加载器(Application)。       992 | 993 | 从Java 2(JDK 1.2)开始,类加载过程采取了双亲委派模型(PDM)。 994 | 995 | PDM更好的保证了Java平台的安全性,在该机制中,JVM自带的Bootstrap是启动类加载器,其他的加载器都有且仅有一个父类加载器。 996 | 997 | 类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类加载器自行加载。 998 | 999 | JVM不会向Java程序提供对Bootstrap的引用。 1000 | 1001 | 双亲委派模型:要求除了顶层的启动类加载器外,其余加载器都应当有自己的父类加载器。类加载器之间的父子关系,一般不会以继承的关系来实现,而是通过组合关系复用父加载器的代码。 1002 | 1003 | 工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。 1004 | 1005 | 每个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有到父加载器反馈自己无法完成这个加载请求(它的搜索范围没有找到所需的类)时,子加载器才会尝试自己去加载。 1006 | 1007 | 为什么要使用:Java类随着它的类加载器一起具备了一种带优先级的层次关系。 1008 | 1009 | 比如java.lang.Object,它存放在rt.jar中,无论哪个类加载器要加载这个类,最终都是委派给启动类加载器进行加载,因此Object类在程序的各个类加载器环境中,都是同一个类。 1010 | 1011 | 自己编写一个与rt.jar类库中已有类重名的java类,可以正常编译,但无法被加载运行。 1012 | 1013 | 1014 | 1015 | ###

3、Java内存分配

1016 | 1017 | **Java 内存分配主要包括以下几个区域** 1018 | 1019 | 1. 寄存器:我们在程序中无法控制 1020 | 1021 | 2. 栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中 1022 | 1023 | 3. 堆:存放用 new 产生的数据 1024 | 1025 | 4. 静态域:存放在对象中用 static 定义的静态成员 1026 | 1027 | 5. 常量池:存放常量 1028 | 1029 | 6. 非 RAM (随机存取存储器)存储:硬盘等永久存储空间 1030 | 1031 | **Java 内存分配中的栈** 1032 | 1033 | 在函数中定义的一些**基本类型的变量数据**和**对象的引用变量**都在函数的栈内存中分配。 1034 | 1035 | 当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当该变量退出该作用域后,Java 会自动释放掉为该变量锁分配的内存空间,该内存空间可以立即被另作他用。 1036 | 1037 | **Java 内存分配中的堆** 1038 | 1039 | 堆内存用来存放由 **new 创建的对象和数组**。在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理。 1040 | 1041 | 在堆中产生了一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。 1042 | 1043 | 引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。 1044 | 1045 | **常量池(constant pool)** 1046 | 1047 | 常量池指的是在编译器被确定,并被保存在已编译的 .class 文件中的一些数据。 1048 | 1049 | 除了包含代码中所定义的各种**基本类型(如 int、long 等等)**和对象(**如 String 及数组**)的**常量值(final)**还包含一些以文本形式出现的符号引用,比如: 1050 | 1051 | 1. 类和接口的全限定名; 1052 | 1053 | 2. 字段的名称和描述符; 1054 | 1055 | 3. 方法的名称和描述符。 1056 | 1057 | 虚拟机必须为每个被装载的类型维护一个常量池。常量池就是该类型所用到长量的一个有序集合,包括直接常量(string,integer 和 floationg point 常量)和对其他类型,字段和方法的符号引用。 1058 | 1059 | 1060 | 1061 | ###

4、GC 是什么?为什么要有 GC ?

1062 | 1063 | **GC 是垃圾收集的意思**,内存处理是编程人员容易出问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java 提供的 GC功能可以自动检测对象是否超过作用域从而达到自动回收内存的目的。 1064 | 1065 | Java 语言没有提供释放已分配内存的显示操作方法。Java 程序员不用担心内存管理,因为垃圾收集器会自动进行管理。 1066 | 1067 | 要请求垃圾收集,可以调用下面的方法之一:**System.gc()** 或 **Runtime.getRuntime().gc()** ,但JVM可以屏蔽掉显示的垃圾回收调用。 1068 | 1069 | **垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。** 1070 | 1071 | 垃圾回收器**通常是作为一个单独的低优先级的线程运行,不可预知的情况下**对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员**不能实时的**调用垃圾回收器对某个对象或所有对象进行垃圾回收。 1072 | 1073 | 在 Java 诞生初期,垃圾回收是 Java 最大的亮点之一,因为服务器端的编程需要有效的防止内存泄露问题,然而时过境迁,如今 Java 的垃圾回收机制已经成为被诟病的东西。 1074 | 1075 | 移动智能终端用户通常觉得 iOS 的系统比 Android 系统有更好的用户体验,其中一个深层次的原因就在于 Android 系统中垃圾回收的不可预知性。 1076 | 1077 | 1078 | 1079 | -------------------------------------------------------------------------------- /面试题/【美团】Java 岗 154 道面试题(2.0版).md: -------------------------------------------------------------------------------- 1 | # 目录 2 | 3 | ## Java集合22题 4 | 5 | 1. ArrayList 和 Vector 的区别。 6 | 7 | 2. 说说 ArrayList,Vector,LinkedList 的存储性能和特性。 8 | 9 | 3. 快速失败 (fail-fast) 和安全失败 (fail-safe) 的区别是什么? 10 | 11 | 4. HashMap 的数据结构。 12 | 13 | 5. HashMap 的工作原理是什么? 14 | 15 | 6. Hashmap 什么时候进行扩容呢? 16 | 17 | 7. List、Map、Set 三个接口,存取元素时,各有什么特点? 18 | 19 | 8. Set 里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用 == 还是 equals()? 它们有何区别? 20 | 21 | 9. 两个对象值相同 (x.equals(y) == true),但却可有不同的 hash code,这句话对不对? 22 | 23 | 10. Heap 和 Stack 有什么区别。 24 | 25 | 11. Java 集合类框架的基本接口有哪些? 26 | 27 | 12. HashSet 和 TreeSet 有什么区别? 28 | 29 | 13. HashSet 的底层实现是什么? 30 | 31 | 14. LinkedHashMap 的实现原理? 32 | 33 | 15. 为什么集合类没有实现 Cloneable 和 Serializable 接口? 34 | 35 | 16. 什么是迭代器 (Iterator)? 36 | 37 | 17. Iterator 和 ListIterator 的区别是什么? 38 | 39 | 18. 数组 (Array) 和列表 (ArrayList) 有什么区别?什么时候应该使用 Array 而不是 ArrayList? 40 | 41 | 19. Java 集合类框架的最佳实践有哪些? 42 | 43 | 20. Set 里的元素是不能重复的,那么用什么方法来区分重复与否呢?是用 == 还是 equals()?它们有何区别? 44 | 45 | 21. Comparable 和 Comparator 接口是干什么的?列出它们的区别。 46 | 47 | 22. Collection 和 Collections 的区别。 48 | 49 | 50 | ## JVM与调优21题 51 | 52 | 1. Java 类加载过程? 53 | 54 | 2. 描述一下 JVM 加载 Class 文件的原理机制? 55 | 56 | 3. Java 内存分配。 57 | 58 | 4. GC 是什么? 为什么要有 GC? 59 | 60 | 5. 简述 Java 垃圾回收机制 61 | 62 | 6. 如何判断一个对象是否存活?(或者 GC 对象的判定方法) 63 | 64 | 7. 垃圾回收的优点和原理。并考虑 2 种回收机制 65 | 66 | 8. 垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收? 67 | 68 | 9. Java 中会存在内存泄漏吗,请简单描述 69 | 70 | 10. 深拷贝和浅拷贝。 71 | 72 | 11. System.gc() 和 Runtime.gc() 会做什么事情? 73 | 74 | 12. finalize() 方法什么时候被调用?析构函数 (finalization) 的目的是什么? 75 | 76 | 13. 如果对象的引用被置为 null,垃圾收集器是否会立即释放对象占用的内存? 77 | 78 | 14. 什么是分布式垃圾回收(DGC)?它是如何工作的? 79 | 80 | 15. 串行(serial)收集器和吞吐量(throughput)收集器的区别是什么? 81 | 82 | 16. 在 Java 中,对象什么时候可以被垃圾回收? 83 | 84 | 17. 简述 Java 内存分配与回收策率以及 Minor GC 和 Major GC。 85 | 86 | 18. JVM 的永久代中会发生垃圾回收么? 87 | 88 | 19. Java 中垃圾收集的方法有哪些? 89 | 90 | 20. 什么是类加载器,类加载器有哪些? 91 | 92 | 21. 类加载器双亲委派模型机制? 93 | 94 | 95 | ## 并发编程28题 96 | 97 | 1. Synchronized 用过吗,其原理是什么? 98 | 99 | 2. 你刚才提到获取对象的锁,这个“锁”到底是什么?如何确定对象的锁? 100 | 101 | 3. 什么是可重入性,为什么说 Synchronized 是可重入锁? 102 | 103 | 4. JVM 对 Java 的原生锁做了哪些优化? 104 | 105 | 5. 为什么说 Synchronized 是非公平锁? 106 | 107 | 6. 什么是锁消除和锁粗化? 108 | 109 | 7. 为什么说 Synchronized 是一个悲观锁?乐观锁的实现原理又是什么?什么是 CAS,它有什么特性? 110 | 111 | 8. 乐观锁一定就是好的吗? 112 | 113 | 9. 跟 Synchronized 相比,可重入锁 ReentrantLock 其实现原理有什么不同? 114 | 115 | 10. 那么请谈谈 AQS 框架是怎么回事儿? 116 | 117 | 11. 请尽可能详尽地对比下 Synchronized 和 ReentrantLock 的异同。 118 | 119 | 12. ReentrantLock 是如何实现可重入性的? 120 | 121 | 13. 除了 ReetrantLock,你还接触过 JUC 中的哪些并发工具? 122 | 123 | 14. 请谈谈 ReadWriteLock 和 StampedLock。 124 | 125 | 15. 如何让 Java 的线程彼此同步?你了解过哪些同步器?请分别介绍下。 126 | 127 | 16. CyclicBarrier 和 CountDownLatch 看起来很相似,请对比下呢? 128 | 129 | 17. Java 线程池相关问题 130 | 131 | 18. Java 中的线程池是如何实现的? 132 | 133 | 19. 创建线程池的几个核心构造参数? 134 | 135 | 20. 线程池中的线程是怎么创建的?是一开始就随着线程池的启动创建好的吗? 136 | 137 | 21. 既然提到可以通过配置不同参数创建出不同的线程池,那么 Java 中默认实现好的线程池又有哪些呢?请比较它们的异同 138 | 139 | 22. 如何在 Java 线程池中提交线程? 140 | 141 | 23. 什么是 Java 的内存模型,Java 中各个线程是怎么彼此看到对方的变量的? 142 | 143 | 24. 请谈谈 volatile 有什么特点,为什么它能保证变量对所有线程的可见性? 144 | 145 | 25. 既然 volatile 能够保证线程间的变量可见性,是不是就意味着基于 volatile 变量的运算就是并发安全的? 146 | 147 | 26. 请对比下 volatile 对比 Synchronized 的异同。 148 | 149 | 27. 请谈谈 ThreadLocal 是怎么解决并发安全的? 150 | 151 | 28. 很多人都说要慎用 ThreadLocal,谈谈你的理解,使用 ThreadLocal 需要注意些什么? 152 | 153 | 154 | ## Spring 25题 155 | 156 | 1. 什么是 Spring 框架?Spring 框架有哪些主要模块? 157 | 158 | 2. 使用 Spring 框架能带来哪些好处? 159 | 160 | 3. 什么是控制反转(IOC)?什么是依赖注入? 161 | 162 | 4. 请解释下 Spring 框架中的 IoC? 163 | 164 | 5. BeanFactory 和 ApplicationContext 有什么区别? 165 | 166 | 6. Spring 有几种配置方式? 167 | 168 | 7. 如何用基于 XML 配置的方式配置 Spring? 169 | 170 | 8. 如何用基于 Java 配置的方式配置 Spring? 171 | 172 | 9. 怎样用注解的方式配置 Spring? 173 | 174 | 10. 请解释 Spring Bean 的生命周期? 175 | 176 | 11. Spring Bean 的作用域之间有什么区别? 177 | 178 | 12. 什么是 Spring inner beans? 179 | 180 | 13. Spring 框架中的单例 Beans 是线程安全的么? 181 | 182 | 14. 请举例说明如何在 Spring 中注入一个 Java Collection? 183 | 184 | 15. 如何向 Spring Bean 中注入一个 Java.util.Properties? 185 | 186 | 16. 请解释 Spring Bean 的自动装配? 187 | 188 | 17. 请解释自动装配模式的区别? 189 | 190 | 18. 如何开启基于注解的自动装配? 191 | 192 | 19. 请举例解释@Required 注解? 193 | 194 | 20. 请举例解释@Autowired 注解? 195 | 196 | 21. 请举例说明@Qualifier 注解? 197 | 198 | 22. 构造方法注入和设值注入有什么区别? 199 | 200 | 23. Spring 框架中有哪些不同类型的事件? 201 | 202 | 24. FileSystemResource 和 ClassPathResource 有何区别? 203 | 204 | 25. Spring 框架中都用到了哪些设计模式? 205 | 206 | 207 | ## 设计模式 10题 208 | 209 | 1. 请列举出在 JDK 中几个常用的设计模式? 210 | 211 | 2. 什么是设计模式?你是否在你的代码里面使用过任何设计模式? 212 | 213 | 3. Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式 214 | 215 | 4. 在 Java 中,什么叫观察者设计模式(observer design pattern)? 216 | 217 | 5. 使用工厂模式最主要的好处是什么?在哪里使用? 218 | 219 | 6. 举一个用 Java 实现的装饰模式(decorator design pattern)?它是作用于对象层次还是类 220 | 221 | 层次? 222 | 223 | 7. 在 Java 中,为什么不允许从静态方法中访问非静态变量? 224 | 225 | 8. 设计一个 ATM 机,请说出你的设计思路? 226 | 227 | 9. 在 Java 中,什么时候用重载,什么时候用重写? 228 | 229 | 10. 举例说明什么情况下会更倾向于使用抽象类而不是接口 230 | 231 | 232 | ## SpringBoot 22题 233 | 234 | 1. 什么是 Spring Boot? 235 | 236 | 2. Spring Boot 有哪些优点? 237 | 238 | 3. 什么是 JavaConfig? 239 | 240 | 4. 如何重新加载 Spring Boot 上的更改,而无需重新启动服务器? 241 | 242 | 5. Spring Boot 中的监视器是什么? 243 | 244 | 6. 如何在 Spring Boot 中禁用 Actuator 端点安全性? 245 | 246 | 7. 如何在自定义端口上运行 Spring Boot 应用程序? 247 | 248 | 8. 什么是 YAML? 249 | 250 | 9. 如何实现 Spring Boot 应用程序的安全性? 251 | 252 | 10. 如何集成 Spring Boot 和 ActiveMQ? 253 | 254 | 11. 如何使用 Spring Boot 实现分页和排序? 255 | 256 | 12. 什么是 Swagger?你用 Spring Boot 实现了它吗? 257 | 258 | 13. 什么是 Spring Profiles? 259 | 260 | 14. 什么是 Spring Batch? 261 | 262 | 15. 什么是 FreeMarker 模板? 263 | 264 | 16. 如何使用 Spring Boot 实现异常处理? 265 | 266 | 17. 您使用了哪些 starter maven 依赖项? 267 | 268 | 18. 什么是 CSRF 攻击? 269 | 270 | 19. 什么是 WebSockets? 271 | 272 | 20. 什么是 AOP? 273 | 274 | 21. 什么是 Apache Kafka? 275 | 276 | 22. 我们如何监视所有 Spring Boot 微服务? 277 | 278 | 279 | ## Netty10题 280 | 281 | 1. BIO、NIO和AIO的区别? 282 | 283 | 2. NIO的组成? 284 | 285 | 3. Netty的特点? 286 | 287 | 4. Netty的线程模型? 288 | 289 | 5. TCP 粘包/拆包的原因及解决方法? 290 | 291 | 6. 了解哪几种序列化协议? 292 | 293 | 7. 如何选择序列化协议? 294 | 295 | 8. Netty的零拷贝实现? 296 | 297 | 9. Netty的高性能表现在哪些方面? 298 | 299 | 10. NIOEventLoopGroup源码? 300 | 301 | 302 | ## Redis 16题 303 | 304 | 1. 什么是redis? 305 | 306 | 2. Reids的特点 307 | 308 | 4. Redis支持的数据类型 309 | 310 | 5. Redis是单进程单线程的 311 | 312 | 6. 虚拟内存 313 | 314 | 7. Redis锁 315 | 316 | 8. 读写分离模型 317 | 318 | 9. 数据分片模型 319 | 320 | 10. Redis的回收策略 321 | 322 | 11. 使用Redis有哪些好处? 323 | 324 | 12. redis相比memcached有哪些优势? 325 | 326 | 13. redis常见性能问题和解决方案 327 | 328 | 14. MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据245 329 | 330 | 15. Memcache与Redis的区别都有哪些? 331 | 332 | 16. Redis 常见的性能问题都有哪些?如何解决? 333 | 334 | 17. Redis 最适合的场景 335 | -------------------------------------------------------------------------------- /面试题/面试经历汇总.md: -------------------------------------------------------------------------------- 1 | ## 本文主要收集大家的面试经历 2 | ### 五轮阿里面试题及答案 3 | --------------------------------------------------------------------------------