diff --git a/DevelopCommonTools/GitIntroduce.md b/DevelopCommonTools/GitIntroduce.md
new file mode 100644
index 00000000000..e58f13b63aa
--- /dev/null
+++ b/DevelopCommonTools/GitIntroduce.md
@@ -0,0 +1,258 @@
+
+
+- [版本控制](#版本控制)
+ - [什么是版本控制](#什么是版本控制)
+ - [为什么要版本控制](#为什么要版本控制)
+ - [本地版本控制系统](#本地版本控制系统)
+ - [集中化的版本控制系统](#集中化的版本控制系统)
+ - [分布式版本控制系统](#分布式版本控制系统)
+- [认识 Git](#认识-git)
+ - [Git 简史](#git-简史)
+ - [Git 与其他版本管理系统的主要区别](#git-与其他版本管理系统的主要区别)
+ - [Git 的三种状态](#git-的三种状态)
+- [Git 使用快速入门](#git-使用快速入门)
+ - [获取 Git 仓库](#获取-git-仓库)
+ - [记录每次更新到仓库](#记录每次更新到仓库)
+ - [推送改动到远程仓库](#推送改动到远程仓库)
+ - [远程仓库的移除与重命名](#远程仓库的移除与重命名)
+ - [查看提交历史](#查看提交历史)
+ - [撤销操作](#撤销操作)
+ - [分支](#分支)
+- [推荐阅读](#推荐阅读)
+
+
+
+## 版本控制
+
+### 什么是版本控制
+
+版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。 除了项目源代码,你可以对任何类型的文件进行版本控制。
+
+### 为什么要版本控制
+
+有了它你就可以将某个文件回溯到之前的状态,甚至将整个项目都回退到过去某个时间点的状态,你可以比较文件的变化细节,查出最后是谁修改了哪个地方,从而找出导致怪异问题出现的原因,又是谁在何时报告了某个功能缺陷等等。
+
+### 本地版本控制系统
+
+许多人习惯用复制整个项目目录的方式来保存不同的版本,或许还会改名加上备份时间以示区别。 这么做唯一的好处就是简单,但是特别容易犯错。 有时候会混淆所在的工作目录,一不小心会写错文件或者覆盖意想外的文件。
+
+为了解决这个问题,人们很久以前就开发了许多种本地版本控制系统,大多都是采用某种简单的数据库来记录文件的历次更新差异。
+
+
+
+### 集中化的版本控制系统
+
+接下来人们又遇到一个问题,如何让在不同系统上的开发者协同工作? 于是,集中化的版本控制系统(Centralized Version Control Systems,简称 CVCS)应运而生。
+
+集中化的版本控制系统都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。
+
+
+
+这么做虽然解决了本地版本控制系统无法让在不同系统上的开发者协同工作的诟病,但也还是存在下面的问题:
+
+- **单点故障:** 中央服务器宕机,则其他人无法使用;如果中心数据库磁盘损坏有没有进行备份,你将丢失所有数据。本地版本控制系统也存在类似问题,只要整个项目的历史记录被保存在单一位置,就有丢失所有历史更新记录的风险。
+- **必须联网才能工作:** 受网络状况、带宽影响。
+
+### 分布式版本控制系统
+
+于是分布式版本控制系统(Distributed Version Control System,简称 DVCS)面世了。 Git 就是一个典型的分布式版本控制系统。
+
+这类系统,客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来。 这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。 因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份。
+
+
+
+分布式版本控制系统可以不用联网就可以工作,因为每个人的电脑上都是完整的版本库,当你修改了某个文件后,你只需要将自己的修改推送给别人就可以了。但是,在实际使用分布式版本控制系统的时候,很少会直接进行推送修改,而是使用一台充当“中央服务器”的东西。这个服务器的作用仅仅是用来方便“交换”大家的修改,没有它大家也一样干活,只是交换修改不方便而已。
+
+分布式版本控制系统的优势不单是不必联网这么简单,后面我们还会看到 Git 极其强大的分支管理等功能。
+
+## 认识 Git
+
+### Git 简史
+
+Linux 内核项目组当时使用分布式版本控制系统 BitKeeper 来管理和维护代码。但是,后来开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束,他们收回了 Linux 内核社区免费使用 BitKeeper 的权力。 Linux 开源社区(特别是 Linux 的缔造者 Linus Torvalds)基于使用 BitKeeper 时的经验教训,开发出自己的版本系统,而且对新的版本控制系统做了很多改进。
+
+### Git 与其他版本管理系统的主要区别
+
+ Git 在保存和对待各种信息的时候与其它版本控制系统有很大差异,尽管操作起来的命令形式非常相近,理解这些差异将有助于防止你使用中的困惑。
+
+下面我们主要说一个关于 Git 其他版本管理系统的主要差别:**对待数据的方式**。
+
+**Git采用的是直接记录快照的方式,而非差异比较。我后面会详细介绍这两种方式的差别。**
+
+大部分版本控制系统(CVS、Subversion、Perforce、Bazaar 等等)都是以文件变更列表的方式存储信息,这类系统**将它们保存的信息看作是一组基本文件和每个文件随时间逐步累积的差异。**
+
+具体原理如下图所示,理解起来其实很简单,每个我们对提交更新一个文件之后,系统记录都会记录这个文件做了哪些更新,以增量符号Δ(Delta)表示。
+
+
` ,比如我们要让本地的一个仓库和 Github 上创建的一个仓库关联可以这样`git remote add origin https://github.com/Snailclimb/test.git`
+- 将这些改动提交到远端仓库:`git push origin master` (可以把 *master* 换成你想要推送的任何分支)
+
+ 如此你就能够将你的改动推送到所添加的服务器上去了。
+
+### 远程仓库的移除与重命名
+
+- 将 test 重命名位 test1:`git remote rename test test1`
+- 移除远程仓库 test1:`git remote rm test1`
+
+### 查看提交历史
+
+在提交了若干更新,又或者克隆了某个项目之后,你也许想回顾下提交历史。 完成这个任务最简单而又有效的工具是 `git log` 命令。`git log` 会按提交时间列出所有的更新,最近的更新排在最上面。
+
+**可以添加一些参数来查看自己希望看到的内容:**
+
+只看某个人的提交记录:
+
+```shell
+git log --author=bob
+```
+
+### 撤销操作
+
+有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。 此时,可以运行带有 `--amend` 选项的提交命令尝试重新提交:
+
+```console
+git commit --amend
+```
+
+取消暂存的文件
+
+```console
+git reset filename
+```
+
+撤消对文件的修改:
+
+```
+git checkout -- filename
+```
+
+假如你想丢弃你在本地的所有改动与提交,可以到服务器上获取最新的版本历史,并将你本地主分支指向它:
+
+```
+git fetch origin
+git reset --hard origin/master
+```
+
+
+
+### 分支
+
+分支是用来将特性开发绝缘开来的。在你创建仓库的时候,*master* 是“默认的”分支。在其他分支上进行开发,完成后再将它们合并到主分支上。
+
+我们通常在开发新功能、修复一个紧急 bug 等等时候会选择创建分支。单分支开发好还是多分支开发好,还是要看具体场景来说。
+
+创建一个名字叫做 test 的分支
+
+```console
+git branch test
+```
+
+切换当前分支到 test(当你切换分支的时候,Git 会重置你的工作目录,使其看起来像回到了你在那个分支上最后一次提交的样子。 Git 会自动添加、删除、修改文件以确保此时你的工作目录和这个分支最后一次提交时的样子一模一样)
+
+```console
+git checkout test
+```
+
+
+

+
+
+你也可以直接这样创建分支并切换过去(上面两条命令的合写)
+
+```console
+git checkout -b feature_x
+```
+
+切换到主分支
+
+```
+git checkout master
+```
+
+合并分支(可能会有冲突)
+
+```java
+ git merge test
+```
+
+把新建的分支删掉
+
+```
+git branch -d feature_x
+```
+
+将分支推送到远端仓库(推送成功后其他人可见):
+
+```
+git push origin
+```
+
+
+
+## 推荐阅读
+
+- [Git - 简明指南](http://rogerdudler.github.io/git-guide/index.zh.html)
+- [图解Git](http://marklodato.github.io/visual-git-guide/index-zh-cn.html)
+- [猴子都能懂得Git入门](https://backlog.com/git-tutorial/cn/intro/intro1_1.html)
+- https://git-scm.com/book/en/v2
diff --git "a/EssentialContentForInterview/BATJrealInterviewExperience/5\351\235\242\351\230\277\351\207\214,\347\273\210\350\216\267offer.md" "b/EssentialContentForInterview/BATJrealInterviewExperience/5\351\235\242\351\230\277\351\207\214,\347\273\210\350\216\267offer.md"
new file mode 100644
index 00000000000..9efac14f6d4
--- /dev/null
+++ "b/EssentialContentForInterview/BATJrealInterviewExperience/5\351\235\242\351\230\277\351\207\214,\347\273\210\350\216\267offer.md"
@@ -0,0 +1,96 @@
+> 作者:ppxyn。本文来自读者投稿,同时也欢迎各位投稿,**对于不错的原创文章我根据你的选择给予现金(50-200)、付费专栏或者任选书籍进行奖励!所以,快提 pr 或者邮件的方式(邮件地址在主页)给我投稿吧!** 当然,我觉得奖励是次要的,最重要的是你可以从自己整理知识点的过程中学习到很多知识。
+
+**目录**
+
+
+
+- [前言](#前言)
+- [一面\(技术面\)](#一面技术面)
+- [二面\(技术面\)](#二面技术面)
+- [三面\(技术面\)](#三面技术面)
+- [四面\(半个技术面\)](#四面半个技术面)
+- [五面\(HR面\)](#五面hr面)
+- [总结](#总结)
+
+
+
+### 前言
+
+在接触 Java 之前我接触的比较多的是硬件方面,用的比较多的语言就是C和C++。到了大三我才正式选择 Java 方向,到目前为止使用Java到现在大概有一年多的时间,所以Java算不上很好。刚开始投递的时候,实习刚辞职,也没准备笔试面试,很多东西都忘记了。所以,刚开始我并没有直接就投递阿里,毕竟心里还是有一点点小害怕的。于是,我就先投递了几个不算大的公司来练手,就是想着刷刷经验而已或者说是练练手(ps:还是挺对不起那些公司的)。面了一个月其他公司后,我找了我实验室的学长内推我,后面就有了这5次面试。
+
+下面简单的说一下我的这5次面试:4次技术面+1次HR面,希望我的经历能对你有所帮助。
+
+### 一面(技术面)
+
+1. 自我介绍(主要讲自己会的技术细节,项目经验,经历那些就一语带过,后面面试官会问你的)。
+2. 聊聊项目(就是一个很普通的分布式商城,自己做了一些改进),让我画了整个项目的架构图,然后针对项目抛了一系列的提高性能的问题,还问了我做项目的过程中遇到了那些问题,如何解决的,差不读就这些吧。
+3. 可能是我前面说了我会数据库优化,然后面试官就开始问索引、事务隔离级别、悲观锁和乐观锁、索引、ACID、MVVC这些问题。
+4. 浏览器输入URL发生了什么? TCP和UDP区别? TCP如何保证传输可靠性?
+5. 讲下跳表怎么实现的?哈夫曼编码是怎么回事?非递归且不用额外空间(不用栈),如何遍历二叉树
+6. 后面又问了很多JVM方面的问题,比如Java内存模型、常见的垃圾回收器、双亲委派模型这些
+7. 你有什么问题要问吗?
+
+### 二面(技术面)
+
+1. 自我介绍(主要讲自己会的技术细节,项目经验,经历那些就一语带过,后面面试官会问你的)。
+2. 操作系统的内存管理机制
+3. 进程和线程的区别
+4. 说下你对线程安全的理解
+5. volatile 有什么作用 ,sychronized和lock有什么区别
+6. ReentrantLock实现原理
+7. 用过CountDownLatch么?什么场景下用的?
+8. AQS底层原理。
+9. 造成死锁的原因有哪些,如何预防?
+10. 加锁会带来哪些性能问题。如何解决?
+11. HashMap、ConcurrentHashMap源码。HashMap是线程安全的吗?Hashtable呢?ConcurrentHashMap有了解吗?
+12. 是否可以实习?
+13. 你有什么问题要问吗?
+
+### 三面(技术面)
+
+1. 有没有参加过 ACM 或者他竞赛,有没有拿过什么奖?( 我说我没参加过ACM,本科参加过数学建模竞赛,名次并不好,没拿过什么奖。面试官好像有点失望,然后我又赶紧补充说我和老师一起做过一个项目,目前已经投入使用。面试官还比较感兴趣,后面又和他聊了一下这个项目。)
+2. 研究生期间,做过什么项目,发过论文吗?有什么成果吗?
+3. 你觉得你有什么优点和缺点?你觉得你相比于那些比你更优秀的人欠缺什么?
+4. 有读过什么源码吗?(我说我读过 Java 集合框架和 Netty 的,面试官说 Java 集合前几面一定问的差不多,就不问了,然后就问我 Netty的,我当时很慌啊!)
+5. 介绍一下自己对 Netty 的认识,为什么要用。说说业务中,Netty 的使用场景。什么是TCP 粘包/拆包,解决办法。Netty线程模型。Dubbo 在使用 Netty 作为网络通讯时候是如何避免粘包与半包问题?讲讲Netty的零拷贝?巴拉巴拉问了好多,我记得有好几个我都没回答上来,心里想着凉凉了啊。
+6. 用到了那些开源技术、在开源领域做过贡献吗?
+7. 常见的排序算法及其复杂度,现场写了快排。
+8. 红黑树,B树的一些问题。
+9. 讲讲算法及数据结构在实习项目中的用处。
+10. 自己的未来规划(就简单描述了一下自己未来的设想啊,说的还挺诚恳,面试官好像还挺满意的)
+11. 你有什么问题要问吗?
+
+### 四面(半个技术面)
+
+三面面完当天,晚上9点接到面试电话,感觉像是部门或者项目主管。 这个和之前的面试不大相同,感觉面试官主要考察的是你解决问题的能力、学习能力和团队协作能力。
+
+1. 让我讲一个自己觉得最不错的项目。然后就巴拉巴拉的聊,我记得主要是问了项目是如何进行协作的、遇到问题是如何解决的、与他人发生冲突是如何解决的这些。感觉聊了挺久。
+2. 出现 OOM 后你会怎么排查问题?
+3. 自己平时是如何学习新技术的?除了 Java 还回去了解其他技术吗?
+4. 上一段实习经历的收获。
+5. NginX如何做负载均衡、常见的负载均衡算法有哪些、一致性哈希的一致性是什么意思、一致性哈希是如何做哈希的
+6. 你有什么问题问我吗?
+7. 还有一些其他的,想不起来了,感觉这一面不是偏向技术来问。
+
+## 五面(HR面)
+
+1. 自我介绍(主要讲能突出自己的经历,会的编程技术一语带过)。
+2. 你觉得你有什么优点和缺点?如何克服这些缺点?
+3. 说一件大学里你自己比较有成就感的一件事情,为此付出了那些努力。
+4. 你前面跟其他面试官讲过一些你做的项目吧?可以给我讲讲吗?你要考虑到我不是一个做技术的人,怎么让我也听得懂。项目中有什么问题,你怎么解决的?你最大的收获是什么?
+5. 你目前有面试过其他公司吗?如果让你选,这些公司和阿里,你选哪个?(送分题,回答不好可能送命)
+6. 你期望的工作地点是哪里?
+7. 你有什么问题吗?
+
+### 总结
+
+1. 可以看出面试官问我的很多问题都是比较常见的问题,所以记得一定要提前准备,还要深入准备,不要回答的太皮毛。很多时候一个问题可能会牵扯出很多问题,遇到不会的问题不要慌,冷静分析,如果你真的回答不上来,也不要担心自己是不是就要挂了,很可能这个问题本身就比较难。
+2. 表达能力和沟通能力太重要了,一定要提前练一下,我自身就是一个不太会说话的人,所以,面试前我对于自我介绍、项目介绍和一些常见问题都在脑子里练了好久,确保面试的时候能够很清晰和简洁的说出来。
+3. 等待面试的过程和面试的过程真的好熬人,那段时间我压力也比较大,好在我私下找到学长聊了很多,心情也好了很多。
+4. 面试之后及时总结,面的好的话,不要得意,尽快准备下一场面试吧!
+
+我觉得我还算是比较幸运的,最后也祝大家都能获得心仪的Offer。
+
+
+
+
diff --git "a/\351\235\242\350\257\225\345\277\205\345\244\207/\346\234\200\346\234\200\346\234\200\345\270\270\350\247\201\347\232\204Java\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223/\347\254\254\344\270\200\345\221\250\357\274\2102018-8-7\357\274\211.md" "b/EssentialContentForInterview/MostCommonJavaInterviewQuestions/\347\254\254\344\270\200\345\221\250\357\274\2102018-8-7\357\274\211.md"
similarity index 55%
rename from "\351\235\242\350\257\225\345\277\205\345\244\207/\346\234\200\346\234\200\346\234\200\345\270\270\350\247\201\347\232\204Java\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223/\347\254\254\344\270\200\345\221\250\357\274\2102018-8-7\357\274\211.md"
rename to "EssentialContentForInterview/MostCommonJavaInterviewQuestions/\347\254\254\344\270\200\345\221\250\357\274\2102018-8-7\357\274\211.md"
index 6214a716cb8..4ca58dbfff6 100644
--- "a/\351\235\242\350\257\225\345\277\205\345\244\207/\346\234\200\346\234\200\346\234\200\345\270\270\350\247\201\347\232\204Java\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223/\347\254\254\344\270\200\345\221\250\357\274\2102018-8-7\357\274\211.md"
+++ "b/EssentialContentForInterview/MostCommonJavaInterviewQuestions/\347\254\254\344\270\200\345\221\250\357\274\2102018-8-7\357\274\211.md"
@@ -1,20 +1,16 @@
-## 一 Java中的值传递和引用传递(非常重要)
+## 一 为什么 Java 中只有值传递?
-**首先要明确的是:“对象传递(数组、类、接口)是引用传递,原始类型数据(整型、浮点型、字符型、布尔型)传递是值传递。”**
-### 那么什么是值传递和应用传递呢?
+首先回顾一下在程序设计语言中有关将参数传递给方法(或函数)的一些专业术语。**按值调用(call by value)表示方法接收的是调用者提供的值,而按引用调用(call by reference)表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。** 它用来描述各种程序设计语言(不只是Java)中方法参数传递方式。
-**值传递**是指对象被值传递,意味着传递了对象的一个副本,即使副本被改变,也不会影响源对象。(因为值传递的时候,实际上是将实参的值复制一份给形参。)
+**Java程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。**
-**引用传递**是指对象被引用传递,意味着传递的并不是实际的对象,而是对象的引用。因此,外部对引用对象的改变会反映到所有的对象上。(因为引用传递的时候,实际上是将实参的地址值复制一份给形参。)
+**下面通过 3 个例子来给大家说明**
-有时候面试官不是单纯问你“Java中是值传递还是引用传递”是什么啊,骚年?而是给出一个例子,然后让你写出答案,这种也常见在笔试题目中!所以,非常重要了,请看下面的例子:
+### example 1
-### 值传递和应用传递实例
-
-#### 1. 值传递
```java
public static void main(String[] args) {
@@ -48,157 +44,116 @@ num2 = 20
**解析:**
-在swap方法中,a、b的值进行交换,并不会影响到num1、num2。因为,a、b中的值,只是从num1、num2的复制过来的。
-也就是说,a、b相当于num1、num2的副本,副本的内容无论怎么修改,都不会影响到原件本身。
-
-#### 2. 引用传递
-
-```java
-public static void main(String[] args) {
- int[] arr = {1,2,3,4,5};
-
- change(arr);
-
- System.out.println(arr[0]);
-}
-
-public static void change(int[] array) {
-//将数组的第一个元素变为0
- array[0] = 0;
-}
-```
-
-**结果:**
-
-```
-1
-0
-```
+
-**解析:**
+在swap方法中,a、b的值进行交换,并不会影响到 num1、num2。因为,a、b中的值,只是从 num1、num2 的复制过来的。也就是说,a、b相当于num1、num2 的副本,副本的内容无论怎么修改,都不会影响到原件本身。
-无论是主函数,还是change方法,操作的都是同一个地址值对应的数组。 。因此,外部对引用对象的改变会反映到所有的对象上。
+**通过上面例子,我们已经知道了一个方法不能修改一个基本数据类型的参数,而对象引用作为参数就不一样,请看 example2.**
-### 一些特殊的例子
-#### 1. StringBuffer类型传递
+### example 2
```java
- // 测试引用传递:StringBuffer
- @org.junit.Test
- public void method1() {
- StringBuffer str = new StringBuffer("公众号:Java面试通关手册");
- System.out.println(str);
- change1(str);
- System.out.println(str);
+ public static void main(String[] args) {
+ int[] arr = { 1, 2, 3, 4, 5 };
+ System.out.println(arr[0]);
+ change(arr);
+ System.out.println(arr[0]);
}
- public static void change1(StringBuffer str) {
- str = new StringBuffer("abc");//输出:“公众号:Java面试通关手册”
- //str.append("欢迎大家关注");//输出:公众号:Java面试通关手册欢迎大家关注
- //str.insert(3, "(编程)");//输出:公众号(编程):Java面试通关手册
-
+ public static void change(int[] array) {
+ // 将数组的第一个元素变为0
+ array[0] = 0;
}
```
**结果:**
```
-公众号:Java面试通关手册
-公众号:Java面试通关手册
+1
+0
```
**解析:**
+
-很多要这个时候要问了:StringBuffer创建的明明也是对象,那为什么输出结果依然是原来的值呢?
-
-因为在`change1`方法内部我们是新建了一个StringBuffer对象,所以`str`指向了另外一个地址,相应的操作也同样是指向另外的地址的。
+array 被初始化 arr 的拷贝也就是一个对象的引用,也就是说 array 和 arr 指向的时同一个数组对象。 因此,外部对引用对象的改变会反映到所对应的对象上。
-那么,如果将`change1`方法改成如下图所示,想必大家应该知道输出什么了,如果你还不知道,那可能就是我讲的有问题了,我反思(开个玩笑,上面程序中已经给出答案):
-```
- public static void change1(StringBuffer str) {
+**通过 example2 我们已经看到,实现一个改变对象参数状态的方法并不是一件难事。理由很简单,方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。**
- str.append("欢迎大家关注");
- str.insert(3, "(编程)");
-
- }
-```
+**很多程序设计语言(特别是,C++和Pascal)提供了两种参数传递的方式:值调用和引用调用。有些程序员(甚至本书的作者)认为Java程序设计语言对对象采用的是引用调用,实际上,这种理解是不对的。由于这种误解具有一定的普遍性,所以下面给出一个反例来详细地阐述一下这个问题。**
-#### 2. String类型传递
+### example 3
```java
- // 测试引用传递:Sring
- @org.junit.Test
- public void method2() {
- String str = new String("公众号:Java面试通关手册");
- System.out.println(str);
- change2(str);
- System.out.println(str);
- }
+public class Test {
- public static void change2(String str) {
- // str="abc"; //输出:公众号:Java面试通关手册
- str = new String("abc"); //输出:公众号:Java面试通关手册
+ public static void main(String[] args) {
+ // TODO Auto-generated method stub
+ Student s1 = new Student("小张");
+ Student s2 = new Student("小李");
+ Test.swap(s1, s2);
+ System.out.println("s1:" + s1.getName());
+ System.out.println("s2:" + s2.getName());
}
+ public static void swap(Student x, Student y) {
+ Student temp = x;
+ x = y;
+ y = temp;
+ System.out.println("x:" + x.getName());
+ System.out.println("y:" + y.getName());
+ }
+}
```
**结果:**
```
-公众号:Java面试通关手册
-公众号:Java面试通关手册
+x:小李
+y:小张
+s1:小张
+s2:小李
```
-可以看到不论是执行`str="abc;"`还是`str = new String("abc");`str的输出的值都不变。
-按照我们上面讲“StringBuffer类型传递”的时候说的,`str="abc;"`应该会让str的输出的值都不变。为什么呢?因为String在创建之后是不可变的。
+**解析:**
-#### 3. 一道类似的题目
+交换之前:
-下面的程序输出是什么?
-```java
-public class Demo {
- public static void main(String[] args) {
- Person p = new Person("张三");
+
- change(p);
+交换之后:
- System.out.println(p.name);
- }
+
- public static void change(Person p) {
- Person person = new Person("李四");
- p = person;
- }
-}
-class Person {
- String name;
+通过上面两张图可以很清晰的看出: **方法并没有改变存储在变量 s1 和 s2 中的对象引用。swap方法的参数x和y被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝**
- public Person(String name) {
- this.name = name;
- }
-}
-```
-很明显仍然会输出`张三`。因为`change`方法中重新创建了一个`Person`对象。
+### 总结
-那么,如果把` change`方法改为下图所示,输出结果又是什么呢?
+Java程序设计语言对对象采用的不是引用调用,实际上,对象引用是按
+值传递的。
-```java
- public static void change(Person p) {
- p.name="李四";
- }
-```
-答案我就不说了,我觉得大家如果认真看完上面的内容之后应该很很清楚了。
+下面再总结一下Java中方法参数的使用情况:
+
+- 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型》
+- 一个方法可以改变一个对象参数的状态。
+- 一个方法不能让对象参数引用一个新的对象。
+
+
+### 参考:
+
+《Java核心技术卷Ⅰ》基础知识第十版第四章4.5小节
## 二 ==与equals(重要)
**==** : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。(基本数据类型==比较的是值,引用数据类型==比较的是内存地址)
**equals()** : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
+
- 情况1:类没有覆盖equals()方法。则通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。
- 情况2:类覆盖了equals()方法。一般,我们都覆盖equals()方法来两个对象的内容相等;若它们的内容相等,则返回true(即,认为这两个对象相等)。
@@ -226,6 +181,7 @@ public class test1 {
```
**说明:**
+
- String中的equals方法是被重写过的,因为object的equals方法是比较的对象的内存地址,而String的equals方法比较的是对象的值。
- 当创建String类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个String对象。
@@ -236,7 +192,27 @@ public class test1 {
面试官可能会问你:“你重写过 hashcode 和 equals 么,为什么重写equals时必须重写hashCode方法?”
### hashCode()介绍
-hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 函数。另外需要注意的是: Object 的 hashcode 方法是本地方法,也就是用 c 语言或 c++ 实现的,该方法直接返回对象的 内存地址。
+hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 函数。另外需要注意的是: Object 的 hashcode 方法是本地方法,也就是用 c 语言或 c++ 实现的,该方法通常用来将对象的 内存地址 转换为整数之后返回。
+
+```java
+ /**
+ * Returns a hash code value for the object. This method is
+ * supported for the benefit of hash tables such as those provided by
+ * {@link java.util.HashMap}.
+ *
+ * As much as is reasonably practical, the hashCode method defined by
+ * class {@code Object} does return distinct integers for distinct
+ * objects. (This is typically implemented by converting the internal
+ * address of the object into an integer, but this implementation
+ * technique is not required by the
+ * Java™ programming language.)
+ *
+ * @return a hash code value for this object.
+ * @see java.lang.Object#equals(java.lang.Object)
+ * @see java.lang.System#identityHashCode
+ */
+ public native int hashCode();
+```
散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)
diff --git "a/\351\235\242\350\257\225\345\277\205\345\244\207/\346\234\200\346\234\200\346\234\200\345\270\270\350\247\201\347\232\204Java\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223/\347\254\254\344\272\214\345\221\250(2018-8-13).md" "b/EssentialContentForInterview/MostCommonJavaInterviewQuestions/\347\254\254\344\272\214\345\221\250(2018-8-13).md"
similarity index 83%
rename from "\351\235\242\350\257\225\345\277\205\345\244\207/\346\234\200\346\234\200\346\234\200\345\270\270\350\247\201\347\232\204Java\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223/\347\254\254\344\272\214\345\221\250(2018-8-13).md"
rename to "EssentialContentForInterview/MostCommonJavaInterviewQuestions/\347\254\254\344\272\214\345\221\250(2018-8-13).md"
index 2f6712bd320..426498cb2d9 100644
--- "a/\351\235\242\350\257\225\345\277\205\345\244\207/\346\234\200\346\234\200\346\234\200\345\270\270\350\247\201\347\232\204Java\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223/\347\254\254\344\272\214\345\221\250(2018-8-13).md"
+++ "b/EssentialContentForInterview/MostCommonJavaInterviewQuestions/\347\254\254\344\272\214\345\221\250(2018-8-13).md"
@@ -5,23 +5,38 @@
**可变性**
-String类中使用字符数组:`private final char value[]`保存字符串,所以String对象是不可变的。StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,char[]value,这两种对象都是可变的。
-
+
+简单的来说:String 类中使用 final 关键字字符数组保存字符串,`private final char value[]`,所以 String 对象是不可变的。而StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串`char[]value` 但是没有用 final 关键字修饰,所以这两种对象都是可变的。
+
+StringBuilder 与 StringBuffer 的构造方法都是调用父类构造方法也就是 AbstractStringBuilder 实现的,大家可以自行查阅源码。
+
+AbstractStringBuilder.java
+
+```java
+abstract class AbstractStringBuilder implements Appendable, CharSequence {
+ char[] value;
+ int count;
+ AbstractStringBuilder() {
+ }
+ AbstractStringBuilder(int capacity) {
+ value = new char[capacity];
+ }
+```
+
**线程安全性**
-String中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
+String 中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。
**性能**
-每次对String 类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String 对象。StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用StirngBuilder 相比使用StringBuffer 仅能获得10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
+每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StirngBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
**对于三者使用的总结:**
-
-- 如果要操作少量的数据用 = String
-- 单线程操作字符串缓冲区 下操作大量数据 = StringBuilder
-- 多线程操作字符串缓冲区 下操作大量数据 = StringBuffer
+1. 操作少量的数据 = String
+2. 单线程操作字符串缓冲区下操作大量数据 = StringBuilder
+3. 多线程操作字符串缓冲区下操作大量数据 = StringBuffer
#### String为什么是不可变的吗?
简单来说就是String类利用了final修饰的char类型数组存储字符,源码如下图所以:
@@ -158,6 +173,8 @@ Java语言通过字节码的方式,在一定程度上解决了传统解释型
4. 一个类实现接口的话要实现接口的所有方法,而抽象类不一定
5. 接口不能用new实例化,但可以声明,但是必须引用一个实现该接口的对象 从设计层面来说,抽象是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。
+注意:Java8 后接口可以有默认实现( default )。
+
### 成员变量与局部变量的区别有那些?
1. 从语法形式上,看成员变量是属于类的,而局部变量是在方法中定义的变量或是方法的参数;成员变量可以被public,private,static等修饰符所修饰,而局部变量不能被访问控制修饰符及static所修饰;但是,成员变量和局部变量都能被final所修饰;
diff --git "a/\351\235\242\350\257\225\345\277\205\345\244\207/\346\234\200\346\234\200\346\234\200\345\270\270\350\247\201\347\232\204Java\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223/\347\254\254\345\233\233\345\221\250(2018-8-30).md" "b/EssentialContentForInterview/MostCommonJavaInterviewQuestions/\347\254\254\345\233\233\345\221\250(2018-8-30).md"
similarity index 97%
rename from "\351\235\242\350\257\225\345\277\205\345\244\207/\346\234\200\346\234\200\346\234\200\345\270\270\350\247\201\347\232\204Java\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223/\347\254\254\345\233\233\345\221\250(2018-8-30).md"
rename to "EssentialContentForInterview/MostCommonJavaInterviewQuestions/\347\254\254\345\233\233\345\221\250(2018-8-30).md"
index c9a6ebf38d5..3cb02d73d5b 100644
--- "a/\351\235\242\350\257\225\345\277\205\345\244\207/\346\234\200\346\234\200\346\234\200\345\270\270\350\247\201\347\232\204Java\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223/\347\254\254\345\233\233\345\221\250(2018-8-30).md"
+++ "b/EssentialContentForInterview/MostCommonJavaInterviewQuestions/\347\254\254\345\233\233\345\221\250(2018-8-30).md"
@@ -20,7 +20,7 @@
2. **可运行(runnable)**:线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获 取cpu的使用权。
3. **运行(running)**:可运行状态(runnable)的线程获得了cpu时间片(timeslice),执行程序代码。
4. **阻塞(block)**:阻塞状态是指线程因为某种原因放弃了cpu使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有 机会再次获得cpu timeslice转到运行(running)状态。阻塞的情况分三种:
- - **(一). 等待阻塞**:运行(running)的线程执行o.wait()方法,JVM会把该线程放 入等待队列(waitting queue)中。
+ - **(一). 等待阻塞**:运行(running)的线程执行o.wait()方法,JVM会把该线程放 入等待队列(waiting queue)中。
- **(二). 同步阻塞**:运行(running)的线程在获取对象的同步锁时,若该同步 锁 被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
- **(三). 其他阻塞**: 运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。
5. **死亡(dead)**:线程run()、main()方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。
@@ -82,10 +82,10 @@ public class Run {

从上面的运行结果可以看出:线程是一个子任务,CPU以不确定的方式,或者说是以随机的时间来调用线程中的run方法。
-###②实现Runnable接口
+### ②实现Runnable接口
推荐实现Runnable接口方式开发多线程,因为Java单继承但是可以实现多个接口。
-。
+可以通过调用 Thead 类的 `setDaemon(true)` 方法设置当前的线程为守护线程。
注意事项:
@@ -178,7 +178,7 @@ Thread类中包含的成员变量代表了线程的某些优先级。如**Thread
- 两者最主要的区别在于:**sleep方法没有释放锁,而wait方法释放了锁** 。
- 两者都可以暂停线程的执行。
- Wait通常被用于线程间交互/通信,sleep通常被用于暂停执行。
-- wait()方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的notify()或者nofifyAl()方法。sleep()方法执行完成后,线程会自动苏醒。
+- wait()方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的notify()或者notifyAll()方法。sleep()方法执行完成后,线程会自动苏醒。
## 9 为什么我们调用start()方法时会执行run()方法,为什么我们不能直接调用run()方法?
diff --git a/EssentialContentForInterview/PreparingForInterview/JavaInterviewLibrary.md b/EssentialContentForInterview/PreparingForInterview/JavaInterviewLibrary.md
new file mode 100644
index 00000000000..4901f88932d
--- /dev/null
+++ b/EssentialContentForInterview/PreparingForInterview/JavaInterviewLibrary.md
@@ -0,0 +1,78 @@
+最近浏览 Github ,收藏了一些还算不错的 Java面试/学习相关的仓库,分享给大家,希望对你有帮助。我暂且按照目前的 Star 数量来排序。
+
+本文由 SnailClimb 整理,如需转载请联系作者。
+
+### 1. interviews
+
+- Github地址: [https://github.com/kdn251/interviews/blob/master/README-zh-cn.md](https://github.com/kdn251/interviews/blob/master/README-zh-cn.md)
+- star: 31k
+- 介绍: 软件工程技术面试个人指南。
+- 概览:
+
+ 
+
+### 2. JCSprout
+
+- Github地址:[https://github.com/crossoverJie/JCSprout](https://github.com/crossoverJie/JCSprout)
+- star: 17.7k
+- 介绍: Java Core Sprout:处于萌芽阶段的 Java 核心知识库。
+- 概览:
+
+ 
+
+### 3. JavaGuide
+
+- Github地址: [https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide)
+- star: 17.4k
+- 介绍: 【Java学习+面试指南】 一份涵盖大部分Java程序员所需要掌握的核心知识。
+- 概览:
+
+ 
+
+### 4. technology-talk
+
+- Github地址: [https://github.com/aalansehaiyang/technology-talk](https://github.com/aalansehaiyang/technology-talk)
+- star: 4.2k
+- 介绍: 汇总java生态圈常用技术框架、开源中间件,系统架构、项目管理、经典架构案例、数据库、常用三方库、线上运维等知识。
+
+### 5. fullstack-tutorial
+
+- Github地址: [https://github.com/frank-lam/fullstack-tutorial](https://github.com/frank-lam/fullstack-tutorial)
+- star: 2.8k
+- 介绍: Full Stack Developer Tutorial,后台技术栈/全栈开发/架构师之路,秋招/春招/校招/面试。 from zero to hero。
+- 概览:
+
+ 
+
+### 6. java-bible
+
+- Github地址:[https://github.com/biezhi/java-bible](https://github.com/biezhi/java-bible)
+- star: 1.9k
+- 介绍: 这里记录了一些技术摘要,部分文章来自网络,本项目的目的力求分享精品技术干货,以Java为主。
+- 概览:
+
+ 
+
+### 7. EasyJob
+
+- Github地址:[https://github.com/it-interview/EasyJob](https://github.com/it-interview/EasyJob)
+- star: 1.9k
+- 介绍: 互联网求职面试题、知识点和面经整理。
+
+### 8. advanced-java
+
+- Github地址:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java)
+- star: 1k
+- 介绍: 互联网 Java 工程师进阶知识完全扫盲
+
+### 9. 3y
+
+- Github地址:[https://github.com/ZhongFuCheng3y/3y](https://github.com/ZhongFuCheng3y/3y)
+- star: 0.4 k
+- 介绍: Java 知识整合。
+
+除了这九个仓库,再推荐几个不错的学习方向的仓库给大家。
+
+1. Star 数高达 4w+的 CS 笔记-CS-Notes:[https://github.com/CyC2018/CS-Notes](https://github.com/CyC2018/CS-Notes)
+2. 后端(尤其是Java)程序员的 Linux 学习仓库-Linux-Tutorial:[https://github.com/judasn/Linux-Tutorial](https://github.com/judasn/Linux-Tutorial)( Star:4.6k)
+3. 两个算法相关的仓库,刷 Leetcode 的小伙伴必备:①awesome-java-leetcode:[https://github.com/Blankj/awesome-java-leetcode](https://github.com/Blankj/awesome-java-leetcode);②LintCode:[https://github.com/awangdev/LintCode](https://github.com/awangdev/LintCode)
diff --git a/EssentialContentForInterview/PreparingForInterview/JavaProgrammerNeedKnow.md b/EssentialContentForInterview/PreparingForInterview/JavaProgrammerNeedKnow.md
new file mode 100644
index 00000000000..ef111f4cf15
--- /dev/null
+++ b/EssentialContentForInterview/PreparingForInterview/JavaProgrammerNeedKnow.md
@@ -0,0 +1,81 @@
+ 身边的朋友或者公众号的粉丝很多人都向我询问过:“我是双非/三本/专科学校的,我有机会进入大厂吗?”、“非计算机专业的学生能学好吗?”、“如何学习Java?”、“Java学习该学那些东西?”、“我该如何准备Java面试?”......这些方面的问题。我会根据自己的一点经验对大部分人关心的这些问题进行答疑解惑。现在又刚好赶上考研结束,这篇文章也算是给考研结束准备往Java后端方向发展的朋友们指名一条学习之路。道理懂了如果没有实际行动,那这篇文章对你或许没有任何意义。
+
+### Question1:我是双非/三本/专科学校的,我有机会进入大厂吗?
+
+ 我自己也是非985非211学校的,结合自己的经历以及一些朋友的经历,我觉得让我回答这个问题再好不过。
+
+ 首先,我觉得学校歧视很正常,真的太正常了,如果要抱怨的话,你只能抱怨自己没有进入名校。但是,千万不要动不动说自己学校差,动不动拿自己学校当做自己进不了大厂的借口,学历只是筛选简历的很多标准中的一个而已,如果你够优秀,简历够丰富,你也一样可以和名校同学一起同台竞争。
+
+ 企业HR肯定是更喜欢高学历的人,毕竟985,211优秀人才比例肯定比普通学校高很多,HR团队肯定会优先在这些学校里选。这就好比相亲,你是愿意在很多优秀的人中选一个优秀的,还是愿意在很多普通的人中选一个优秀的呢?
+
+ 双非本科甚至是二本、三本甚至是专科的同学也有很多进入大厂的,不过比率相比于名校的低很多而已。从大厂招聘的结果上看,高学历人才的数量占据大头,那些成功进入BAT、美团,京东,网易等大厂的双非本科甚至是二本、三本甚至是专科的同学往往是因为具备丰富的项目经历或者在某个含金量比较高的竞赛比如ACM中取得了不错的成绩。**一部分学历不突出但能力出众的面试者能够进入大厂并不是说明学历不重要,而是学历的软肋能够通过其他的优势来弥补。** 所以,如果你的学校不够好而你自己又想去大厂的话,建议你可以从这几点来做:**①尽量在面试前最好有一个可以拿的出手的项目;②有实习条件的话,尽早出去实习,实习经历也会是你的简历的一个亮点(有能力在大厂实习最佳!);③参加一些含金量比较高的比赛,拿不拿得到名次没关系,重在锻炼。**
+
+
+### Question2:非计算机专业的学生能学好Java后台吗?我能进大厂吗?
+
+ 当然可以!现在非科班的程序员很多,很大一部分原因是互联网行业的工资比较高。我们学校外面的培训班里面90%都是非科班,我觉得他们很多人学的都还不错。另外,我的一个朋友本科是机械专业,大一开始自学安卓,技术贼溜,在我看来他比大部分本科是计算机的同学学的还要好。参考Question1的回答,即使你是非科班程序员,如果你想进入大厂的话,你也可以通过自己的其他优势来弥补。
+
+ 我觉得我们不应该因为自己的专业给自己划界限或者贴标签,说实话,很多科班的同学可能并不如你,你以为科班的同学就会认真听讲吗?还不是几乎全靠自己课下自学!不过如果你是非科班的话,你想要学好,那么注定就要舍弃自己本专业的一些学习时间,这是无可厚非的。
+
+ 建议非科班的同学,首先要打好计算机基础知识基础:①计算机网络、②操作系统、③数据机构与算法,我个人觉得这3个对你最重要。这些东西就像是内功,对你以后的长远发展非常有用。当然,如果你想要进大厂的话,这些知识也是一定会被问到的。另外,“一定学好数据机构与算法!一定学好数据机构与算法!一定学好数据机构与算法!”,重要的东西说3遍。
+
+
+
+### Question3: 我没有实习经历的话找工作是不是特别艰难?
+
+ 没有实习经历没关系,只要你有拿得出手的项目或者大赛经历的话,你依然有可能拿到大厂的 offer 。笔主当时找工作的时候就没有实习经历以及大赛获奖经历,单纯就是凭借自己的项目经验撑起了整个面试。
+
+ 如果你既没有实习经历,又没有拿得出手的项目或者大赛经历的话,我觉得在简历关,除非你有其他特别的亮点,不然,你应该就会被刷。
+
+### Question4: 我该如何准备面试呢?面试的注意事项有哪些呢?
+
+下面是我总结的一些准备面试的Tips以及面试必备的注意事项:
+
+1. **准备一份自己的自我介绍,面试的时候根据面试对象适当进行修改**(突出重点,突出自己的优势在哪里,切忌流水账);
+2. **注意随身带上自己的成绩单和简历复印件;** (有的公司在面试前都会让你交一份成绩单和简历当做面试中的参考。)
+3. **如果需要笔试就提前刷一些笔试题,大部分在线笔试的类型是选择题+编程题,有的还会有简答题。**(平时空闲时间多的可以刷一下笔试题目(牛客网上有很多),但是不要只刷面试题,不动手code,程序员不是为了考试而存在的。)另外,注意抓重点,因为题目太多了,但是有很多题目几乎次次遇到,像这样的题目一定要搞定。
+4. **提前准备技术面试。** 搞清楚自己面试中可能涉及哪些知识点、那些知识点是重点。面试中哪些问题会被经常问到、自己改如何回答。(强烈不推荐背题,第一:通过背这种方式你能记住多少?能记住多久?第二:背题的方式的学习很难坚持下去!)
+5. **面试之前做好定向复习。** 也就是专门针对你要面试的公司来复习。比如你在面试之前可以在网上找找有没有你要面试的公司的面经。
+6. **准备好自己的项目介绍。** 如果有项目的话,技术面试第一步,面试官一般都是让你自己介绍一下你的项目。你可以从下面几个方向来考虑:①对项目整体设计的一个感受(面试官可能会让你画系统的架构图;②在这个项目中你负责了什么、做了什么、担任了什么角色;③ 从这个项目中你学会了那些东西,使用到了那些技术,学会了那些新技术的使用;④项目描述中,最好可以体现自己的综合素质,比如你是如何协调项目组成员协同开发的或者在遇到某一个棘手的问题的时候你是如何解决的又或者说你在这个项目用了什么技术实现了什么功能比如:用redis做缓存提高访问速度和并发量、使用消息队列削峰和降流等等。
+7. **面试之后记得复盘。** 面试遭遇失败是很正常的事情,所以善于总结自己的失败原因才是最重要的。如果失败,不要灰心;如果通过,切勿狂喜。
+
+
+**一些还算不错的 Java面试/学习相关的仓库,相信对大家准备面试一定有帮助:**[盘点一下Github上开源的Java面试/学习相关的仓库,看完弄懂薪资至少增加10k](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484817&idx=1&sn=12f0c254a240c40c2ccab8314653216b&chksm=fd9853f0caefdae6d191e6bf085d44ab9c73f165e3323aa0362d830e420ccbfad93aa5901021&token=766994974&lang=zh_CN#rd)
+
+### Question5: 我该自学还是报培训班呢?
+
+ 我本人更加赞同自学(你要知道去了公司可没人手把手教你了,而且几乎所有的公司都对培训班出生的有偏见。为什么有偏见,你学个东西还要去培训班,说明什么,同等水平下,你的自学能力以及自律能力一定是比不上自学的人的)。但是如果,你连每天在寝室坚持学上8个小时以上都坚持不了,或者总是容易半途而废的话,我还是推荐你去培训班。观望身边同学去培训班的,大多是非计算机专业或者是没有自律能力以及自学能力非常差的人。
+
+ 另外,如果自律能力不行,你也可以通过结伴学习、参加老师的项目等方式来督促自己学习。
+
+ 总结:去不去培训班主要还是看自己,如果自己能坚持自学就自学,坚持不下来就去培训班。
+
+### Question6: 没有项目经历/博客/Github开源项目怎么办?
+
+ 从现在开始做!
+
+ 网上有很多非常不错的项目视频,你就跟着一步一步做,不光要做,还要改进,改善。另外,如果你的老师有相关 Java 后台项目的话,你也可以主动申请参与进来。
+
+ 如果有自己的博客,也算是简历上的一个亮点。建议可以在掘金、Segmentfault、CSDN等技术交流社区写博客,当然,你也可以自己搭建一个博客(采用 Hexo+Githu Pages 搭建非常简单)。写一些什么?学习笔记、实战内容、读书笔记等等都可以。
+
+ 多用 Github,用好 Github,上传自己不错的项目,写好 readme 文档,在其他技术社区做好宣传。相信你也会收获一个不错的开源项目!
+
+
+### Question7: 大厂到底青睐什么样的应届生?
+
+ 从阿里、腾讯等大厂招聘官网对于Java后端方向/后端方向的应届实习生的要求,我们大概可以总结归纳出下面这 4 点能给简历增加很多分数:
+
+- 参加过竞赛(含金量超高的是ACM);
+- 对数据结构与算法非常熟练;
+- 参与过实际项目(比如学校网站);
+- 参与过某个知名的开源项目或者自己的某个开源项目很不错;
+
+ 除了我上面说的这三点,在面试Java工程师的时候,下面几点也提升你的个人竞争力:
+
+- 熟悉Python、Shell、Perl等脚本语言;
+- 熟悉 Java 优化,JVM调优;
+- 熟悉 SOA 模式;
+- 熟悉自己所用框架的底层知识比如Spring;
+- 了解分布式一些常见的理论;
+- 具备高并发开发经验;大数据开发经验等等。
+
diff --git a/EssentialContentForInterview/PreparingForInterview/books.md b/EssentialContentForInterview/PreparingForInterview/books.md
new file mode 100644
index 00000000000..34f495fd798
--- /dev/null
+++ b/EssentialContentForInterview/PreparingForInterview/books.md
@@ -0,0 +1,66 @@
+
+### 核心基础知识
+
+- [《图解HTTP》](https://book.douban.com/subject/25863515/)(推荐,豆瓣评分 8.1 , 1.6K+人评价): 讲漫画一样的讲HTTP,很有意思,不会觉得枯燥,大概也涵盖也HTTP常见的知识点。因为篇幅问题,内容可能不太全面。不过,如果不是专门做网络方向研究的小伙伴想研究HTTP相关知识的话,读这本书的话应该来说就差不多了。
+- [《大话数据结构》](https://book.douban.com/subject/6424904/)(推荐,豆瓣评分 7.9 , 1K+人评价):入门类型的书籍,读起来比较浅显易懂,适合没有数据结构基础或者说数据结构没学好的小伙伴用来入门数据结构。
+- [《数据结构与算法分析:C语言描述》](https://book.douban.com/subject/1139426/)(推荐,豆瓣评分 8.9,1.6K+人评价):本书是《Data Structures and Algorithm Analysis in C》一书第2版的简体中译本。原书曾被评为20世纪顶尖的30部计算机著作之一,作者Mark Allen Weiss在数据结构和算法分析方面卓有建树,他的数据结构和算法分析的著作尤其畅销,并受到广泛好评.已被世界500余所大学用作教材。
+- [《算法图解》](https://book.douban.com/subject/26979890/)(推荐,豆瓣评分 8.4,0.6K+人评价):入门类型的书籍,读起来比较浅显易懂,适合没有算法基础或者说算法没学好的小伙伴用来入门。示例丰富,图文并茂,以让人容易理解的方式阐释了算法.读起来比较快,内容不枯燥!
+- [《算法 第四版》](https://book.douban.com/subject/10432347/)(推荐,豆瓣评分 9.3,0.4K+人评价):Java语言描述,算法领域经典的参考书,全面介绍了关于算法和数据结构的必备知识,并特别针对排序、搜索、图处理和字符串处理进行了论述。书的内容非常多,可以说是Java程序员的必备书籍之一了。
+
+
+
+
+### Java相关
+
+- [《Effective java 》](https://book.douban.com/subject/3360807/)(推荐,豆瓣评分 9.0,1.4K+人评价):本书介绍了在Java编程中78条极具实用价值的经验规则,这些经验规则涵盖了大多数开发人员每天所面临的问题的解决方案。通过对Java平台设计专家所使用的技术的全面描述,揭示了应该做什么,不应该做什么才能产生清晰、健壮和高效的代码。本书中的每条规则都以简短、独立的小文章形式出现,并通过例子代码加以进一步说明。本书内容全面,结构清晰,讲解详细。可作为技术人员的参考用书。
+- [《Head First Java.第二版》](https://book.douban.com/subject/2000732/)(推荐,豆瓣评分 8.7,1.0K+人评价): 可以说是我的Java启蒙书籍了,特别适合新手读当然也适合我们用来温故Java知识点。
+- [《Java多线程编程核心技术》](https://book.douban.com/subject/26555197/): Java多线程入门级书籍还不错,但是说实话,质量不是很高,很快就可以阅读完。
+- [《JAVA网络编程 第4版》](https://book.douban.com/subject/26259017/): 可以系统的学习一下网络的一些概念以及网络编程在Java中的使用。
+- [《Java核心技术卷1+卷2》](https://book.douban.com/subject/25762168/)(推荐): 很棒的两本书,建议有点Java基础之后再读,介绍的还是比较深入的,非常推荐。这两本书我一般也会用来巩固知识点,是两本适合放在自己身边的好书。
+- [《Java编程思想(第4版)》](https://book.douban.com/subject/2130190/)(推荐,豆瓣评分 9.1,3.2K+人评价):这本书要常读,初学者可以快速概览,中等程序员可以深入看看java,老鸟还可以用之回顾java的体系。这本书之所以厉害,因为它在无形中整合了设计模式,这本书之所以难读,也恰恰在于他对设计模式的整合是无形的。
+- [《Java并发编程的艺术》](https://book.douban.com/subject/26591326/)(推荐,豆瓣评分 7.2,0.2K+人评价): 这本书不是很适合作为Java并发入门书籍,需要具备一定的JVM基础。我感觉有些东西讲的还是挺深入的,推荐阅读。
+- [《实战Java高并发程序设计》](https://book.douban.com/subject/26663605/)(推荐):豆瓣评分 8.3 ,书的质量没的说,推荐大家好好看一下。
+- [《Java程序员修炼之道》](https://book.douban.com/subject/24841235/): 很杂,我只看了前面几章,不太推荐阅读。
+- [《深入理解Java虚拟机(第2版)周志明》](https://book.douban.com/subject/24722612/)(推荐,豆瓣评分 8.9,1.0K+人评价):建议多刷几遍,书中的所有知识点可以通过JAVA运行时区域和JAVA的内存模型与线程两个大模块罗列完全。
+- [《Netty实战》](https://book.douban.com/subject/27038538/)(推荐,豆瓣评分 7.8,92人评价):内容很细,如果想学Netty的话,推荐阅读这本书!
+- [《从Paxos到Zookeeper》](https://book.douban.com/subject/26292004/)(推荐,豆瓣评分 7.8,0.3K人评价):简要介绍几种典型的分布式一致性协议,以及解决分布式一致性问题的思路,其中重点讲解了Paxos和ZAB协议。同时,本书深入介绍了分布式一致性问题的工业解决方案——ZooKeeper,并着重向读者展示这一分布式协调框架的使用方法、内部实现及运维技巧,旨在帮助读者全面了解ZooKeeper,并更好地使用和运维ZooKeeper。
+
+### JavaWeb相关
+
+- [《深入分析Java Web技术内幕》](https://book.douban.com/subject/25953851/): 感觉还行,涉及的东西也蛮多。
+- [《Spring实战(第4版)》](https://book.douban.com/subject/26767354/)(推荐,豆瓣评分 8.3
+,0.3K+人评价):不建议当做入门书籍读,入门的话可以找点国人的书或者视频看。这本定位就相当于是关于Spring的新华字典,只有一些基本概念的介绍和示例,涵盖了Spring的各个方面,但都不够深入。就像作者在最后一页写的那样:“学习Spring,这才刚刚开始”。
+- [《Java Web整合开发王者归来》](https://book.douban.com/subject/4189495/)(已过时):当时刚开始学的时候就是开的这本书,基本上是完完整整的看完了。不过,我不是很推荐大家看。这本书比较老了,里面很多东西都已经算是过时了。不过,这本书的一个很大优点是:基础知识点概括全面。
+- [《Redis实战》](https://book.douban.com/subject/26612779/):如果你想了解Redis的一些概念性知识的话,这本书真的非常不错。
+- [《Redis设计与实现》](https://book.douban.com/subject/25900156/)(推荐,豆瓣评分 8.5,0.5K+人评价)
+- [《深入剖析Tomcat》](https://book.douban.com/subject/10426640/)(推荐,豆瓣评分 8.4,0.2K+人评价):本书深入剖析Tomcat 4和Tomcat 5中的每个组件,并揭示其内部工作原理。通过学习本书,你将可以自行开发Tomcat组件,或者扩展已有的组件。 读完这本书,基本可以摆脱背诵面试题的尴尬。
+- [《高性能MySQL》](https://book.douban.com/subject/23008813/)(推荐,豆瓣评分 9.3,0.4K+人评价):mysql 领域的经典之作,拥有广泛的影响力。不但适合数据库管理员(dba)阅读,也适合开发人员参考学习。不管是数据库新手还是专家,相信都能从本书有所收获。
+- [深入理解Nginx(第2版)](https://book.douban.com/subject/26745255/):作者讲的非常细致,注释都写的都很工整,对于 Nginx 的开发人员非常有帮助。优点是细致,缺点是过于细致,到处都是代码片段,缺少一些抽象。
+- [《RabbitMQ实战指南》](https://book.douban.com/subject/27591386/):《RabbitMQ实战指南》从消息中间件的概念和RabbitMQ的历史切入,主要阐述RabbitMQ的安装、使用、配置、管理、运维、原理、扩展等方面的细节。如果你想浅尝RabbitMQ的使用,这本书是你最好的选择;如果你想深入RabbitMQ的原理,这本书也是你最好的选择;总之,如果你想玩转RabbitMQ,这本书一定是最值得看的书之一
+- [《Spring Cloud微服务实战》](https://book.douban.com/subject/27025912/):从时下流行的微服务架构概念出发,详细介绍了Spring Cloud针对微服务架构中几大核心要素的解决方案和基础组件。对于各个组件的介绍,《Spring Cloud微服务实战》主要以示例与源码结合的方式来帮助读者更好地理解这些组件的使用方法以及运行原理。同时,在介绍的过程中,还包含了作者在实践中所遇到的一些问题和解决思路,可供读者在实践中作为参考。
+- [《第一本Docker书》](https://book.douban.com/subject/26780404/):Docker入门书籍!
+
+### 操作系统
+
+- [《鸟哥的Linux私房菜》](https://book.douban.com/subject/4889838/)(推荐,,豆瓣评分 9.1,0.3K+人评价):本书是最具知名度的Linux入门书《鸟哥的Linux私房菜基础学习篇》的最新版,全面而详细地介绍了Linux操作系统。全书分为5个部分:第一部分着重说明Linux的起源及功能,如何规划和安装Linux主机;第二部分介绍Linux的文件系统、文件、目录与磁盘的管理;第三部分介绍文字模式接口 shell和管理系统的好帮手shell脚本,另外还介绍了文字编辑器vi和vim的使用方法;第四部分介绍了对于系统安全非常重要的Linux账号的管理,以及主机系统与程序的管理,如查看进程、任务分配和作业管理;第五部分介绍了系统管理员(root)的管理事项,如了解系统运行状况、系统服务,针对登录文件进行解析,对系统进行备份以及核心的管理等。
+
+### 架构相关
+
+- [《大型网站技术架构:核心原理与案例分析+李智慧》](https://book.douban.com/subject/25723064/)(推荐):这本书我读过,基本不需要你有什么基础啊~读起来特别轻松,但是却可以学到很多东西,非常推荐了。另外我写过这本书的思维导图,关注我的微信公众号:“Java面试通关手册”回复“大型网站技术架构”即可领取思维导图。
+- [《亿级流量网站架构核心技术》](https://book.douban.com/subject/26999243/)(推荐):一书总结并梳理了亿级流量网站高可用和高并发原则,通过实例详细介绍了如何落地这些原则。本书分为四部分:概述、高可用原则、高并发原则、案例实战。从负载均衡、限流、降级、隔离、超时与重试、回滚机制、压测与预案、缓存、池化、异步化、扩容、队列等多方面详细介绍了亿级流量网站的架构核心技术,让读者看后能快速运用到实践项目中。
+- [《架构解密从分布式到微服务(Leaderus著)》](https://book.douban.com/subject/27081188/):很一般的书籍,我就是当做课后图书来阅读的。
+
+### 代码优化
+
+- [《重构_改善既有代码的设计》](https://book.douban.com/subject/4262627/)(推荐):豆瓣 9.1 分,重构书籍的开山鼻祖。
+
+### 课外书籍
+
+- 《追风筝的人》(推荐)
+- 《穆斯林的葬礼》 (推荐)
+- 《三体》 (推荐)
+- 《活着——余华》 (推荐)
+
+
+
+
diff --git a/EssentialContentForInterview/PreparingForInterview/interviewPrepare.md b/EssentialContentForInterview/PreparingForInterview/interviewPrepare.md
new file mode 100644
index 00000000000..c99ca1c156e
--- /dev/null
+++ b/EssentialContentForInterview/PreparingForInterview/interviewPrepare.md
@@ -0,0 +1,74 @@
+这是【备战春招/秋招系列】的第二篇文章,主要是简单地介绍如何去准备面试。
+
+不论是校招还是社招都避免不了各种面试、笔试,如何去准备这些东西就显得格外重要。不论是笔试还是面试都是有章可循的,我这个“有章可循”说的意思只是说应对技术面试是可以提前准备。 我其实特别不喜欢那种临近考试就提前背啊记啊各种题的行为,非常反对!我觉得这种方法特别极端,而且在稍有一点经验的面试官面前是根本没有用的。建议大家还是一步一个脚印踏踏实实地走。
+
+### 1 如何获取大厂面试机会?
+
+**在讲如何获取大厂面试机会之前,先来给大家科普/对比一下两个校招非常常见的概念——春招和秋招。**
+
+1. **招聘人数** :秋招多于春招 ;
+2. **招聘时间** : 秋招一般7月左右开始,大概一直持续到10月底。但是大厂(如BAT)都会早开始早结束,所以一定要把握好时间。春招最佳时间为3月,次佳时间为4月,进入5月基本就不会再有春招了(金三银四)。
+3. **应聘难度** :秋招略大于春招;
+4. **招聘公司:** 秋招数量多,而春招数量较少,一般为秋招的补充。
+
+**综上,一般来说,秋招的含金量明显是高于春招的。**
+
+**下面我就说一下我自己知道的一些方法,不过应该也涵盖了大部分获取面试机会的方法。**
+
+1. **关注大厂官网,随时投递简历(走流程的网申);**
+2. **线下参加宣讲会,直接投递简历;**
+3. **找到师兄师姐/认识的人,帮忙内推(能够让你避开网申简历筛选,笔试筛选,还是挺不错的,不过也还是需要你的简历够棒);**
+4. **博客发文被看中/Github优秀开源项目作者,大厂内部人员邀请你面试;**
+5. **求职类网站投递简历(不是太推荐,适合海投);**
+
+
+除了这些方法,我也遇到过这样的经历:有些大公司的一些部门可能暂时没招够人,然后如果你的亲戚或者朋友刚好在这个公司,而你正好又在寻求offer,那么面试机会基本上是有了,而且这种面试的难度好像一般还普遍比其他正规面试低很多。
+
+### 2 面试前的准备
+
+### 2.1 准备自己的自我介绍
+
+从HR面、技术面到高管面/部门主管面,面试官一般会让你先自我介绍一下,所以好好准备自己的自我介绍真的非常重要。网上一般建议的是准备好两份自我介绍:一份对hr说的,主要讲能突出自己的经历,会的编程技术一语带过;另一份对技术面试官说的,主要讲自己会的技术细节,项目经验,经历那些就一语带过。
+
+我这里简单分享一下我自己的自我介绍的一个简单的模板吧:
+
+> 面试官,您好!我叫某某。大学时间我主要利用课外时间学习某某。在校期间参与过一个某某系统的开发,另外,自己学习过程中也写过很多系统比如某某系统。在学习之余,我比较喜欢通过博客整理分享自己所学知识。我现在是某某社区的认证作者,写过某某很不错的文章。另外,我获得过某某奖,我的Github上开源的某个项目已经有多少Star了。
+
+### 2.2 关于着装
+
+穿西装、打领带、小皮鞋?NO!NO!NO!这是互联网公司面试又不是去走红毯,所以你只需要穿的简单大方就好,不需要太正式。
+
+### 2.3 随身带上自己的成绩单和简历
+
+有的公司在面试前都会让你交一份成绩单和简历当做面试中的参考。
+
+### 2.4 如果需要笔试就提前刷一些笔试题
+
+平时空闲时间多的可以刷一下笔试题目(牛客网上有很多)。但是不要只刷面试题,不动手code,程序员不是为了考试而存在的。
+
+### 2.5 花时间一些逻辑题
+
+面试中发现有些公司都有逻辑题测试环节,并且都把逻辑笔试成绩作为很重要的一个参考。
+
+### 2.6 准备好自己的项目介绍
+
+如果有项目的话,技术面试第一步,面试官一般都是让你自己介绍一下你的项目。你可以从下面几个方向来考虑:
+
+1. 对项目整体设计的一个感受(面试官可能会让你画系统的架构图)
+2. 在这个项目中你负责了什么、做了什么、担任了什么角色
+3. 从这个项目中你学会了那些东西,使用到了那些技术,学会了那些新技术的使用
+4. 另外项目描述中,最好可以体现自己的综合素质,比如你是如何协调项目组成员协同开发的或者在遇到某一个棘手的问题的时候你是如何解决的又或者说你在这个项目用了什么技术实现了什么功能比如:用redis做缓存提高访问速度和并发量、使用消息队列削峰和降流等等。
+
+### 2.7 提前准备技术面试
+
+搞清楚自己面试中可能涉及哪些知识点、那些知识点是重点。面试中哪些问题会被经常问到、自己改如何回答。(强烈不推荐背题,第一:通过背这种方式你能记住多少?能记住多久?第二:背题的方式的学习很难坚持下去!)
+
+### 2.7 面试之前做好定向复习
+
+所谓定向复习就是专门针对你要面试的公司来复习。比如你在面试之前可以在网上找找有没有你要面试的公司的面经。
+
+举个栗子:在我面试 ThoughtWorks 的前几天我就在网上找了一些关于 ThoughtWorks 的技术面的一些文章。然后知道了 ThoughtWorks 的技术面会让我们在之前做的作业的基础上增加一个或两个功能,所以我提前一天就把我之前做的程序重新重构了一下。然后在技术面的时候,简单的改了几行代码之后写个测试就完事了。如果没有提前准备,我觉得 20 分钟我很大几率会完不成这项任务。
+
+# 3 面试之后复盘
+
+如果失败,不要灰心;如果通过,切勿狂喜。面试和工作实际上是两回事,可能很多面试未通过的人,工作能力比你强的多,反之亦然。我个人觉得面试也像是一场全新的征程,失败和胜利都是平常之事。所以,劝各位不要因为面试失败而灰心、丧失斗志。也不要因为面试通过而沾沾自喜,等待你的将是更美好的未来,继续加油!
\ No newline at end of file
diff --git "a/EssentialContentForInterview/PreparingForInterview/\345\246\202\346\236\234\351\235\242\350\257\225\345\256\230\351\227\256\344\275\240\342\200\234\344\275\240\346\234\211\344\273\200\344\271\210\351\227\256\351\242\230\351\227\256\346\210\221\345\220\227\357\274\237\342\200\235\346\227\266\357\274\214\344\275\240\350\257\245\345\246\202\344\275\225\345\233\236\347\255\224.md" "b/EssentialContentForInterview/PreparingForInterview/\345\246\202\346\236\234\351\235\242\350\257\225\345\256\230\351\227\256\344\275\240\342\200\234\344\275\240\346\234\211\344\273\200\344\271\210\351\227\256\351\242\230\351\227\256\346\210\221\345\220\227\357\274\237\342\200\235\346\227\266\357\274\214\344\275\240\350\257\245\345\246\202\344\275\225\345\233\236\347\255\224.md"
new file mode 100644
index 00000000000..d4d6b64b0a5
--- /dev/null
+++ "b/EssentialContentForInterview/PreparingForInterview/\345\246\202\346\236\234\351\235\242\350\257\225\345\256\230\351\227\256\344\275\240\342\200\234\344\275\240\346\234\211\344\273\200\344\271\210\351\227\256\351\242\230\351\227\256\346\210\221\345\220\227\357\274\237\342\200\235\346\227\266\357\274\214\344\275\240\350\257\245\345\246\202\344\275\225\345\233\236\347\255\224.md"
@@ -0,0 +1,64 @@
+我还记得当时我去参加面试的时候,几乎每一场面试,特别是HR面和高管面的时候,面试官总是会在结尾问我:“问了你这么多问题了,你有什么问题问我吗?”。这个时候很多人内心就会陷入短暂的纠结中:我该问吗?不问的话面试官会不会对我影响不好?问什么问题?问这个问题会不会让面试官对我的影响不好啊?
+
+
+
+### 这个问题对最终面试结果的影响到底大不大?
+
+就技术面试而言,回答这个问题的时候,只要你不是触碰到你所面试的公司的雷区,那么我觉得这对你能不能拿到最终offer来说影响确实是不大的。我说这些并不代表你就可以直接对面试官说:“我没问题了。”,笔主当时面试的时候确实也说过挺多次“没问题要问了。”,最终也没有导致笔主被pass掉(可能是前面表现比较好,哈哈,自恋一下)。我现在回想起来,觉得自己当时做法其实挺不对的。面试本身就是一个双向选择的过程,你对这个问题的回答也会侧面反映出你对这次面试的上心程度,你的问题是否有价值,也影响了你最终的选择与公司是否选择你。
+
+面试官在技术面试中主要考察的还是你这样个人到底有没有胜任这个工作的能力以及你是否适合公司未来的发展需要,很多公司还需要你认同它的文化,我觉得你只要不是太笨,应该不会栽在这里。除非你和另外一个人在能力上相同,但是只能在你们两个人中选一个,那么这个问题才对你能不能拿到offer至关重要。有准备总比没准备好,给面试官留一个好的影响总归是没错的。
+
+但是,就非技术面试来说,我觉得好好回答这个问题对你最终的结果还是比较重要的。
+
+总的来说不管是技术面试还是非技术面试,如果你想赢得公司的青睐和尊重,我觉得我们都应该重视这个问题。
+
+### 真诚一点,不要问太 Low 的问题
+
+回答这个问题很重要的一点就是你没有必要放低自己的姿态问一些很虚或者故意讨好面试官的问题,也不要把自己从面经上学到的东西照搬下来使用。面试官也不是傻子,特别是那种特别有经验的面试官,你是真心诚意的问问题,还是从别处照搬问题来讨好面试官,人家可能一听就听出来了。总的来说,还是要真诚。除此之外,不要问太Low的问题,会显得你整个人格局比较小或者说你根本没有准备(侧面反映你对这家公司不伤心,既然你不上心,为什么要要你呢)。举例几个比较 Low 的问题,大家看看自己有没有问过其中的问题:
+
+- 贵公司的主要业务是什么?(面试之前自己不知道提前网上查一下吗?)
+- 贵公司的男女比例如何?(考虑脱单?记住你是来工作的!)
+- 贵公司一年搞几次外出旅游?(你是来工作的,这些娱乐活动先别放在心上!)
+- ......
+
+### 有哪些有价值的问题值得问?
+
+针对这个问题。笔主专门找了几个专门做HR工作的小哥哥小姐姐们询问并且查阅了挺多前辈们的回答,然后结合自己的实际经历,我概括了下面几个比较适合问的问题。
+
+#### 面对HR或者其他Level比较低的面试官时
+
+1. **能不能谈谈你作为一个公司老员工对公司的感受?** (这个问题比较容易回答,不会让面试官陷入无话可说的尴尬境地。另外,从面试官的回答中你可以加深对这个公司的了解,让你更加清楚这个公司到底是不是你想的那样或者说你是否能适应这个公司的文化。除此之外,这样的问题在某种程度上还可以拉进你与面试官的距离。)
+2. **能不能问一下,你当时因为什么原因选择加入这家公司的呢或者说这家公司有哪些地方吸引你?有什么地方你觉得还不太好或者可以继续完善吗?** (类似第一个问题,都是问面试官个人对于公司的看法,)
+3. **我觉得我这次表现的不是太好,你有什么建议或者评价给我吗?**(这个是我常问的。我觉得说自己表现不好只是这个语境需要这样来说,这样可以显的你比较谦虚好学上进。)
+4. **接下来我会有一段空档期,有什么值得注意或者建议学习的吗?** (体现出你对工作比较上心,自助学习意识比较强。)
+5. **这个岗位为什么还在招人?** (岗位真实性和价值咨询)
+6. **大概什么时候能给我回复呢?** (终面的时候,如果面试官没有说的话,可以问一下)
+7. ......
+
+
+
+#### 面对部门领导
+
+1. **部门的主要人员分配以及对应的主要工作能简单介绍一下吗?**
+2. **未来如果我要加入这个团队,你对我的期望是什么?** (部门领导一般情况下是你的直属上级了,你以后和他打交道的机会应该是最多的。你问这个问题,会让他感觉你是一个对他的部门比较上心,比较有团体意识,并且愿意倾听的候选人。)
+3. **公司对新入职的员工的培养机制是什么样的呢?** (正规的公司一般都有培养机制,提前问一下是对你自己的负责也会显的你比较上心)
+4. **以您来看,这个岗位未来在公司内部的发展如何?** (在我看来,问这个问题也是对你自己的负责吧,谁不想发展前景更好的岗位呢?)
+5. **团队现在面临的最大挑战是什么?** (这样的问题不会暴露你对公司的不了解,并且也能让你对未来工作的挑战或困难有一个提前的预期。)
+
+
+
+#### 面对Level比较高的(比如总裁,老板)
+
+1. **贵公司的发展目标和方向是什么?** (看下公司的发展是否满足自己的期望)
+2. **与同行业的竞争者相比,贵公司的核心竞争优势在什么地方?** (充分了解自己的优势和劣势)
+3. **公司现在面临的最大挑战是什么?**
+
+### 来个补充,顺便送个祝福给大家
+
+薪酬待遇和相关福利问题一般在终面的时候(最好不要在前面几面的时候就问到这个问题),面试官会提出来或者在面试完之后以邮件的形式告知你。一般来说,如果面试官很愿意为你回答问题,对你的问题也比较上心的话,那他肯定是觉得你就是他们要招的人。
+
+大家在面试的时候,可以根据自己对于公司或者岗位的了解程度,对上面提到的问题进行适当修饰或者修改。上面提到的一些问题只是给没有经验的朋友一个参考,如果你还有其他比较好的问题的话,那当然也更好啦!
+
+金三银四。过了二月就到了面试高峰期或者说是黄金期。几份惊喜几份愁,愿各位能始终不忘初心!每个人都有每个人的难处。引用一句《阿甘正传》里面的台词:“生活就像一盒巧克力,你永远不知道下一块是什么味道“。
+
+
\ No newline at end of file
diff --git "a/EssentialContentForInterview/PreparingForInterview/\347\250\213\345\272\217\345\221\230\347\232\204\347\256\200\345\216\206\344\271\213\351\201\223.md" "b/EssentialContentForInterview/PreparingForInterview/\347\250\213\345\272\217\345\221\230\347\232\204\347\256\200\345\216\206\344\271\213\351\201\223.md"
new file mode 100644
index 00000000000..d07fa52a7e7
--- /dev/null
+++ "b/EssentialContentForInterview/PreparingForInterview/\347\250\213\345\272\217\345\221\230\347\232\204\347\256\200\345\216\206\344\271\213\351\201\223.md"
@@ -0,0 +1,106 @@
+# 程序员的简历就该这样写
+
+### 1 前言
+一份好的简历可以在整个申请面试以及面试过程中起到非常好的作用。 在不夸大自己能力的情况下,写出一份好的简历也是一项很棒的能力。
+
+### 2 为什么说简历很重要?
+
+#### 2.1 先从面试前来说
+
+假如你是网申,你的简历必然会经过HR的筛选,一张简历HR可能也就花费10秒钟看一下,然后HR就会决定你这一关是Fail还是Pass。
+
+假如你是内推,如果你的简历没有什么优势的话,就算是内推你的人再用心,也无能为力。
+
+另外,就算你通过了筛选,后面的面试中,面试官也会根据你的简历来判断你究竟是否值得他花费很多时间去面试。
+
+所以,简历就像是我们的一个门面一样,它在很大程度上决定了你能否进入到下一轮的面试中。
+
+#### 2.2 再从面试中来说
+
+我发现大家比较喜欢看面经 ,这点无可厚非,但是大部分面经都没告诉你很多问题都是在特定条件下才问的。举个简单的例子:一般情况下你的简历上注明你会的东西才会被问到(Java、数据结构、网络、算法这些基础是每个人必问的),比如写了你会 redis,那面试官就很大概率会问你 redis 的一些问题。比如:redis的常见数据类型及应用场景、redis是单线程为什么还这么快、 redis 和 memcached 的区别、redis 内存淘汰机制等等。
+
+所以,首先,你要明确的一点是:**你不会的东西就不要写在简历上**。另外,**你要考虑你该如何才能让你的亮点在简历中凸显出来**,比如:你在某某项目做了什么事情解决了什么问题(只要有项目就一定有要解决的问题)、你的某一个项目里使用了什么技术后整体性能和并发量提升了很多等等。
+
+面试和工作是两回事,聪明的人会把面试官往自己擅长的领域领,其他人则被面试官牵着鼻子走。虽说面试和工作是两回事,但是你要想要获得自己满意的 offer ,你自身的实力必须要强。
+
+### 3 下面这几点你必须知道
+
+1. 大部分公司的HR都说我们不看重学历(骗你的!),但是如果你的学校不出众的话,很难在一堆简历中脱颖而出,除非你的简历上有特别的亮点,比如:某某大厂的实习经历、获得了某某大赛的奖等等。
+2. **大部分应届生找工作的硬伤是没有工作经验或实习经历,所以如果你是应届生就不要错过秋招和春招。一旦错过,你后面就极大可能会面临社招,这个时候没有工作经验的你可能就会面临各种碰壁,导致找不到一个好的工作**
+3. **写在简历上的东西一定要慎重,这是面试官大量提问的地方;**
+4. **将自己的项目经历完美的展示出来非常重要。**
+
+### 4 必须了解的两大法则
+
+
+**①STAR法则(Situation Task Action Result):**
+
+- **Situation:** 事情是在什么情况下发生;
+- **Task::** 你是如何明确你的任务的;
+- **Action:** 针对这样的情况分析,你采用了什么行动方式;
+- **Result:** 结果怎样,在这样的情况下你学习到了什么。
+
+简而言之,STAR法则,就是一种讲述自己故事的方式,或者说,是一个清晰、条理的作文模板。不管是什么,合理熟练运用此法则,可以轻松的对面试官描述事物的逻辑方式,表现出自己分析阐述问题的清晰性、条理性和逻辑性。
+
+下面这段内容摘自百度百科,我觉得写的非常不错:
+
+> STAR法则,500强面试题回答时的技巧法则,备受面试者成功者和500强HR的推崇。
+由于这个法则被广泛应用于面试问题的回答,尽管我们还在写简历阶段,但是,写简历时能把面试的问题就想好,会使自己更加主动和自信,做到简历,面试关联性,逻辑性强,不至于在一个月后去面试,却把简历里的东西都忘掉了(更何况有些朋友会稍微夸大简历内容)
+在我们写简历时,每个人都要写上自己的工作经历,活动经历,想必每一个同学,都会起码花上半天甚至更长的时间去搜寻脑海里所有有关的经历,争取找出最好的东西写在简历上。
+但是此时,我们要注意了,简历上的任何一个信息点都有可能成为日后面试时的重点提问对象,所以说,不能只管写上让自己感觉最牛的经历就完事了,要想到今后,在面试中,你所写的经历万一被面试官问到,你真的能回答得流利,顺畅,且能通过这段经历,证明自己正是适合这个职位的人吗?
+
+**②FAB 法则(Feature Advantage Benefit):**
+
+- **Feature:** 是什么;
+- **Advantage:** 比别人好在哪些地方;
+- **Benefit:** 如果雇佣你,招聘方会得到什么好处。
+
+简单来说,这个法则主要是让你的面试官知道你的优势、招了你之后对公司有什么帮助。
+
+### 5 项目经历怎么写?
+
+简历上有一两个项目经历很正常,但是真正能把项目经历很好的展示给面试官的非常少。对于项目经历大家可以考虑从如下几点来写:
+
+1. 对项目整体设计的一个感受
+2. 在这个项目中你负责了什么、做了什么、担任了什么角色
+3. 从这个项目中你学会了那些东西,使用到了那些技术,学会了那些新技术的使用
+4. 另外项目描述中,最好可以体现自己的综合素质,比如你是如何协调项目组成员协同开发的或者在遇到某一个棘手的问题的时候你是如何解决的又或者说你在这个项目用了什么技术实现了什么功能比如:用redis做缓存提高访问速度和并发量、使用消息队列削峰和降流等等。
+
+### 6 专业技能该怎么写?
+先问一下你自己会什么,然后看看你意向的公司需要什么。一般HR可能并不太懂技术,所以他在筛选简历的时候可能就盯着你专业技能的关键词来看。对于公司有要求而你不会的技能,你可以花几天时间学习一下,然后在简历上可以写上自己了解这个技能。比如你可以这样写(下面这部分内容摘自我的简历,大家可以根据自己的情况做一些修改和完善):
+
+- 计算机网络、数据结构、算法、操作系统等课内基础知识:掌握
+- Java 基础知识:掌握
+- JVM 虚拟机(Java内存区域、虚拟机垃圾算法、虚拟垃圾收集器、JVM内存管理):掌握
+- 高并发、高可用、高性能系统开发:掌握
+- Struts2、Spring、Hibernate、Ajax、Mybatis、JQuery :掌握
+- SSH 整合、SSM 整合、 SOA 架构:掌握
+- Dubbo: 掌握
+- Zookeeper: 掌握
+- 常见消息队列: 掌握
+- Linux:掌握
+- MySQL常见优化手段:掌握
+- Spring Boot +Spring Cloud +Docker:了解
+- Hadoop 生态相关技术中的 HDFS、Storm、MapReduce、Hive、Hbase :了解
+- Python 基础、一些常见第三方库比如OpenCV、wxpy、wordcloud、matplotlib:熟悉
+
+### 7 开源程序员Markdown格式简历模板分享
+
+分享一个Github上开源的程序员简历模板。包括PHP程序员简历模板、iOS程序员简历模板、Android程序员简历模板、Web前端程序员简历模板、Java程序员简历模板、C/C++程序员简历模板、NodeJS程序员简历模板、架构师简历模板以及通用程序员简历模板 。
+Github地址:[https://github.com/geekcompany/ResumeSample](https://github.com/geekcompany/ResumeSample)
+
+
+我的下面这篇文章讲了如何写一份Markdown格式的简历,另外,文中还提到了一种实现 Markdown 格式到PDF、HTML、JPEG这几种格式的转换方法。
+
+[手把手教你用Markdown写一份高质量的简历](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484347&idx=1&sn=a986ea7e199871999a5257bd3ed78be1&chksm=fd9855dacaefdccc2c5d5f8f79c4aa1b608ad5b42936bccaefb99a850a2e6e8e2e910e1b3153&token=719595858&lang=zh_CN#rd)
+
+### 8 其他的一些小tips
+
+1. 尽量避免主观表述,少一点语义模糊的形容词,尽量要简洁明了,逻辑结构清晰。
+2. 注意排版(不需要花花绿绿的),尽量使用Markdown语法。
+3. 如果自己有博客或者个人技术栈点的话,写上去会为你加分很多。
+4. 如果自己的Github比较活跃的话,写上去也会为你加分很多。
+5. 注意简历真实性,一定不要写自己不会的东西,或者带有欺骗性的内容
+6. 项目经历建议以时间倒序排序,另外项目经历不在于多,而在于有亮点。
+7. 如果内容过多的话,不需要非把内容压缩到一页,保持排版干净整洁就可以了。
+8. 简历最后最好能加上:“感谢您花时间阅读我的简历,期待能有机会和您共事。”这句话,显的你会很有礼貌。
diff --git "a/EssentialContentForInterview/PreparingForInterview/\347\276\216\345\233\242\351\235\242\350\257\225\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223.md" "b/EssentialContentForInterview/PreparingForInterview/\347\276\216\345\233\242\351\235\242\350\257\225\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223.md"
new file mode 100644
index 00000000000..0efdd618688
--- /dev/null
+++ "b/EssentialContentForInterview/PreparingForInterview/\347\276\216\345\233\242\351\235\242\350\257\225\345\270\270\350\247\201\351\227\256\351\242\230\346\200\273\347\273\223.md"
@@ -0,0 +1,950 @@
+
+
+- [一 基础篇](#一-基础篇)
+ - [1. `System.out.println(3|9)`输出什么?](#1-systemoutprintln39输出什么)
+ - [2. 说一下转发\(Forward\)和重定向\(Redirect\)的区别](#2-说一下转发forward和重定向redirect的区别)
+ - [3. 在浏览器中输入url地址到显示主页的过程,整个过程会使用哪些协议](#3-在浏览器中输入url地址到显示主页的过程整个过程会使用哪些协议)
+ - [4. TCP 三次握手和四次挥手](#4-tcp-三次握手和四次挥手)
+ - [为什么要三次握手](#为什么要三次握手)
+ - [为什么要传回 SYN](#为什么要传回-syn)
+ - [传了 SYN,为啥还要传 ACK](#传了-syn为啥还要传-ack)
+ - [为什么要四次挥手](#为什么要四次挥手)
+ - [5. IP地址与MAC地址的区别](#5-ip地址与mac地址的区别)
+ - [6. HTTP请求,响应报文格式](#6-http请求响应报文格式)
+ - [7. 为什么要使用索引?索引这么多优点,为什么不对表中的每一个列创建一个索引呢?索引是如何提高查询速度的?说一下使用索引的注意事项?Mysql索引主要使用的两种数据结构?什么是覆盖索引?](#7-为什么要使用索引索引这么多优点为什么不对表中的每一个列创建一个索引呢索引是如何提高查询速度的说一下使用索引的注意事项mysql索引主要使用的两种数据结构什么是覆盖索引)
+ - [8. 进程与线程的区别是什么?进程间的几种通信方式说一下?线程间的几种通信方式知道不?](#8-进程与线程的区别是什么进程间的几种通信方式说一下线程间的几种通信方式知道不)
+ - [9. 为什么要用单例模式?手写几种线程安全的单例模式?](#9-为什么要用单例模式手写几种线程安全的单例模式)
+ - [10. 简单介绍一下bean;知道Spring的bean的作用域与生命周期吗?](#10-简单介绍一下bean知道spring的bean的作用域与生命周期吗)
+ - [11. Spring 中的事务传播行为了解吗?TransactionDefinition 接口中哪五个表示隔离级别的常量?](#11-spring-中的事务传播行为了解吗transactiondefinition-接口中哪五个表示隔离级别的常量)
+ - [事务传播行为](#事务传播行为)
+ - [隔离级别](#隔离级别)
+ - [12. SpringMVC 原理了解吗?](#12-springmvc-原理了解吗)
+ - [13. Spring AOP IOC 实现原理](#13-spring-aop-ioc-实现原理)
+- [二 进阶篇](#二-进阶篇)
+ - [1 消息队列MQ的套路](#1-消息队列mq的套路)
+ - [1.1 介绍一下消息队列MQ的应用场景/使用消息队列的好处](#11-介绍一下消息队列mq的应用场景使用消息队列的好处)
+ - [1)通过异步处理提高系统性能](#1通过异步处理提高系统性能)
+ - [2)降低系统耦合性](#2降低系统耦合性)
+ - [1.2 那么使用消息队列会带来什么问题?考虑过这些问题吗?](#12-那么使用消息队列会带来什么问题考虑过这些问题吗)
+ - [1.3 介绍一下你知道哪几种消息队列,该如何选择呢?](#13-介绍一下你知道哪几种消息队列该如何选择呢)
+ - [1.4 关于消息队列其他一些常见的问题展望](#14-关于消息队列其他一些常见的问题展望)
+ - [2 谈谈 InnoDB 和 MyIsam 两者的区别](#2-谈谈-innodb-和-myisam-两者的区别)
+ - [2.1 两者的对比](#21-两者的对比)
+ - [2.2 关于两者的总结](#22-关于两者的总结)
+ - [3 聊聊 Java 中的集合吧!](#3-聊聊-java-中的集合吧)
+ - [3.1 Arraylist 与 LinkedList 有什么不同?\(注意加上从数据结构分析的内容\)](#31-arraylist-与-linkedlist-有什么不同注意加上从数据结构分析的内容)
+ - [3.2 HashMap的底层实现](#32-hashmap的底层实现)
+ - [1)JDK1.8之前](#1jdk18之前)
+ - [2)JDK1.8之后](#2jdk18之后)
+ - [3.3 既然谈到了红黑树,你给我手绘一个出来吧,然后简单讲一下自己对于红黑树的理解](#33-既然谈到了红黑树你给我手绘一个出来吧然后简单讲一下自己对于红黑树的理解)
+ - [3.4 红黑树这么优秀,为何不直接使用红黑树得了?](#34-红黑树这么优秀为何不直接使用红黑树得了)
+ - [3.5 HashMap 和 Hashtable 的区别/HashSet 和 HashMap 区别](#35-hashmap-和-hashtable-的区别hashset-和-hashmap-区别)
+- [三 终结篇](#三-终结篇)
+ - [1. Object类有哪些方法?](#1-object类有哪些方法)
+ - [1.1 Object类的常见方法总结](#11-object类的常见方法总结)
+ - [1.2 hashCode与equals](#12-hashcode与equals)
+ - [1.2.1 hashCode\(\)介绍](#121-hashcode介绍)
+ - [1.2.2 为什么要有hashCode](#122-为什么要有hashcode)
+ - [1.2.3 hashCode\(\)与equals\(\)的相关规定](#123-hashcode与equals的相关规定)
+ - [1.2.4 为什么两个对象有相同的hashcode值,它们也不一定是相等的?](#124-为什么两个对象有相同的hashcode值它们也不一定是相等的)
+ - [1.3 ==与equals](#13-与equals)
+ - [2 ConcurrentHashMap 相关问题](#2-concurrenthashmap-相关问题)
+ - [2.1 ConcurrentHashMap 和 Hashtable 的区别](#21-concurrenthashmap-和-hashtable-的区别)
+ - [2.2 ConcurrentHashMap线程安全的具体实现方式/底层具体实现](#22-concurrenthashmap线程安全的具体实现方式底层具体实现)
+ - [JDK1.7\(上面有示意图\)](#jdk17上面有示意图)
+ - [JDK1.8\(上面有示意图\)](#jdk18上面有示意图)
+ - [3 谈谈 synchronized 和 ReenTrantLock 的区别](#3-谈谈-synchronized-和-reentrantlock-的区别)
+ - [4 线程池了解吗?](#4-线程池了解吗)
+ - [4.1 为什么要用线程池?](#41-为什么要用线程池)
+ - [4.2 Java 提供了哪几种线程池?他们各自的使用场景是什么?](#42-java-提供了哪几种线程池他们各自的使用场景是什么)
+ - [Java 主要提供了下面4种线程池](#java-主要提供了下面4种线程池)
+ - [各种线程池的适用场景介绍](#各种线程池的适用场景介绍)
+ - [4.3 创建的线程池的方式](#43-创建的线程池的方式)
+ - [5 Nginx](#5-nginx)
+ - [5.1 简单介绍一下Nginx](#51-简单介绍一下nginx)
+ - [反向代理](#反向代理)
+ - [负载均衡](#负载均衡)
+ - [动静分离](#动静分离)
+ - [5.2 为什么要用 Nginx?](#52-为什么要用-nginx)
+ - [5.3 Nginx 的四个主要组成部分了解吗?](#53-nginx-的四个主要组成部分了解吗)
+
+
+
+
+这些问题是2018年去美团面试的同学被问到的一些常见的问题,希望对你有帮助!
+
+# 一 基础篇
+
+
+## 1. `System.out.println(3|9)`输出什么?
+
+正确答案:11.
+
+**考察知识点:&和&&;|和||**
+
+**&和&&:**
+
+共同点:两者都可做逻辑运算符。它们都表示运算符的两边都是true时,结果为true;
+
+不同点: &也是位运算符。& 表示在运算时两边都会计算,然后再判断;&&表示先运算符号左边的东西,然后判断是否为true,是true就继续运算右边的然后判断并输出,是false就停下来直接输出不会再运行后面的东西。
+
+**|和||:**
+
+共同点:两者都可做逻辑运算符。它们都表示运算符的两边任意一边为true,结果为true,两边都不是true,结果就为false;
+
+不同点:|也是位运算符。| 表示两边都会运算,然后再判断结果;|| 表示先运算符号左边的东西,然后判断是否为true,是true就停下来直接输出不会再运行后面的东西,是false就继续运算右边的然后判断并输出。
+
+**回到本题:**
+
+3 | 9=0011(二进制) | 1001(二进制)=1011(二进制)=11(十进制)
+
+## 2. 说一下转发(Forward)和重定向(Redirect)的区别
+
+**转发是服务器行为,重定向是客户端行为。**
+
+**转发(Forword)** 通过RequestDispatcher对象的`forward(HttpServletRequest request,HttpServletResponse response)`方法实现的。`RequestDispatcher` 可以通过`HttpServletRequest` 的 `getRequestDispatcher()`方法获得。例如下面的代码就是跳转到 login_success.jsp 页面。
+
+```java
+request.getRequestDispatcher("login_success.jsp").forward(request, response);
+```
+
+**重定向(Redirect)** 是利用服务器返回的状态吗来实现的。客户端浏览器请求服务器的时候,服务器会返回一个状态码。服务器通过HttpServletRequestResponse的setStatus(int status)方法设置状态码。如果服务器返回301或者302,则浏览器会到新的网址重新请求该资源。
+
+1. **从地址栏显示来说:** forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址. redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.
+2. **从数据共享来说:** forward:转发页面和转发到的页面可以共享request里面的数据. redirect:不能共享数据.
+3. **从运用地方来说:** forward:一般用于用户登陆的时候,根据角色转发到相应的模块. redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等
+4. **从效率来说:** forward:高. redirect:低.
+
+
+## 3. 在浏览器中输入url地址到显示主页的过程,整个过程会使用哪些协议
+
+图片来源:《图解HTTP》:
+
+
+
+总体来说分为以下几个过程:
+
+1. DNS解析
+2. TCP连接
+3. 发送HTTP请求
+4. 服务器处理请求并返回HTTP报文
+5. 浏览器解析渲染页面
+6. 连接结束
+
+具体可以参考下面这篇文章:
+
+- [https://segmentfault.com/a/1190000006879700](https://segmentfault.com/a/1190000006879700)
+
+## 4. TCP 三次握手和四次挥手
+
+为了准确无误地把数据送达目标处,TCP协议采用了三次握手策略。
+
+**漫画图解:**
+
+图片来源:《图解HTTP》
+
+
+**简单示意图:**
+
+
+- 客户端–发送带有 SYN 标志的数据包–一次握手–服务端
+- 服务端–发送带有 SYN/ACK 标志的数据包–二次握手–客户端
+- 客户端–发送带有带有 ACK 标志的数据包–三次握手–服务端
+
+#### 为什么要三次握手
+
+**三次握手的目的是建立可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收是正常的。**
+
+第一次握手:Client 什么都不能确认;Server 确认了对方发送正常,自己接收正常。
+
+第二次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己接收正常,对方发送正常
+
+第三次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常,对方发送接收正常
+
+所以三次握手就能确认双发收发功能都正常,缺一不可。
+
+#### 为什么要传回 SYN
+接收端传回发送端所发送的 SYN 是为了告诉发送端,我接收到的信息确实就是你所发送的信号了。
+
+> SYN 是 TCP/IP 建立连接时使用的握手信号。在客户机和服务器之间建立正常的 TCP 网络连接时,客户机首先发出一个 SYN 消息,服务器使用 SYN-ACK 应答表示接收到了这个消息,最后客户机再以 ACK(Acknowledgement[汉译:确认字符 ,在数据通信传输中,接收站发给发送站的一种传输控制字符。它表示确认发来的数据已经接受无误。 ])消息响应。这样在客户机和服务器之间才能建立起可靠的TCP连接,数据才可以在客户机和服务器之间传递。
+
+
+#### 传了 SYN,为啥还要传 ACK
+
+双方通信无误必须是两者互相发送信息都无误。传了 SYN,证明发送方(主动关闭方)到接收方(被动关闭方)的通道没有问题,但是接收方到发送方的通道还需要 ACK 信号来进行验证。
+
+
+
+断开一个 TCP 连接则需要“四次挥手”:
+
+- 客户端-发送一个 FIN,用来关闭客户端到服务器的数据传送
+- 服务器-收到这个 FIN,它发回一 个 ACK,确认序号为收到的序号加1 。和 SYN 一样,一个 FIN 将占用一个序号
+- 服务器-关闭与客户端的连接,发送一个FIN给客户端
+- 客户端-发回 ACK 报文确认,并将确认序号设置为收到序号加1
+
+
+#### 为什么要四次挥手
+
+任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了TCP连接。
+
+举个例子:A 和 B 打电话,通话即将结束后,A 说“我没啥要说的了”,B回答“我知道了”,但是 B 可能还会有要说的话,A 不能要求 B 跟着自己的节奏结束通话,于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”,A 回答“知道了”,这样通话才算结束。
+
+上面讲的比较概括,推荐一篇讲的比较细致的文章:[https://blog.csdn.net/qzcsu/article/details/72861891](https://blog.csdn.net/qzcsu/article/details/72861891)
+
+
+
+## 5. IP地址与MAC地址的区别
+
+参考:[https://blog.csdn.net/guoweimelon/article/details/50858597](https://blog.csdn.net/guoweimelon/article/details/50858597)
+
+IP地址是指互联网协议地址(Internet Protocol Address)IP Address的缩写。IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。
+
+
+
+MAC 地址又称为物理地址、硬件地址,用来定义网络设备的位置。网卡的物理地址通常是由网卡生产厂家写入网卡的,具有全球唯一性。MAC地址用于在网络中唯一标示一个网卡,一台电脑会有一或多个网卡,每个网卡都需要有一个唯一的MAC地址。
+
+## 6. HTTP请求,响应报文格式
+
+
+
+HTTP请求报文主要由请求行、请求头部、请求正文3部分组成
+
+HTTP响应报文主要由状态行、响应头部、响应正文3部分组成
+
+详细内容可以参考:[https://blog.csdn.net/a19881029/article/details/14002273](https://blog.csdn.net/a19881029/article/details/14002273)
+
+## 7. 为什么要使用索引?索引这么多优点,为什么不对表中的每一个列创建一个索引呢?索引是如何提高查询速度的?说一下使用索引的注意事项?Mysql索引主要使用的两种数据结构?什么是覆盖索引?
+
+**为什么要使用索引?**
+
+1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
+2. 可以大大加快 数据的检索速度(大大减少的检索的数据量), 这也是创建索引的最主要的原因。
+3. 帮助服务器避免排序和临时表
+4. 将随机IO变为顺序IO
+5. 可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
+
+**索引这么多优点,为什么不对表中的每一个列创建一个索引呢?**
+
+1. 当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。
+2. 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。
+3. 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
+
+**索引是如何提高查询速度的?**
+
+将无序的数据变成相对有序的数据(就像查目录一样)
+
+**说一下使用索引的注意事项**
+
+1. 避免 where 子句中对字段施加函数,这会造成无法命中索引。
+2. 在使用InnoDB时使用与业务无关的自增主键作为主键,即使用逻辑主键,而不要使用业务主键。
+3. 将打算加索引的列设置为 NOT NULL ,否则将导致引擎放弃使用索引而进行全表扫描
+4. 删除长期未使用的索引,不用的索引的存在会造成不必要的性能损耗 MySQL 5.7 可以通过查询 sys 库的 schema_unused_indexes 视图来查询哪些索引从未被使用
+5. 在使用 limit offset 查询缓慢时,可以借助索引来提高性能
+
+**Mysql索引主要使用的哪两种数据结构?**
+
+- 哈希索引:对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTree索引。
+- BTree索引:Mysql的BTree索引使用的是B树中的B+Tree。但对于主要的两种存储引擎(MyISAM和InnoDB)的实现方式是不同的。
+
+更多关于索引的内容可以查看我的这篇文章:[【思维导图-索引篇】搞定数据库索引就是这么简单](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484486&idx=1&sn=215450f11e042bca8a58eac9f4a97686&chksm=fd985227caefdb3117b8375f150676f5824aa20d1ebfdbcfb93ff06e23e26efbafae6cf6b48e&token=1990180468&lang=zh_CN#rd)
+
+**什么是覆盖索引?**
+
+如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称
+之为“覆盖索引”。我们知道在InnoDB存储引擎中,如果不是主键索引,叶子节点存储的是主键+列值。最终还是要“回表”,也就是要通过主键再查找一次,这样就会比较慢。覆盖索引就是把要查询出的列和索引是对应的,不做回表操作!
+
+
+## 8. 进程与线程的区别是什么?进程间的几种通信方式说一下?线程间的几种通信方式知道不?
+ **进程与线程的区别是什么?**
+
+线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。另外,也正是因为共享资源,所以线程中执行时一般都要进行同步和互斥。总的来说,进程和线程的主要差别在于它们是不同的操作系统资源管理方式。
+
+**进程间的几种通信方式说一下?**
+
+
+1. **管道(pipe)**:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有血缘关系的进程间使用。进程的血缘关系通常指父子进程关系。管道分为pipe(无名管道)和fifo(命名管道)两种,有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间通信。
+2. **信号量(semophore)**:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它通常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
+3. **消息队列(message queue)**:消息队列是由消息组成的链表,存放在内核中 并由消息队列标识符标识。消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。消息队列与管道通信相比,其优势是对每个消息指定特定的消息类型,接收的时候不需要按照队列次序,而是可以根据自定义条件接收特定类型的消息。
+4. **信号(signal)**:信号是一种比较复杂的通信方式,用于通知接收进程某一事件已经发生。
+5. **共享内存(shared memory)**:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问,共享内存是最快的IPC方式,它是针对其他进程间的通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量配合使用,来实现进程间的同步和通信。
+6. **套接字(socket)**:socket,即套接字是一种通信机制,凭借这种机制,客户/服务器(即要进行通信的进程)系统的开发工作既可以在本地单机上进行,也可以跨网络进行。也就是说它可以让不在同一台计算机但通过网络连接计算机上的进程进行通信。也因为这样,套接字明确地将客户端和服务器区分开来。
+
+**线程间的几种通信方式知道不?**
+
+1、锁机制
+
+- 互斥锁:提供了以排它方式阻止数据结构被并发修改的方法。
+- 读写锁:允许多个线程同时读共享数据,而对写操作互斥。
+- 条件变量:可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
+
+2、信号量机制:包括无名线程信号量与有名线程信号量
+
+3、信号机制:类似于进程间的信号处理。
+
+线程间通信的主要目的是用于线程同步,所以线程没有象进程通信中用于数据交换的通信机制。
+
+## 9. 为什么要用单例模式?手写几种线程安全的单例模式?
+
+**简单来说使用单例模式可以带来下面几个好处:**
+
+- 对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销;
+- 由于 new 操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻 GC 压力,缩短 GC 停顿时间。
+
+**懒汉式(双重检查加锁版本)**
+
+```java
+public class Singleton {
+
+ //volatile保证,当uniqueInstance变量被初始化成Singleton实例时,多个线程可以正确处理uniqueInstance变量
+ private volatile static Singleton uniqueInstance;
+ private Singleton() {
+ }
+ public static Singleton getInstance() {
+ //检查实例,如果不存在,就进入同步代码块
+ if (uniqueInstance == null) {
+ //只有第一次才彻底执行这里的代码
+ synchronized(Singleton.class) {
+ //进入同步代码块后,再检查一次,如果仍是null,才创建实例
+ if (uniqueInstance == null) {
+ uniqueInstance = new Singleton();
+ }
+ }
+ }
+ return uniqueInstance;
+ }
+}
+```
+
+**静态内部类方式**
+
+静态内部实现的单例是懒加载的且线程安全。
+
+只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance(只有第一次使用这个单例的实例的时候才加载,同时不会有线程安全问题)。
+
+```java
+public class Singleton {
+ private static class SingletonHolder {
+ private static final Singleton INSTANCE = new Singleton();
+ }
+ private Singleton (){}
+ public static final Singleton getInstance() {
+ return SingletonHolder.INSTANCE;
+ }
+}
+```
+
+## 10. 简单介绍一下bean;知道Spring的bean的作用域与生命周期吗?
+
+在 Spring 中,那些组成应用程序的主体及由 Spring IOC 容器所管理的对象,被称之为 bean。简单地讲,bean 就是由 IOC 容器初始化、装配及管理的对象,除此之外,bean 就与应用程序中的其他对象没有什么区别了。而 bean 的定义以及 bean 相互间的依赖关系将通过配置元数据来描述。
+
+Spring中的bean默认都是单例的,这些单例Bean在多线程程序下如何保证线程安全呢? 例如对于Web应用来说,Web容器对于每个用户请求都创建一个单独的Sevlet线程来处理请求,引入Spring框架之后,每个Action都是单例的,那么对于Spring托管的单例Service Bean,如何保证其安全呢? Spring的单例是基于BeanFactory也就是Spring容器的,单例Bean在此容器内只有一个,Java的单例是基于 JVM,每个 JVM 内只有一个实例。
+
+
+
+Spring的bean的生命周期以及更多内容可以查看:[一文轻松搞懂Spring中bean的作用域与生命周期](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484400&idx=2&sn=7201eb365102fce017f89cb3527fb0bc&chksm=fd985591caefdc872a2fac897288119f94c345e4e12150774f960bf5f816b79e4b9b46be3d7f&token=1990180468&lang=zh_CN#rd)
+
+
+## 11. Spring 中的事务传播行为了解吗?TransactionDefinition 接口中哪五个表示隔离级别的常量?
+
+#### 事务传播行为
+
+事务传播行为(为了解决业务层方法之间互相调用的事务问题):
+当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
+
+**支持当前事务的情况:**
+
+- TransactionDefinition.PROPAGATION_REQUIRED: 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
+- TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
+- TransactionDefinition.PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)
+
+**不支持当前事务的情况:**
+
+- TransactionDefinition.PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
+- TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
+- TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。
+
+**其他情况:**
+
+- TransactionDefinition.PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
+
+
+#### 隔离级别
+
+TransactionDefinition 接口中定义了五个表示隔离级别的常量:
+
+- **TransactionDefinition.ISOLATION_DEFAULT:** 使用后端数据库默认的隔离级别,Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别.
+- **TransactionDefinition.ISOLATION_READ_UNCOMMITTED:** 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
+- **TransactionDefinition.ISOLATION_READ_COMMITTED:** 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
+- **TransactionDefinition.ISOLATION_REPEATABLE_READ:** 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
+- **TransactionDefinition.ISOLATION_SERIALIZABLE:** 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
+
+## 12. SpringMVC 原理了解吗?
+
+
+
+客户端发送请求-> 前端控制器 DispatcherServlet 接受客户端请求 -> 找到处理器映射 HandlerMapping 解析请求对应的 Handler-> HandlerAdapter 会根据 Handler 来调用真正的处理器开处理请求,并处理相应的业务逻辑 -> 处理器返回一个模型视图 ModelAndView -> 视图解析器进行解析 -> 返回一个视图对象->前端控制器 DispatcherServlet 渲染数据(Model)->将得到视图对象返回给用户
+
+关于 SpringMVC 原理更多内容可以查看我的这篇文章:[SpringMVC 工作原理详解](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484496&idx=1&sn=5472ffa687fe4a05f8900d8ee6726de4&chksm=fd985231caefdb27fc75b44ecf76b6f43e4617e0b01b3c040f8b8fab32e51dfa5118eed1d6ad&token=1990180468&lang=zh_CN#rd)
+
+## 13. Spring AOP IOC 实现原理
+
+过了秋招挺长一段时间了,说实话我自己也忘了如何简要概括 Spring AOP IOC 实现原理,就在网上找了一个较为简洁的答案,下面分享给各位。
+
+**IOC:** 控制反转也叫依赖注入。IOC利用java反射机制,AOP利用代理模式。IOC 概念看似很抽象,但是很容易理解。说简单点就是将对象交给容器管理,你只需要在spring配置文件中配置对应的bean以及设置相关的属性,让spring容器来生成类的实例对象以及管理对象。在spring容器启动的时候,spring会把你在配置文件中配置的bean都初始化好,然后在你需要调用的时候,就把它已经初始化好的那些bean分配给你需要调用这些bean的类。
+
+**AOP:** 面向切面编程。(Aspect-Oriented Programming) 。AOP可以说是对OOP的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码,属于静态代理。
+
+
+
+# 二 进阶篇
+
+## 1 消息队列MQ的套路
+
+消息队列/消息中间件应该是Java程序员必备的一个技能了,如果你之前没接触过消息队列的话,建议先去百度一下某某消息队列入门,然后花2个小时就差不多可以学会任何一种消息队列的使用了。如果说仅仅学会使用是万万不够的,在实际生产环境还要考虑消息丢失等等情况。关于消息队列面试相关的问题,推荐大家也可以看一下视频《Java工程师面试突击第1季-中华石杉老师》,如果大家没有资源的话,可以在我的公众号“Java面试通关手册”后台回复关键字“1”即可!
+
+### 1.1 介绍一下消息队列MQ的应用场景/使用消息队列的好处
+
+面试官一般会先问你这个问题,预热一下,看你知道消息队列不,一般在第一面的时候面试官可能只会问消息队列MQ的应用场景/使用消息队列的好处、使用消息队列会带来什么问题、消息队列的技术选型这几个问题,不会太深究下去,在后面的第二轮/第三轮技术面试中可能会深入问一下。
+
+**《大型网站技术架构》第四章和第七章均有提到消息队列对应用性能及扩展性的提升。**
+
+#### 1)通过异步处理提高系统性能
+
+如上图,**在不使用消息队列服务器的时候,用户的请求数据直接写入数据库,在高并发的情况下数据库压力剧增,使得响应速度变慢。但是在使用消息队列之后,用户的请求数据发送给消息队列之后立即 返回,再由消息队列的消费者进程从消息队列中获取数据,异步写入数据库。由于消息队列服务器处理速度快于数据库(消息队列也比数据库有更好的伸缩性),因此响应速度得到大幅改善。**
+
+通过以上分析我们可以得出**消息队列具有很好的削峰作用的功能**——即**通过异步处理,将短时间高并发产生的事务消息存储在消息队列中,从而削平高峰期的并发事务。** 举例:在电子商务一些秒杀、促销活动中,合理使用消息队列可以有效抵御促销活动刚开始大量订单涌入对系统的冲击。如下图所示:
+
+因为**用户请求数据写入消息队列之后就立即返回给用户了,但是请求数据在后续的业务校验、写数据库等操作中可能失败**。因此使用消息队列进行异步处理之后,需要**适当修改业务流程进行配合**,比如**用户在提交订单之后,订单数据写入消息队列,不能立即返回用户订单提交成功,需要在消息队列的订单消费者进程真正处理完该订单之后,甚至出库后,再通过电子邮件或短信通知用户订单成功**,以免交易纠纷。这就类似我们平时手机订火车票和电影票。
+
+#### 2)降低系统耦合性
+我们知道模块分布式部署以后聚合方式通常有两种:1.**分布式消息队列**和2.**分布式服务**。
+
+> **先来简单说一下分布式服务:**
+
+目前使用比较多的用来构建**SOA(Service Oriented Architecture面向服务体系结构)**的**分布式服务框架**是阿里巴巴开源的**Dubbo**.如果想深入了解Dubbo的可以看我写的关于Dubbo的这一篇文章:**《高性能优秀的服务框架-dubbo介绍》**:[https://juejin.im/post/5acadeb1f265da2375072f9c](https://juejin.im/post/5acadeb1f265da2375072f9c)
+
+> **再来谈我们的分布式消息队列:**
+
+我们知道如果模块之间不存在直接调用,那么新增模块或者修改模块就对其他模块影响较小,这样系统的可扩展性无疑更好一些。
+
+我们最常见的**事件驱动架构**类似生产者消费者模式,在大型网站中通常用利用消息队列实现事件驱动结构。如下图所示:
+
+**消息队列使利用发布-订阅模式工作,消息发送者(生产者)发布消息,一个或多个消息接受者(消费者)订阅消息。** 从上图可以看到**消息发送者(生产者)和消息接受者(消费者)之间没有直接耦合**,消息发送者将消息发送至分布式消息队列即结束对消息的处理,消息接受者从分布式消息队列获取该消息后进行后续处理,并不需要知道该消息从何而来。**对新增业务,只要对该类消息感兴趣,即可订阅该消息,对原有系统和业务没有任何影响,从而实现网站业务的可扩展性设计**。
+
+消息接受者对消息进行过滤、处理、包装后,构造成一个新的消息类型,将消息继续发送出去,等待其他消息接受者订阅该消息。因此基于事件(消息对象)驱动的业务架构可以是一系列流程。
+
+**另外为了避免消息队列服务器宕机造成消息丢失,会将成功发送到消息队列的消息存储在消息生产者服务器上,等消息真正被消费者服务器处理后才删除消息。在消息队列服务器宕机后,生产者服务器会选择分布式消息队列服务器集群中的其他服务器发布消息。**
+
+**备注:** 不要认为消息队列只能利用发布-订阅模式工作,只不过在解耦这个特定业务环境下是使用发布-订阅模式的,**比如在我们的ActiveMQ消息队列中还有点对点工作模式**,具体的会在后面的文章给大家详细介绍,这一篇文章主要还是让大家对消息队列有一个更透彻的了解。
+
+> 这个问题一般会在上一个问题问完之后,紧接着被问到。“使用消息队列会带来什么问题?”这个问题要引起重视,一般我们都会考虑使用消息队列会带来的好处而忽略它带来的问题!
+
+### 1.2 那么使用消息队列会带来什么问题?考虑过这些问题吗?
+
+- **系统可用性降低:** 系统可用性在某种程度上降低,为什么这样说呢?在加入MQ之前,你不用考虑消息丢失或者说MQ挂掉等等的情况,但是,引入MQ之后你就需要去考虑了!
+- **系统复杂性提高:** 加入MQ之后,你需要保证消息没有被重复消费、处理消息丢失的情况、保证消息传递的顺序性等等问题!
+- **一致性问题:** 我上面讲了消息队列可以实现异步,消息队列带来的异步确实可以提高系统响应速度。但是,万一消息的真正消费者并没有正确消费消息怎么办?这样就会导致数据不一致的情况了!
+
+> 了解下面这个问题是为了我们更好的进行技术选型!该部分摘自:《Java工程师面试突击第1季-中华石杉老师》,如果大家没有资源的话,可以在我的公众号“Java面试通关手册”后台回复关键字“1”即可!
+
+### 1.3 介绍一下你知道哪几种消息队列,该如何选择呢?
+
+
+| 特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafaka |
+| :---------------------- | -----------------------------------------------------------: | -----------------------------------------------------------: | -----------------------------------------------------------: | -----------------------------------------------------------: |
+| 单机吞吐量 | 万级,吞吐量比RocketMQ和Kafka要低了一个数量级 | 万级,吞吐量比RocketMQ和Kafka要低了一个数量级 | 10万级,RocketMQ也是可以支撑高吞吐的一种MQ | 10万级别,这是kafka最大的优点,就是吞吐量高。一般配合大数据类的系统来进行实时数据计算、日志采集等场景 |
+| topic数量对吞吐量的影响 | | | topic可以达到几百,几千个的级别,吞吐量会有较小幅度的下降这是RocketMQ的一大优势,在同等机器下,可以支撑大量的topic | topic从几十个到几百个的时候,吞吐量会大幅度下降。所以在同等机器下,kafka尽量保证topic数量不要过多。如果要支撑大规模topic,需要增加更多的机器资源 |
+| 可用性 | 高,基于主从架构实现高可用性 | 高,基于主从架构实现高可用性 | 非常高,分布式架构 | 非常高,kafka是分布式的,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用 |
+| 消息可靠性 | 有较低的概率丢失数据 | | 经过参数优化配置,可以做到0丢失 | 经过参数优化配置,消息可以做到0丢失 |
+| 时效性 | ms级 | 微秒级,这是rabbitmq的一大特点,延迟是最低的 | ms级 | 延迟在ms级以内 |
+| 功能支持 | MQ领域的功能极其完备 | 基于erlang开发,所以并发能力很强,性能极其好,延时很低 | MQ功能较为完善,还是分布式的,扩展性好 | 功能较为简单,主要支持简单的MQ功能,在大数据领域的实时计算以及日志采集被大规模使用,是事实上的标准 |
+| 优劣势总结 | 非常成熟,功能强大,在业内大量的公司以及项目中都有应用。偶尔会有较低概率丢失消息,而且现在社区以及国内应用都越来越少,官方社区现在对ActiveMQ 5.x维护越来越少,几个月才发布一个版本而且确实主要是基于解耦和异步来用的,较少在大规模吞吐的场景中使用 | erlang语言开发,性能极其好,延时很低;吞吐量到万级,MQ功能比较完备而且开源提供的管理界面非常棒,用起来很好用。社区相对比较活跃,几乎每个月都发布几个版本分在国内一些互联网公司近几年用rabbitmq也比较多一些但是问题也是显而易见的,RabbitMQ确实吞吐量会低一些,这是因为他做的实现机制比较重。而且erlang开发,国内有几个公司有实力做erlang源码级别的研究和定制?如果说你没这个实力的话,确实偶尔会有一些问题,你很难去看懂源码,你公司对这个东西的掌控很弱,基本职能依赖于开源社区的快速维护和修复bug。而且rabbitmq集群动态扩展会很麻烦,不过这个我觉得还好。其实主要是erlang语言本身带来的问题。很难读源码,很难定制和掌控。 | 接口简单易用,而且毕竟在阿里大规模应用过,有阿里品牌保障。日处理消息上百亿之多,可以做到大规模吞吐,性能也非常好,分布式扩展也很方便,社区维护还可以,可靠性和可用性都是ok的,还可以支撑大规模的topic数量,支持复杂MQ业务场景。而且一个很大的优势在于,阿里出品都是java系的,我们可以自己阅读源码,定制自己公司的MQ,可以掌控。社区活跃度相对较为一般,不过也还可以,文档相对来说简单一些,然后接口这块不是按照标准JMS规范走的有些系统要迁移需要修改大量代码。还有就是阿里出台的技术,你得做好这个技术万一被抛弃,社区黄掉的风险,那如果你们公司有技术实力我觉得用RocketMQ挺好的 | kafka的特点其实很明显,就是仅仅提供较少的核心功能,但是提供超高的吞吐量,ms级的延迟,极高的可用性以及可靠性,而且分布式可以任意扩展。同时kafka最好是支撑较少的topic数量即可,保证其超高吞吐量。而且kafka唯一的一点劣势是有可能消息重复消费,那么对数据准确性会造成极其轻微的影响,在大数据领域中以及日志采集中,这点轻微影响可以忽略这个特性天然适合大数据实时计算以及日志收集。 |
+
+> 这部分内容,我这里不给出答案,大家可以自行根据自己学习的消息队列查阅相关内容,我可能会在后面的文章中介绍到这部分内容。另外,下面这些问题在视频《Java工程师面试突击第1季-中华石杉老师》中都有提到,如果大家没有资源的话,可以在我的公众号“Java面试通关手册”后台回复关键字“1”即可!
+
+### 1.4 关于消息队列其他一些常见的问题展望
+
+1. 引入消息队列之后如何保证高可用性
+2. 如何保证消息不被重复消费呢?
+3. 如何保证消息的可靠性传输(如何处理消息丢失的问题)?
+4. 我该怎么保证从消息队列里拿到的数据按顺序执行?
+5. 如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时,说说怎么解决?
+6. 如果让你来开发一个消息队列中间件,你会怎么设计架构?
+
+
+
+## 2 谈谈 InnoDB 和 MyIsam 两者的区别
+
+### 2.1 两者的对比
+
+1. **count运算上的区别:** 因为MyISAM缓存有表meta-data(行数等),因此在做COUNT(*)时对于一个结构很好的查询是不需要消耗多少资源的。而对于InnoDB来说,则没有这种缓存
+2. **是否支持事务和崩溃后的安全恢复:** MyISAM 强调的是性能,每次查询具有原子性,其执行数度比InnoDB类型更快,但是不提供事务支持。但是InnoDB 提供事务支持事务,外部键等高级数据库功能。 具有事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表。
+3. **是否支持外键:** MyISAM不支持,而InnoDB支持。
+
+
+### 2.2 关于两者的总结
+
+MyISAM更适合读密集的表,而InnoDB更适合写密集的的表。 在数据库做主从分离的情况下,经常选择MyISAM作为主库的存储引擎。
+
+一般来说,如果需要事务支持,并且有较高的并发读取频率(MyISAM的表锁的粒度太大,所以当该表写并发量较高时,要等待的查询就会很多了),InnoDB是不错的选择。如果你的数据量很大(MyISAM支持压缩特性可以减少磁盘的空间占用),而且不需要支持事务时,MyISAM是最好的选择。
+
+
+## 3 聊聊 Java 中的集合吧!
+
+### 3.1 Arraylist 与 LinkedList 有什么不同?(注意加上从数据结构分析的内容)
+
+- **1. 是否保证线程安全:** ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;
+- **2. 底层数据结构:** Arraylist 底层使用的是Object数组;LinkedList 底层使用的是双向链表数据结构(注意双向链表和双向循环链表的区别:);
+- **3. 插入和删除是否受元素位置的影响:** ① **ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。** 比如:执行`add(E e) `方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是O(1)。但是如果要在指定位置 i 插入和删除元素的话(`add(int index, E element) `)时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 ② **LinkedList 采用链表存储,所以插入,删除元素时间复杂度不受元素位置的影响,都是近似 O(1)而数组为近似 O(n)。**
+- **4. 是否支持快速随机访问:** LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于`get(int index) `方法)。
+- **5. 内存空间占用:** ArrayList的空 间浪费主要体现在在list列表的结尾会预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。
+
+**补充内容:RandomAccess接口**
+
+```java
+public interface RandomAccess {
+}
+```
+
+查看源码我们发现实际上 RandomAccess 接口中什么都没有定义。所以,在我看来 RandomAccess 接口不过是一个标识罢了。标识什么? 标识实现这个接口的类具有随机访问功能。
+
+在binarySearch()方法中,它要判断传入的list 是否RamdomAccess的实例,如果是,调用indexedBinarySearch()方法,如果不是,那么调用iteratorBinarySearch()方法
+
+```java
+ public static
+ int binarySearch(List extends Comparable super T>> list, T key) {
+ if (list instanceof RandomAccess || list.size() Java 中的集合这类问题几乎是面试必问的,问到这类问题的时候,HashMap 又是几乎必问的问题,所以大家一定要引起重视!
+
+### 3.2 HashMap的底层实现
+
+#### 1)JDK1.8之前
+
+JDK1.8 之前 HashMap 底层是 **数组和链表** 结合在一起使用也就是 **链表散列**。**HashMap 通过 key 的 hashCode 经过扰动函数处理过后得到 hash 值,然后通过 `(n - 1) & hash` 判断当前元素存放的位置(这里的 n 指的时数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。**
+
+**所谓扰动函数指的就是 HashMap 的 hash 方法。使用 hash 方法也就是扰动函数是为了防止一些实现比较差的 hashCode() 方法 换句话说使用扰动函数之后可以减少碰撞。**
+
+**JDK 1.8 HashMap 的 hash 方法源码:**
+
+JDK 1.8 的 hash方法 相比于 JDK 1.7 hash 方法更加简化,但是原理不变。
+
+```java
+ static final int hash(Object key) {
+ int h;
+ // key.hashCode():返回散列值也就是hashcode
+ // ^ :按位异或
+ // >>>:无符号右移,忽略符号位,空位都以0补齐
+ return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
+ }
+```
+对比一下 JDK1.7的 HashMap 的 hash 方法源码.
+
+```java
+static int hash(int h) {
+ // This function ensures that hashCodes that differ only by
+ // constant multiples at each bit position have a bounded
+ // number of collisions (approximately 8 at default load factor).
+
+ h ^= (h >>> 20) ^ (h >>> 12);
+ return h ^ (h >>> 7) ^ (h >>> 4);
+}
+```
+
+相比于 JDK1.8 的 hash 方法 ,JDK 1.7 的 hash 方法的性能会稍差一点点,因为毕竟扰动了 4 次。
+
+所谓 **“拉链法”** 就是:将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。
+
+
+
+
+
+
+#### 2)JDK1.8之后
+
+相比于之前的版本, JDK1.8之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。
+
+
+
+TreeMap、TreeSet以及JDK1.8之后的HashMap底层都用到了红黑树。红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。
+
+> 问完 HashMap 的底层原理之后,面试官可能就会紧接着问你 HashMap 底层数据结构相关的问题!
+
+### 3.3 既然谈到了红黑树,你给我手绘一个出来吧,然后简单讲一下自己对于红黑树的理解
+
+
+
+**红黑树特点:**
+
+1. 每个节点非红即黑;
+2. 根节点总是黑色的;
+3. 每个叶子节点都是黑色的空节点(NIL节点);
+4. 如果节点是红色的,则它的子节点必须是黑色的(反之不一定);
+5. 从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)
+
+
+**红黑树的应用:**
+
+TreeMap、TreeSet以及JDK1.8之后的HashMap底层都用到了红黑树。
+
+**为什么要用红黑树**
+
+简单来说红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。
+
+
+### 3.4 红黑树这么优秀,为何不直接使用红黑树得了?
+
+说一下自己对于这个问题的看法:我们知道红黑树属于(自)平衡二叉树,但是为了保持“平衡”是需要付出代价的,红黑树在插入新数据后可能需要通过左旋,右旋、变色这些操作来保持平衡,这费事啊。你说说我们引入红黑树就是为了查找数据快,如果链表长度很短的话,根本不需要引入红黑树的,你引入之后还要付出代价维持它的平衡。但是链表过长就不一样了。至于为什么选 8 这个值呢?通过概率统计所得,这个值是综合查询成本和新增元素成本得出的最好的一个值。
+
+### 3.5 HashMap 和 Hashtable 的区别/HashSet 和 HashMap 区别
+
+**HashMap 和 Hashtable 的区别**
+
+1. **线程是否安全:** HashMap 是非线程安全的,HashTable 是线程安全的;HashTable 内部的方法基本都经过 `synchronized` 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!);
+2. **效率:** 因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在代码中使用它;
+3. **对Null key 和Null value的支持:** HashMap 中,null 可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为 null。。但是在 HashTable 中 put 进的键值只要有一个 null,直接抛出 NullPointerException。
+4. **初始容量大小和每次扩充容量大小的不同 :** ①创建时如果不指定容量初始值,Hashtable 默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap 默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。②创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为2的幂次方大小(HashMap 中的`tableSizeFor()`方法保证,下面给出了源代码)。也就是说 HashMap 总是使用2的幂作为哈希表的大小,后面会介绍到为什么是2的幂次方。
+5. **底层数据结构:** JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。
+
+**HashSet 和 HashMap 区别**
+
+如果你看过 HashSet 源码的话就应该知道:HashSet 底层就是基于 HashMap 实现的。(HashSet 的源码非常非常少,因为除了 clone() 方法、writeObject()方法、readObject()方法是 HashSet 自己不得不实现之外,其他方法都是直接调用 HashMap 中的方法。)
+
+
+
+# 三 终结篇
+
+## 1. Object类有哪些方法?
+
+这个问题,面试中经常出现。我觉得不论是出于应付面试还是说更好地掌握Java这门编程语言,大家都要掌握!
+
+### 1.1 Object类的常见方法总结
+
+Object类是一个特殊的类,是所有类的父类。它主要提供了以下11个方法:
+
+```java
+
+public final native Class> getClass()//native方法,用于返回当前运行时对象的Class对象,使用了final关键字修饰,故不允许子类重写。
+
+public native int hashCode() //native方法,用于返回对象的哈希码,主要使用在哈希表中,比如JDK中的HashMap。
+public boolean equals(Object obj)//用于比较2个对象的内存地址是否相等,String类对该方法进行了重写用户比较字符串的值是否相等。
+
+protected native Object clone() throws CloneNotSupportedException//naitive方法,用于创建并返回当前对象的一份拷贝。一般情况下,对于任何对象 x,表达式 x.clone() != x 为true,x.clone().getClass() == x.getClass() 为true。Object本身没有实现Cloneable接口,所以不重写clone方法并且进行调用的话会发生CloneNotSupportedException异常。
+
+public String toString()//返回类的名字@实例的哈希码的16进制的字符串。建议Object所有的子类都重写这个方法。
+
+public final native void notify()//native方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个。
+
+public final native void notifyAll()//native方法,并且不能重写。跟notify一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程。
+
+public final native void wait(long timeout) throws InterruptedException//native方法,并且不能重写。暂停线程的执行。注意:sleep方法没有释放锁,而wait方法释放了锁 。timeout是等待时间。
+
+public final void wait(long timeout, int nanos) throws InterruptedException//多了nanos参数,这个参数表示额外时间(以毫微秒为单位,范围是 0-999999)。 所以超时的时间还需要加上nanos毫秒。
+
+public final void wait() throws InterruptedException//跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念
+
+protected void finalize() throws Throwable { }//实例被垃圾回收器回收的时候触发的操作
+
+```
+
+> 问完上面这个问题之后,面试官很可能紧接着就会问你“hashCode与equals”相关的问题。
+
+### 1.2 hashCode与equals
+
+面试官可能会问你:“你重写过 hashcode 和 equals 么,为什么重写equals时必须重写hashCode方法?”
+
+#### 1.2.1 hashCode()介绍
+
+hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 函数。另外需要注意的是: Object 的 hashcode 方法是本地方法,也就是用 c 语言或 c++ 实现的,该方法通常用来将对象的 内存地址 转换为整数之后返回。
+
+```java
+ public native int hashCode();
+```
+
+散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)
+
+#### 1.2.2 为什么要有hashCode
+
+
+**我们以“HashSet如何检查重复”为例子来说明为什么要有hashCode:**
+
+当你把对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,同时也会与其他已经加入的对象的hashcode值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同hashcode值的对象,这时会调用equals()方法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。(摘自我的Java启蒙书《Head fist java》第二版)。这样我们就大大减少了equals的次数,相应就大大提高了执行速度。
+
+
+#### 1.2.3 hashCode()与equals()的相关规定
+
+1. 如果两个对象相等,则hashcode一定也是相同的
+2. 两个对象相等,对两个对象分别调用equals方法都返回true
+3. 两个对象有相同的hashcode值,它们也不一定是相等的
+4. **因此,equals方法被覆盖过,则hashCode方法也必须被覆盖**
+5. hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
+
+#### 1.2.4 为什么两个对象有相同的hashcode值,它们也不一定是相等的?
+
+在这里解释一位小伙伴的问题。以下内容摘自《Head Fisrt Java》。
+
+因为hashCode() 所使用的杂凑算法也许刚好会让多个对象传回相同的杂凑值。越糟糕的杂凑算法越容易碰撞,但这也与数据值域分布的特性有关(所谓碰撞也就是指的是不同的对象得到相同的 hashCode)。
+
+我们刚刚也提到了 HashSet,如果 HashSet 在对比的时候,同样的 hashcode 有多个对象,它会使用 equals() 来判断是否真的相同。也就是说 hashcode 只是用来缩小查找成本。
+
+> ==与equals 的对比也是比较常问的基础问题之一!
+
+### 1.3 ==与equals
+
+**==** : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。(基本数据类型==比较的是值,引用数据类型==比较的是内存地址)
+
+**equals()** : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
+
+- 情况1:类没有覆盖equals()方法。则通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。
+- 情况2:类覆盖了equals()方法。一般,我们都覆盖equals()方法来两个对象的内容相等;若它们的内容相等,则返回true(即,认为这两个对象相等)。
+
+
+**举个例子:**
+
+```java
+public class test1 {
+ public static void main(String[] args) {
+ String a = new String("ab"); // a 为一个引用
+ String b = new String("ab"); // b为另一个引用,对象的内容一样
+ String aa = "ab"; // 放在常量池中
+ String bb = "ab"; // 从常量池中查找
+ if (aa == bb) // true
+ System.out.println("aa==bb");
+ if (a == b) // false,非同一对象
+ System.out.println("a==b");
+ if (a.equals(b)) // true
+ System.out.println("aEQb");
+ if (42 == 42.0) { // true
+ System.out.println("true");
+ }
+ }
+}
+```
+
+**说明:**
+
+- String中的equals方法是被重写过的,因为object的equals方法是比较的对象的内存地址,而String的equals方法比较的是对象的值。
+- 当创建String类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个String对象。
+
+> 在[【备战春招/秋招系列5】美团面经总结进阶篇 (附详解答案)](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484625&idx=1&sn=9c4fa1f7d4291a5fbd7daa44bac2b012&chksm=fd9852b0caefdba6edcf9a827aa4a17ddc97bf6ad2e5ee6f7e1aa1b443b54444d05d2b76732b&token=723699735&lang=zh_CN#rd) 这篇文章中,我们已经提到了一下关于 HashMap 在面试中常见的问题:HashMap 的底层实现、简单讲一下自己对于红黑树的理解、红黑树这么优秀,为何不直接使用红黑树得了、HashMap 和 Hashtable 的区别/HashSet 和 HashMap 区别。HashMap 和 ConcurrentHashMap 这俩兄弟在一般只要面试中问到集合相关的问题就一定会被问到,所以各位务必引起重视!
+
+## 2 ConcurrentHashMap 相关问题
+
+### 2.1 ConcurrentHashMap 和 Hashtable 的区别
+
+ConcurrentHashMap 和 Hashtable 的区别主要体现在实现线程安全的方式上不同。
+
+- **底层数据结构:** JDK1.7的 ConcurrentHashMap 底层采用 **分段的数组+链表** 实现,JDK1.8 采用的数据结构跟HashMap1.8的结构一样,数组+链表/红黑二叉树。Hashtable 和 JDK1.8 之前的 HashMap 的底层数据结构类似都是采用 **数组+链表** 的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的;
+- **实现线程安全的方式(重要):** ① **在JDK1.7的时候,ConcurrentHashMap(分段锁)** 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。(默认分配16个Segment,比Hashtable效率提高16倍。) **到了 JDK1.8 的时候已经摒弃了Segment的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。(JDK1.6以后 对 synchronized锁做了很多优化)** 整个看起来就像是优化过且线程安全的 HashMap,虽然在JDK1.8中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本;② **Hashtable(同一把锁)** :使用 synchronized 来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。
+
+**两者的对比图:**
+
+图片来源:http://www.cnblogs.com/chengxiao/p/6842045.html
+
+HashTable:
+
+
+JDK1.7的ConcurrentHashMap:
+
+JDK1.8的ConcurrentHashMap(TreeBin: 红黑二叉树节点
+Node: 链表节点):
+
+
+### 2.2 ConcurrentHashMap线程安全的具体实现方式/底层具体实现
+
+#### JDK1.7(上面有示意图)
+
+首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。
+
+**ConcurrentHashMap 是由 Segment 数组结构和 HashEntry 数组结构组成**。
+
+Segment 实现了 ReentrantLock,所以 Segment 是一种可重入锁,扮演锁的角色。HashEntry 用于存储键值对数据。
+
+```java
+static class Segment extends ReentrantLock implements Serializable {
+}
+```
+
+一个 ConcurrentHashMap 里包含一个 Segment 数组。Segment 的结构和HashMap类似,是一种数组和链表结构,一个 Segment 包含一个 HashEntry 数组,每个 HashEntry 是一个链表结构的元素,每个 Segment 守护着一个HashEntry数组里的元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment的锁。
+
+#### JDK1.8(上面有示意图)
+
+ConcurrentHashMap取消了Segment分段锁,采用CAS和synchronized来保证并发安全。数据结构跟HashMap1.8的结构类似,数组+链表/红黑二叉树。
+
+synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升N倍。
+
+## 3 谈谈 synchronized 和 ReenTrantLock 的区别
+
+**① 两者都是可重入锁**
+
+两者都是可重入锁。“可重入锁”概念是:自己可以再次获取自己的内部锁。比如一个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死锁。同一个线程每次获取锁,锁的计数器都自增1,所以要等到锁的计数器下降为0时才能释放锁。
+
+**② synchronized 依赖于 JVM 而 ReenTrantLock 依赖于 API**
+
+synchronized 是依赖于 JVM 实现的,前面我们也讲到了 虚拟机团队在 JDK1.6 为 synchronized 关键字进行了很多优化,但是这些优化都是在虚拟机层面实现的,并没有直接暴露给我们。ReenTrantLock 是 JDK 层面实现的(也就是 API 层面,需要 lock() 和 unlock 方法配合 try/finally 语句块来完成),所以我们可以通过查看它的源代码,来看它是如何实现的。
+
+**③ ReenTrantLock 比 synchronized 增加了一些高级功能**
+
+相比synchronized,ReenTrantLock增加了一些高级功能。主要来说主要有三点:**①等待可中断;②可实现公平锁;③可实现选择性通知(锁可以绑定多个条件)**
+
+- **ReenTrantLock提供了一种能够中断等待锁的线程的机制**,通过lock.lockInterruptibly()来实现这个机制。也就是说正在等待的线程可以选择放弃等待,改为处理其他事情。
+- **ReenTrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。** ReenTrantLock默认情况是非公平的,可以通过 ReenTrantLock类的`ReentrantLock(boolean fair)`构造方法来制定是否是公平的。
+- synchronized关键字与wait()和notify/notifyAll()方法相结合可以实现等待/通知机制,ReentrantLock类当然也可以实现,但是需要借助于Condition接口与newCondition() 方法。Condition是JDK1.5之后才有的,它具有很好的灵活性,比如可以实现多路通知功能也就是在一个Lock对象中可以创建多个Condition实例(即对象监视器),**线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知,在调度线程上更加灵活。 在使用notify/notifyAll()方法进行通知时,被通知的线程是由 JVM 选择的,用ReentrantLock类结合Condition实例可以实现“选择性通知”** ,这个功能非常重要,而且是Condition接口默认提供的。而synchronized关键字就相当于整个Lock对象中只有一个Condition实例,所有的线程都注册在它一个身上。如果执行notifyAll()方法的话就会通知所有处于等待状态的线程这样会造成很大的效率问题,而Condition实例的signalAll()方法 只会唤醒注册在该Condition实例中的所有等待线程。
+
+如果你想使用上述功能,那么选择ReenTrantLock是一个不错的选择。
+
+**④ 两者的性能已经相差无几**
+
+在JDK1.6之前,synchronized 的性能是比 ReenTrantLock 差很多。具体表示为:synchronized 关键字吞吐量岁线程数的增加,下降得非常严重。而ReenTrantLock 基本保持一个比较稳定的水平。我觉得这也侧面反映了, synchronized 关键字还有非常大的优化余地。后续的技术发展也证明了这一点,我们上面也讲了在 JDK1.6 之后 JVM 团队对 synchronized 关键字做了很多优化。JDK1.6 之后,synchronized 和 ReenTrantLock 的性能基本是持平了。所以网上那些说因为性能才选择 ReenTrantLock 的文章都是错的!JDK1.6之后,性能已经不是选择synchronized和ReenTrantLock的影响因素了!而且虚拟机在未来的性能改进中会更偏向于原生的synchronized,所以还是提倡在synchronized能满足你的需求的情况下,优先考虑使用synchronized关键字来进行同步!优化后的synchronized和ReenTrantLock一样,在很多地方都是用到了CAS操作。
+
+
+## 4 线程池了解吗?
+
+
+### 4.1 为什么要用线程池?
+
+线程池提供了一种限制和管理资源(包括执行一个任务)。 每个线程池还维护一些基本统计信息,例如已完成任务的数量。
+
+这里借用《Java并发编程的艺术》提到的来说一下使用线程池的好处:
+
+- **降低资源消耗。** 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
+- **提高响应速度。** 当任务到达时,任务可以不需要的等到线程创建就能立即执行。
+- **提高线程的可管理性。** 线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
+
+### 4.2 Java 提供了哪几种线程池?他们各自的使用场景是什么?
+
+#### Java 主要提供了下面4种线程池
+
+- **FixedThreadPool:** 该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。
+- **SingleThreadExecutor:** 方法返回一个只有一个线程的线程池。若多余一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。
+- **CachedThreadPool:** 该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。
+- **ScheduledThreadPoolExecutor:** 主要用来在给定的延迟后运行任务,或者定期执行任务。ScheduledThreadPoolExecutor又分为:ScheduledThreadPoolExecutor(包含多个线程)和SingleThreadScheduledExecutor (只包含一个线程)两种。
+
+#### 各种线程池的适用场景介绍
+
+- **FixedThreadPool:** 适用于为了满足资源管理需求,而需要限制当前线程数量的应用场景。它适用于负载比较重的服务器;
+- **SingleThreadExecutor:** 适用于需要保证顺序地执行各个任务并且在任意时间点,不会有多个线程是活动的应用场景。
+- **CachedThreadPool:** 适用于执行很多的短期异步任务的小程序,或者是负载较轻的服务器;
+- **ScheduledThreadPoolExecutor:** 适用于需要多个后台执行周期任务,同时为了满足资源管理需求而需要限制后台线程的数量的应用场景,
+- **SingleThreadScheduledExecutor:** 适用于需要单个后台线程执行周期任务,同时保证顺序地执行各个任务的应用场景。
+
+### 4.3 创建的线程池的方式
+
+**(1) 使用 Executors 创建**
+
+我们上面刚刚提到了 Java 提供的几种线程池,通过 Executors 工具类我们可以很轻松的创建我们上面说的几种线程池。但是实际上我们一般都不是直接使用Java提供好的线程池,另外在《阿里巴巴Java开发手册》中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 构造函数 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
+
+```java
+Executors 返回线程池对象的弊端如下:
+
+FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致OOM。
+CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。
+
+```
+**(2) ThreadPoolExecutor的构造函数创建**
+
+
+我们可以自己直接调用 ThreadPoolExecutor 的构造函数来自己创建线程池。在创建的同时,给 BlockQueue 指定容量就可以了。示例如下:
+
+```java
+private static ExecutorService executor = new ThreadPoolExecutor(13, 13,
+ 60L, TimeUnit.SECONDS,
+ new ArrayBlockingQueue(13));
+```
+
+这种情况下,一旦提交的线程数超过当前可用线程数时,就会抛出java.util.concurrent.RejectedExecutionException,这是因为当前线程池使用的队列是有边界队列,队列已经满了便无法继续处理新的请求。但是异常(Exception)总比发生错误(Error)要好。
+
+**(3) 使用开源类库**
+
+Hollis 大佬之前在他的文章中也提到了:“除了自己定义ThreadPoolExecutor外。还有其他方法。这个时候第一时间就应该想到开源类库,如apache和guava等。”他推荐使用guava提供的ThreadFactoryBuilder来创建线程池。下面是参考他的代码示例:
+
+```java
+public class ExecutorsDemo {
+
+ private static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
+ .setNameFormat("demo-pool-%d").build();
+
+ private static ExecutorService pool = new ThreadPoolExecutor(5, 200,
+ 0L, TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
+
+ public static void main(String[] args) {
+
+ for (int i = 0; i < Integer.MAX_VALUE; i++) {
+ pool.execute(new SubThread());
+ }
+ }
+}
+```
+
+通过上述方式创建线程时,不仅可以避免OOM的问题,还可以自定义线程名称,更加方便的出错的时候溯源。
+
+## 5 Nginx
+
+### 5.1 简单介绍一下Nginx
+
+Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器。 Nginx 主要提供反向代理、负载均衡、动静分离(静态资源服务)等服务。下面我简单地介绍一下这些名词。
+
+#### 反向代理
+
+谈到反向代理,就不得不提一下正向代理。无论是正向代理,还是反向代理,说到底,就是代理模式的衍生版本罢了
+
+- **正向代理:**某些情况下,代理我们用户去访问服务器,需要用户手动的设置代理服务器的ip和端口号。正向代理比较常见的一个例子就是 VPN了。
+- **反向代理:** 是用来代理服务器的,代理我们要访问的目标服务器。代理服务器接受请求,然后将请求转发给内部网络的服务器,并将从服务器上得到的结果返回给客户端,此时代理服务器对外就表现为一个服务器。
+
+通过下面两幅图,大家应该更好理解(图源:http://blog.720ui.com/2016/nginx_action_05_proxy/):
+
+
+
+
+
+所以,简单的理解,就是正向代理是为客户端做代理,代替客户端去访问服务器,而反向代理是为服务器做代理,代替服务器接受客户端请求。
+
+#### 负载均衡
+
+在高并发情况下需要使用,其原理就是将并发请求分摊到多个服务器执行,减轻每台服务器的压力,多台服务器(集群)共同完成工作任务,从而提高了数据的吞吐量。
+
+Nginx支持的weight轮询(默认)、ip_hash、fair、url_hash这四种负载均衡调度算法,感兴趣的可以自行查阅。
+
+负载均衡相比于反向代理更侧重的时将请求分担到多台服务器上去,所以谈论负载均衡只有在提供某服务的服务器大于两台时才有意义。
+
+#### 动静分离
+
+动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作,这就是网站静态化处理的核心思路。
+
+### 5.2 为什么要用 Nginx?
+
+> 这部分内容参考极客时间—[Nginx核心知识100讲的内容](https://time.geekbang.org/course/intro/138?code=AycjiiQk6uQRxnVJzBupFkrGkvZlmYELPRsZbWzaAHE=)。
+
+如果面试官问你这个问题,就一定想看你知道 Nginx 服务器的一些优点吗。
+
+Nginx 有以下5个优点:
+
+1. 高并发、高性能(这是其他web服务器不具有的)
+2. 可扩展性好(模块化设计,第三方插件生态圈丰富)
+3. 高可靠性(可以在服务器行持续不间断的运行数年)
+4. 热部署(这个功能对于 Nginx 来说特别重要,热部署指可以在不停止 Nginx服务的情况下升级 Nginx)
+5. BSD许可证(意味着我们可以将源代码下载下来进行修改然后使用自己的版本)
+
+### 5.3 Nginx 的四个主要组成部分了解吗?
+
+> 这部分内容参考极客时间—[Nginx核心知识100讲的内容](https://time.geekbang.org/course/intro/138?code=AycjiiQk6uQRxnVJzBupFkrGkvZlmYELPRsZbWzaAHE=)。
+
+- Nginx 二进制可执行文件:由各模块源码编译出一个文件
+- Nginx.conf 配置文件:控制Nginx 行为
+- acess.log 访问日志: 记录每一条HTTP请求信息
+- error.log 错误日志:定位问题
diff --git "a/\351\235\242\350\257\225\345\277\205\345\244\207/\346\211\213\346\212\212\346\211\213\346\225\231\344\275\240\347\224\250Markdown\345\206\231\344\270\200\344\273\275\351\253\230\350\264\250\351\207\217\347\232\204\347\256\200\345\216\206.md" "b/EssentialContentForInterview/\346\211\213\346\212\212\346\211\213\346\225\231\344\275\240\347\224\250Markdown\345\206\231\344\270\200\344\273\275\351\253\230\350\264\250\351\207\217\347\232\204\347\256\200\345\216\206.md"
similarity index 100%
rename from "\351\235\242\350\257\225\345\277\205\345\244\207/\346\211\213\346\212\212\346\211\213\346\225\231\344\275\240\347\224\250Markdown\345\206\231\344\270\200\344\273\275\351\253\230\350\264\250\351\207\217\347\232\204\347\256\200\345\216\206.md"
rename to "EssentialContentForInterview/\346\211\213\346\212\212\346\211\213\346\225\231\344\275\240\347\224\250Markdown\345\206\231\344\270\200\344\273\275\351\253\230\350\264\250\351\207\217\347\232\204\347\256\200\345\216\206.md"
diff --git "a/\351\235\242\350\257\225\345\277\205\345\244\207/\347\256\200\345\216\206\346\250\241\346\235\277.md" "b/EssentialContentForInterview/\347\256\200\345\216\206\346\250\241\346\235\277.md"
similarity index 100%
rename from "\351\235\242\350\257\225\345\277\205\345\244\207/\347\256\200\345\216\206\346\250\241\346\235\277.md"
rename to "EssentialContentForInterview/\347\256\200\345\216\206\346\250\241\346\235\277.md"
diff --git "a/\351\235\242\350\257\225\345\277\205\345\244\207/\351\235\242\350\257\225\345\277\205\345\244\207\344\271\213\344\271\220\350\247\202\351\224\201\344\270\216\346\202\262\350\247\202\351\224\201.md" "b/EssentialContentForInterview/\351\235\242\350\257\225\345\277\205\345\244\207\344\271\213\344\271\220\350\247\202\351\224\201\344\270\216\346\202\262\350\247\202\351\224\201.md"
similarity index 100%
rename from "\351\235\242\350\257\225\345\277\205\345\244\207/\351\235\242\350\257\225\345\277\205\345\244\207\344\271\213\344\271\220\350\247\202\351\224\201\344\270\216\346\202\262\350\247\202\351\224\201.md"
rename to "EssentialContentForInterview/\351\235\242\350\257\225\345\277\205\345\244\207\344\271\213\344\271\220\350\247\202\351\224\201\344\270\216\346\202\262\350\247\202\351\224\201.md"
diff --git a/Java/ArrayList-Grow.md b/Java/ArrayList-Grow.md
new file mode 100644
index 00000000000..6dd4cc93daf
--- /dev/null
+++ b/Java/ArrayList-Grow.md
@@ -0,0 +1,347 @@
+
+## 一 先从 ArrayList 的构造函数说起
+
+**ArrayList有三种方式来初始化,构造方法源码如下:**
+
+```java
+ /**
+ * 默认初始容量大小
+ */
+ private static final int DEFAULT_CAPACITY = 10;
+
+
+ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
+
+ /**
+ *默认构造函数,使用初始容量10构造一个空列表(无参数构造)
+ */
+ public ArrayList() {
+ this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
+ }
+
+ /**
+ * 带初始容量参数的构造函数。(用户自己指定容量)
+ */
+ public ArrayList(int initialCapacity) {
+ if (initialCapacity > 0) {//初始容量大于0
+ //创建initialCapacity大小的数组
+ this.elementData = new Object[initialCapacity];
+ } else if (initialCapacity == 0) {//初始容量等于0
+ //创建空数组
+ this.elementData = EMPTY_ELEMENTDATA;
+ } else {//初始容量小于0,抛出异常
+ throw new IllegalArgumentException("Illegal Capacity: "+
+ initialCapacity);
+ }
+ }
+
+
+ /**
+ *构造包含指定collection元素的列表,这些元素利用该集合的迭代器按顺序返回
+ *如果指定的集合为null,throws NullPointerException。
+ */
+ public ArrayList(Collection extends E> c) {
+ elementData = c.toArray();
+ if ((size = elementData.length) != 0) {
+ // c.toArray might (incorrectly) not return Object[] (see 6260652)
+ if (elementData.getClass() != Object[].class)
+ elementData = Arrays.copyOf(elementData, size, Object[].class);
+ } else {
+ // replace with empty array.
+ this.elementData = EMPTY_ELEMENTDATA;
+ }
+ }
+
+```
+
+细心的同学一定会发现 :**以无参数构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为10。** 下面在我们分析 ArrayList 扩容时会讲到这一点内容!
+
+## 二 一步一步分析 ArrayList 扩容机制
+
+这里以无参构造函数创建的 ArrayList 为例分析
+
+### 1. 先来看 `add` 方法
+
+```java
+ /**
+ * 将指定的元素追加到此列表的末尾。
+ */
+ public boolean add(E e) {
+ //添加元素之前,先调用ensureCapacityInternal方法
+ ensureCapacityInternal(size + 1); // Increments modCount!!
+ //这里看到ArrayList添加元素的实质就相当于为数组赋值
+ elementData[size++] = e;
+ return true;
+ }
+```
+### 2. 再来看看 `ensureCapacityInternal()` 方法
+
+可以看到 `add` 方法 首先调用了`ensureCapacityInternal(size + 1)`
+
+```java
+ //得到最小扩容量
+ private void ensureCapacityInternal(int minCapacity) {
+ if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
+ // 获取默认的容量和传入参数的较大值
+ minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
+ }
+
+ ensureExplicitCapacity(minCapacity);
+ }
+```
+**当 要 add 进第1个元素时,minCapacity为1,在Math.max()方法比较后,minCapacity 为10。**
+
+### 3. `ensureExplicitCapacity()` 方法
+
+如果调用 `ensureCapacityInternal()` 方法就一定会进过(执行)这个方法,下面我们来研究一下这个方法的源码!
+
+```java
+ //判断是否需要扩容
+ private void ensureExplicitCapacity(int minCapacity) {
+ modCount++;
+
+ // overflow-conscious code
+ if (minCapacity - elementData.length > 0)
+ //调用grow方法进行扩容,调用此方法代表已经开始扩容了
+ grow(minCapacity);
+ }
+
+```
+
+我们来仔细分析一下:
+
+- 当我们要 add 进第1个元素到 ArrayList 时,elementData.length 为0 (因为还是一个空的 list),因为执行了 `ensureCapacityInternal()` 方法 ,所以 minCapacity 此时为10。此时,`minCapacity - elementData.length > 0 `成立,所以会进入 `grow(minCapacity)` 方法。
+- 当add第2个元素时,minCapacity 为2,此时e lementData.length(容量)在添加第一个元素后扩容成 10 了。此时,`minCapacity - elementData.length > 0 ` 不成立,所以不会进入 (执行)`grow(minCapacity)` 方法。
+- 添加第3、4···到第10个元素时,依然不会执行grow方法,数组容量都为10。
+
+直到添加第11个元素,minCapacity(为11)比elementData.length(为10)要大。进入grow方法进行扩容。
+
+### 4. `grow()` 方法
+
+```java
+ /**
+ * 要分配的最大数组大小
+ */
+ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
+
+ /**
+ * ArrayList扩容的核心方法。
+ */
+ private void grow(int minCapacity) {
+ // oldCapacity为旧容量,newCapacity为新容量
+ int oldCapacity = elementData.length;
+ //将oldCapacity 右移一位,其效果相当于oldCapacity /2,
+ //我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍,
+ int newCapacity = oldCapacity + (oldCapacity >> 1);
+ //然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量,
+ if (newCapacity - minCapacity < 0)
+ newCapacity = minCapacity;
+ // 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 MAX_ARRAY_SIZE,
+ //如果minCapacity大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`。
+ if (newCapacity - MAX_ARRAY_SIZE > 0)
+ newCapacity = hugeCapacity(minCapacity);
+ // minCapacity is usually close to size, so this is a win:
+ elementData = Arrays.copyOf(elementData, newCapacity);
+ }
+```
+
+**int newCapacity = oldCapacity + (oldCapacity >> 1),所以 ArrayList 每次扩容之后容量都会变为原来的 1.5 倍!(JDK1.6版本以后)** JDk1.6版本时,扩容之后容量为 1.5 倍+1!详情请参考源码
+
+> ">>"(移位运算符):>>1 右移一位相当于除2,右移n位相当于除以 2 的 n 次方。这里 oldCapacity 明显右移了1位所以相当于oldCapacity /2。对于大数据的2进制运算,位移运算符比那些普通运算符的运算要快很多,因为程序仅仅移动一下而已,不去计算,这样提高了效率,节省了资源
+
+**我们再来通过例子探究一下`grow()` 方法 :**
+
+- 当add第1个元素时,oldCapacity 为0,经比较后第一个if判断成立,newCapacity = minCapacity(为10)。但是第二个if判断不会成立,即newCapacity 不比 MAX_ARRAY_SIZE大,则不会进入 `hugeCapacity` 方法。数组容量为10,add方法中 return true,size增为1。
+- 当add第11个元素进入grow方法时,newCapacity为15,比minCapacity(为11)大,第一个if判断不成立。新容量没有大于数组最大size,不会进入hugeCapacity方法。数组容量扩为15,add方法中return true,size增为11。
+- 以此类推······
+
+**这里补充一点比较重要,但是容易被忽视掉的知识点:**
+
+- java 中的 `length `属性是针对数组说的,比如说你声明了一个数组,想知道这个数组的长度则用到了 length 这个属性.
+- java 中的 `length()` 方法是针对字符串说的,如果想看这个字符串的长度则用到 `length()` 这个方法.
+- java 中的 `size()` 方法是针对泛型集合说的,如果想看这个泛型有多少个元素,就调用此方法来查看!
+
+### 5. `hugeCapacity()` 方法。
+
+从上面 `grow()` 方法源码我们知道: 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 MAX_ARRAY_SIZE,如果minCapacity大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`。
+
+
+```java
+ private static int hugeCapacity(int minCapacity) {
+ if (minCapacity < 0) // overflow
+ throw new OutOfMemoryError();
+ //对minCapacity和MAX_ARRAY_SIZE进行比较
+ //若minCapacity大,将Integer.MAX_VALUE作为新数组的大小
+ //若MAX_ARRAY_SIZE大,将MAX_ARRAY_SIZE作为新数组的大小
+ //MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
+ return (minCapacity > MAX_ARRAY_SIZE) ?
+ Integer.MAX_VALUE :
+ MAX_ARRAY_SIZE;
+ }
+```
+
+
+
+## 三 `System.arraycopy()` 和 `Arrays.copyOf()`方法
+
+
+阅读源码的话,我们就会发现 ArrayList 中大量调用了这两个方法。比如:我们上面讲的扩容操作以及`add(int index, E element)`、`toArray()` 等方法中都用到了该方法!
+
+
+### 3.1 `System.arraycopy()` 方法
+
+```java
+ /**
+ * 在此列表中的指定位置插入指定的元素。
+ *先调用 rangeCheckForAdd 对index进行界限检查;然后调用 ensureCapacityInternal 方法保证capacity足够大;
+ *再将从index开始之后的所有成员后移一个位置;将element插入index位置;最后size加1。
+ */
+ public void add(int index, E element) {
+ rangeCheckForAdd(index);
+
+ ensureCapacityInternal(size + 1); // Increments modCount!!
+ //arraycopy()方法实现数组自己复制自己
+ //elementData:源数组;index:源数组中的起始位置;elementData:目标数组;index + 1:目标数组中的起始位置; size - index:要复制的数组元素的数量;
+ System.arraycopy(elementData, index, elementData, index + 1, size - index);
+ elementData[index] = element;
+ size++;
+ }
+```
+
+我们写一个简单的方法测试以下:
+
+```java
+public class ArraycopyTest {
+
+ public static void main(String[] args) {
+ // TODO Auto-generated method stub
+ int[] a = new int[10];
+ a[0] = 0;
+ a[1] = 1;
+ a[2] = 2;
+ a[3] = 3;
+ System.arraycopy(a, 2, a, 3, 3);
+ a[2]=99;
+ for (int i = 0; i < a.length; i++) {
+ System.out.println(a[i]);
+ }
+ }
+
+}
+```
+
+结果:
+
+```
+0 1 99 2 3 0 0 0 0 0
+```
+
+### 3.2 `Arrays.copyOf()`方法
+
+```java
+ /**
+ 以正确的顺序返回一个包含此列表中所有元素的数组(从第一个到最后一个元素); 返回的数组的运行时类型是指定数组的运行时类型。
+ */
+ public Object[] toArray() {
+ //elementData:要复制的数组;size:要复制的长度
+ return Arrays.copyOf(elementData, size);
+ }
+```
+
+个人觉得使用 `Arrays.copyOf()`方法主要是为了给原有数组扩容,测试代码如下:
+
+```java
+public class ArrayscopyOfTest {
+
+ public static void main(String[] args) {
+ int[] a = new int[3];
+ a[0] = 0;
+ a[1] = 1;
+ a[2] = 2;
+ int[] b = Arrays.copyOf(a, 10);
+ System.out.println("b.length"+b.length);
+ }
+}
+```
+
+结果:
+
+```
+10
+```
+
+
+### 3.3 两者联系和区别
+
+**联系:**
+
+看两者源代码可以发现 copyOf() 内部实际调用了 `System.arraycopy()` 方法
+
+**区别:**
+
+`arraycopy()` 需要目标数组,将原数组拷贝到你自己定义的数组里或者原数组,而且可以选择拷贝的起点和长度以及放入新数组中的位置 `copyOf()` 是系统自动在内部新建一个数组,并返回该数组。
+
+
+
+## 四 `ensureCapacity`方法
+
+ArrayList 源码中有一个 `ensureCapacity` 方法不知道大家注意到没有,这个方法 ArrayList 内部没有被调用过,所以很显然是提供给用户调用的,那么这个方法有什么作用呢?
+
+```java
+ /**
+ 如有必要,增加此 ArrayList 实例的容量,以确保它至少可以容纳由minimum capacity参数指定的元素数。
+ *
+ * @param minCapacity 所需的最小容量
+ */
+ public void ensureCapacity(int minCapacity) {
+ int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
+ // any size if not default element table
+ ? 0
+ // larger than default for default empty table. It's already
+ // supposed to be at default size.
+ : DEFAULT_CAPACITY;
+
+ if (minCapacity > minExpand) {
+ ensureExplicitCapacity(minCapacity);
+ }
+ }
+
+```
+
+**最好在 add 大量元素之前用 `ensureCapacity` 方法,以减少增量重新分配的次数**
+
+我们通过下面的代码实际测试以下这个方法的效果:
+
+```java
+public class EnsureCapacityTest {
+ public static void main(String[] args) {
+ ArrayList