From a9b8b29a42116454bae3fd4d3f1e90714e949de6 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Fri, 18 Jan 2019 19:30:44 +0800 Subject: [PATCH 001/100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 80421657..aee71123 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@
-[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶已上线](https://xiaozhuanlan.com/CyC2018)
From 4107e09fecc4e6b1029018bb2a2b6ed85aeb9038 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Fri, 18 Jan 2019 23:12:16 +0800 Subject: [PATCH 002/100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aee71123..80421657 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@
-[🎉 面试进阶已上线](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018)
From 8a593862f087f9978537aa5b9d4d02005f3a939b Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sat, 19 Jan 2019 13:12:08 +0800 Subject: [PATCH 003/100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 80421657..beeea25d 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@
-[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶专栏已上线](https://xiaozhuanlan.com/CyC2018)
From 0c1ba14cb22f80d267e1ac332ad78e1d84d2e783 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sat, 19 Jan 2019 13:12:24 +0800 Subject: [PATCH 004/100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index beeea25d..54032bbe 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@
-[🎉 面试进阶专栏已上线](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018)
From a3cdc40ad34f4213019a1370d6de9bb7d87a79f3 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sat, 19 Jan 2019 13:12:40 +0800 Subject: [PATCH 005/100] Update README.md --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index c8e161bd..c4e098ab 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,5 @@ -[🎉 面试进阶专栏已上线](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) ## ✏️ 算法 From 9216e4b87dc8a34a4a3bb0fa32ac487e00e31f7d Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sat, 19 Jan 2019 13:16:37 +0800 Subject: [PATCH 006/100] Update style.css --- docs/_style/style.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/_style/style.css b/docs/_style/style.css index cb9403b2..92cb8907 100644 --- a/docs/_style/style.css +++ b/docs/_style/style.css @@ -3,6 +3,10 @@ display: none; } +#main > ul:nth-child(2) { + display: none; +} + .markdown-section h1 { margin: 3rem 0 2rem 0; } @@ -36,4 +40,4 @@ body { /*.anchor span { color: rgb(66, 185, 131); -}*/ \ No newline at end of file +}*/ From bd8d4b904aab571cd168388df36818a68e965227 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sat, 19 Jan 2019 13:20:23 +0800 Subject: [PATCH 007/100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 54032bbe..6bbe7464 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ [🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018)
- +
From 6a374a9ac125d696ad26dbdb8eb715401334d5ab Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sat, 19 Jan 2019 13:38:04 +0800 Subject: [PATCH 008/100] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6bbe7464..3bc0ecfd 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,11 @@ |    Java   
[:coffee:](#coffee-java) |    系统设计   
[:bulb:](#bulb-系统设计) |     工具    
[:hammer:](#hammer-工具) |    编码实践   
[:speak_no_evil:](#speak_no_evil-编码实践) |    后记    
[:memo:](#memo-后记) | --> -
[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) +
+

From 0b50ae2bdec60cc129ff5c2e330428153859299b Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sat, 19 Jan 2019 13:45:47 +0800 Subject: [PATCH 009/100] =?UTF-8?q?Delete=202018=20=E8=BF=99=E4=B8=80?= =?UTF-8?q?=E5=B9=B4.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- other/2018 这一年.md | 139 ------------------------------------------- 1 file changed, 139 deletions(-) delete mode 100644 other/2018 这一年.md diff --git a/other/2018 这一年.md b/other/2018 这一年.md deleted file mode 100644 index dbaa2310..00000000 --- a/other/2018 这一年.md +++ /dev/null @@ -1,139 +0,0 @@ -作者:CyC2018 - -链接:https://www.nowcoder.com/discuss/137593 - -来源:牛客网 - -## 前言 - -2018,有过迷茫,有过努力,也有很多收获。为了记录这一年以来的感受,于是有了这篇文章。 - -## Offer 情况 - -经过了长达一年左右的复习,秋招也收到了几个比较满意的 Offer,参加面试的都通过了。 - -- 百度,企业智能平台; -- 阿里,高德地图,部门已联系,目前还在申报 Offer 中; -- 腾讯,IEG 游戏平台,后台研发,SP; -- 字节跳动,头条后台研发,SSP; -- 华为,Cloud Bu; -- 网易游戏,梦幻事业部; -- 顺丰科技。 - -## 前期准备 - -也是在去年十一月份左右,看着身边两年制的同学经历了长时间而又艰难的秋招,我开始意识到自己应该提前准备了,否则自己的秋招会很惨。 - -本科的时候,虽然学过计算机网络、操作系统和数据结构等课程,而且 Leetcode 也刷了一两百题,但是离招聘要求还差的很远,学的都很浅只够应付考试,也没有实际的项目经验。 - -我的研究生方向是计算机图形学,研究生期间主要做一些科研项目。在选择招聘方向的时候,我也纠结了是不是找图形学相关方向的,但是考虑到图形学的选择不是很多,所以还是决定投后台研发相关的岗位。 - -于是开始收集各种学习资料,也买了很多纸质书。最开始的学习效率并不是很高,很迷茫,觉得要学的内容很多无从下手。那时候看别人的面经,感觉自己太弱了,很多内容都没接触过,于是更加迷茫。迷茫的时候总想着逃避,要是不复习多好,玩玩游戏每天多简单。但是游戏玩的越多,那种焦虑感越是强烈。解决焦虑的唯一办法就是想办法解决当前问题。当慢慢地从消极的学习态度中调整过来,掌握的知识越多,那种焦虑感也随之消失。当然这个过程并不容易,不仅需要很好的毅力,也要根据自身情况找到问题的有效解决方法。 - -## 春招开始 - -三月份各个公司就开始春招了,那时候刚把一些基础知识简单地复习了一下,Leetcode 刷到了三四百题。但是没有后台研发相关的项目,于是花了一个星期左右用 PHP 做了一个微博系统。当时做简历特别痛苦,没内容可以写,看着其他人简历各种新技术,自己都没掌握,所以很虚。 - -## 阿里一轮游 - -最开始投的阿里,实验室大几届有个师兄在天猫精灵团队,所以给我内推了。于是我人生中第一场面试就是阿里,很自然地被虐了一遍。记得当时约好下午两点电话面试,午饭都没吃,怕吃完之后犯困影响状态,然后找了一个很安静又没人的地方呆到了两点,调整自己的状态。可是面试官突然打电话来说有个会议要开,所以推迟了大概一个小时。苦苦等到三点左右,面试正式开始,不出所料面得非常糟糕。首先自己表述的很有问题,很多内容没回答到关键点上,自己会的内容也不怎么继续扩展回答。其次知识掌握得确实不够,连线程安全、ThreadLocal、函数式编程都不会。虽然被虐的很惨,但是也有好处,知道了面试到底是怎样的,自己还有哪方面的不足,该怎么准备。 - -## 腾讯被鞭尸 - -第二场面试是腾讯,在经历了阿里的面试之后,并且又继续复习了一段时间,我对面试就比较有信心了。一面其实回答的挺理想的,虽然很多问题没有立马回答出来,但是经过面试官的耐心提示之后都能回答一些内容。当时面了一个半小时,面试体验特别好。印象比较深刻的题目有,阅读一个 Redis 源码,分析存在哪些问题。其实就是一个计数器实现的限流算法,会有临界值的问题,但是当时没回答出来,只能听面试官给我解释。还有一个微信扫二维码,这个过程发生了什么,也没回答得很好,不过面试官也很耐心地纠正我回答上的错误。一面顺利通过了,但是总监面挂了。总监面没有问什么技术问题,就是问了问项目和职业规划。自己的项目确实比较 Low,我自己在介绍的时候也说得很不堪。职业规划我说自己希望在一些方面深入学习,因为自己现在在这些方面还很薄弱... 面完之后我就知道挂了,因为整个面试过程我都特别虚,还主动说自己技术能力不行。不出所料,面完的当天晚上,状态变成了不合适。 - -但是过了几天,突然收到腾讯的电话,问我是否愿意去深圳参加面试(笔者学校在广州)。当然我毫不犹豫地答应了,很开心腾讯还能给我机会。经过了上一场面试的启示,这次面试我表现地非常自信,自己知道的知识都很有信心地表达出来,被问到不会的内容也不会那么慌张,和面试官探讨一些细节,然后说说自己的想法,还有自己看过相关的内容。由于这是腾讯云部门,对 Linux 内核和 C++ 有很高的要求,问了几个相关的问题我都没回答出来,比如如何实现守护进程,Linux 信号机制,Linux 线程的不可中断阻塞状态如何进入等等。除了这些问题,其它地回答的都还行。遗憾的是,当天晚上面试官打电话告知我面试没通过。但是他说我其它方面都很不错,所以问我愿不愿意参加腾讯云 Java 部门的招聘,于是第二天我又去了一个新的部门面试。 - -这次面试是在部门的会议室进行的,进到公司之后说实话没有自己想象中那么好,工位很挤环境一般。一开始就先随便聊聊,学校的研究工作,学习之类的。然后看了看项目,看完之后我就知道凉了一半,这个项目确实太水了,面试官看了之后没有接着问,也能感受到面试官有点嫌弃。然后他就问了一些基础知识,问到进程调度算法,面试官让我实现一个任务调度系统。因为是第一次手写代码,而且之前确实没考虑过这个问题,然后就胡乱写了一堆代码,特别乱,而且到处涂改。显然面试官是不满意的,写了也有十几分钟之后,我自己都知道已经凉了,然后面试官没让我接着写,也没给我任何提示,说就到这里,面试结束了,还有没有什么问题想问的。当然看过任务调度系统相关的文章会觉得挺容易的,比如使用时间轮实现等等。我依然记得面试官送我出门时候的热情,送我坐电梯的时候还很热情地和我说,非常感谢参加本次面试,辛苦了。 - -## 虎牙过于自信 - -经过了阿里和腾讯的面试之后,我觉得自己大概已经知道该怎么面试了,面试时候该注意什么,该怎么表达等等。而且腾讯面试表现也不差,虽然最后没通过。所以在虎牙面试的时候特别放松,觉得应该能通过。前面面的也都还行,虽然有几个问题没回答好,比如分析一下微博的时间线。通过了第一轮面试直接等第二轮,等到了晚上七点多才等到我。虎牙面试还是很注重技术的,虽然问的都不是很深入,只要简单回答到点上就不会接着问下去。二面也有一些问题没回答好,比如 ConcurrentHashMap 的并发机制,问 Spring 直接说不会。也有一些问题回答得比较乱,没有条理。但是我觉得大部分问题都回答的不错,应该能通过。可是面试完之后,面试官问有没有什么问题要问他,由于太过放松,我就问你们都加班到这么晚不吃饭吗,好饿啊,周六周日还加班吗... 问完之后面试官就很严肃了,说平常不加班的,我突然意识到了问题的严重性... 最后还是凉了。 - -## 百度第一个 Offer - -被三家连续拒了之后,都开始怀疑自己了,不过还是提醒自己要保持信心。 - -幸运的是,百度的面试非常适合我,三轮都是技术面,而且手写算法题目居多,而我准备最多的是算法,所以很顺利通过了面试。但是面试表现并没有特别好,过了比较长的时间才被捞,而且是工程效率部门,做内部工具的,对个人成长并不好,所以不是特别满意。 - -## 网易游戏最好的面试体验 - -其实最开始没有打算投网易游戏的,因为被脉脉洗脑,已经放弃了做游戏。但是因为前面面试基本被拒了,担心没有实习 Offer,因此就试试看。 - -因为没有特别想去网易游戏,所以面试过程也比较放松,就当去聊聊天。面试官非常 nice,那天下午挤了很久地铁,比较口渴,然后面试官看我说得沙哑了,到门口帮我买了一瓶可乐,非常感激。面试之前我就提出我对 C++ 不熟悉,最近主要看 Java 的内容。面试官还是说没关系,尽量回答就好。当然最后我都把问题往 Java 那里回答了,比如 Map 的实现,内存管理等等。最后聊了一些玩过的游戏,就让我回去等消息。网易游戏就一轮面试,确实就一轮。周五参加的面试,下周一就给 Offer 了,效率特别高。 - -## 微众玄学面试 - -通过微众面试我自己都非常吃惊,一面的时候就简单自我介绍了一下,然后面试官开始介绍他自己的工作经历,以及现在部门在做的内容。之后问了我一个场景分析问题,我想了一会儿没想出来,于是面试官拿起草稿纸把各种需求详细说了一遍,然后把系统架构图也画了出来... 最后他问还有什么我优势的地方他没问到的,我问他怎么不问问算法题,他说笔试都通过了没必要再问。面完之后我觉得聊得很开心,但是技术问题没回答好,出乎意料收到了二面通知。二面没问技术,就让介绍了项目,再问问家住哪之类的问题,也顺利通过了。HR 面就不用介绍。收到了微众的 Offer,得知了部门是贷款科技部,非常核心,很吃香,近几年也在扩展一些业务,还是有点小心动的。虽然最后没选择去微众实习,但是一面面试官加了我微信,我很感谢他一面非常耐心给我讲解,并让我通过。他说我是他面试的第一顺位,也就是第一个面试者,所以会放宽很多,也希望我秋招能加入他们。 - -## 实习选择 - -其实最理想的是去百度实习,秋招也会容易很多。但是考虑到百度是在北京,部门很边缘,而且需要实习很长时间也不一定能转正,所以还是放弃了。最后只能在网易游戏和微众选,虽然自己不想做游戏,但是考虑到网易游戏的平台认可程度比微众好,秋招肯定会更容易一些。而且秋招如果还想进微众的话也会比较容易,因为面试官和 HR 都说秋招的时候会优先考虑我,所以最后还是去了网易游戏实习。 - -## 实习之前的快速学习期 - -经历了春招之后,认识到了自己身上的不足,比如交流表达能力的欠缺,知识积累得不够,项目深度不够。因此在实习之前的两三个月,开始针对这些问题逐个解决。 - -- 交流表达能力欠缺,就提前准备好各种非技术问题,然后对着镜子回答,把自己当成听众,并且也用录音机录下来。 -- 知识积累不够,采取的策略是保证广度优先,并且在重要的内容上保证深度。其实之前基础知识已经掌握的比较好了,再学其它技术的时候都有很多相同的地方,所以学起来很快。 -- 项目深度不够,就把那个微博系统做了一点改进,学了 Spring 之后改用 Java 实现。 - -## 不那么安心的实习 - -去实习的时候还是挺惊喜的,因为我被安排的工作是游戏引擎相关的,和自己的研究生方向紧密相关,我觉得做完实习项目之后自己的毕业论文也会比较有灵感。 - -但不幸的是,在去的第一天部门接待聚餐上,服务端主程就说,我们部门工作制是九九六,现在互联网都是九九六。在实习之前我了解的是实习生六点就可以走,而且只用上五天班,听到他这么一说心都凉透了,因为已经想好了晚上和周末时间用来复习。如果知道是九九六,我会选择去百度。 - -其实网易游戏部门氛围还是不错的,对员工很好,而且我的实习导师人也很好,在我生病的那几天很关心我。但是九九六的工作制对秋招复习还是有很大影响的,而且每天上下班花在路上的时间超过了两个小时,下班回寝室之后总想着看会儿视频休息一下,然后又要早早睡觉赶着第二天上班。没办法只能在上下班地铁上复习,还有就是午休时间接着复习。 - -## 秋招开始 - -实习之后已经是九月份了,那时候已经错过了所有提前批。而且实习的时候没怎么复习,九月初还是感觉没怎么准备充分,所以就又等了半个月才开始投简历。 - -但是这个时候和春招相比,已经把大部分后台研发相关的知识点过了一遍,很多重要的内容前前后后也看了十几遍,没有春招时候那么迷茫和焦虑。即使被问到没有掌握的知识,我也有把握通过讨论的方式,给出大概的思路,因为很多技术确实是相通的。 - -## 阿里看不懂的内部流程 - -秋招第一个投递的依然是阿里,最开始系统自动发起了一个新的流程,然后过了几天自动回绝了... 八月末的时候也找人内推了,但是又被阿里直接回绝了... 那时候已经觉得可能是春招面试表现太差,此生无缘阿里了。可是过了一段时间,正式校招的时候,阿里又发起了一个新的流程戏弄我,收到笔试通知的时候,我还犹豫了到底参不参加,因为那时候已经九月中旬,听说阿里已经没有 HC 了。而且按前面回绝我的态度,感觉即使笔试通过面试也通过不了。笔试那天晚上,本来准备看个电影放松一下,后来想了想还是参加了笔试,笔试各种机器学习和数学题,感觉拿错了试卷,笔试完我已经把阿里从我的公司进度列表中删除了,不再纠结阿里。可是过了一段时间收到阿里的面试通知,我以为是走走形式,可能参加笔试的人很少了,所以才选中我参加面试。那时候阿里招聘官网状态一排的已回绝,让我对阿里有一种恐惧感,觉得面试肯定挂。但是真正面试的时候却意外的顺利,收到二面通知的时候特别激动,然后面完二面又让直接等 HR 面,HR 面虽然不是很理想,但是没有很大的问题。又过了很长一段时间,在我去深圳参加腾讯招聘的高铁上,收到了高德地图 HR 的电话,问是否愿意去。虽然得知部门在北京有点小失落,但是还是很开心终于被阿里认可了,摆脱了对阿里的恐惧。 - -实验室上届毕业在阿里云的大佬某天突然和我说,他们部门有新的 HC,让我把简历发给他,他要帮我内推,会帮我安排一场线下面试,如果通过的话,到时候和高德的 HR 沟通一下,直接把我从高德捞过来。很感谢大佬向他老大极力推荐我,给我了这次面试机会。线下面试也很顺利,聊聊实习项目,问问我的开源博客,然后问些 Paxos 等分布式的问题,还有就是手写代码,信号量实现生产者消费者,以及一个位运算的问题。其实位运算的问题面试的时候写的不完善,面试官让我之后完善了再发给他,因为面试一个多小时有点长了。过后我写了详细文档讲解了思路,以及使用 JUnit 进行了详细的单元测试,把文档和代码都发给了他。现在面试已经通过了,但是最近阿里集团 HC 比较紧张,也不知道能不能批到 HC。 - -## 百度又是不那么满意的部门 - -虽然阿里是最先开始流程,但是第一个参加面试的是百度。因为实习的时候通过了百度的面试,所以这次面试还是比较有信心的。百度面试连续三天,都在同一个地方,最后签约也在同一个地方。还记得每次都坐一个小时左右的地铁去那里,路线已经非常熟悉了,和每天去实习的感觉类似。百度面试比较注重技术,三轮面试基本都是问技术问题,而且问的也比较深入,内容也非常广。但是面的不是那么理想,有两点原因,首先是因为确实有些知识点还没掌握好,比如 AC 自动机,系统故障分析等等;其次是对实习项目的描述上还不够好,没有把实习内容的闪光点描述出来,也没有讲清楚为什么做这个项目,自己通过什么方法去做,以及最后的结果。 - -最后百度给了白菜价,部门是企业智能平台,主要是内部系统,虽然会接触到机器学习和大数据。 - -## 腾讯虐我千百遍 - -秋招腾讯第一场面试和实习参加腾讯面试的感觉非常像,第一轮技术面感觉很好,手写堆排序算法,二部图分析等等。面完之后通知待会儿二面,听到之后还是很激动的,觉得这次应该没问题了。我在等二面的时候,碰到了室友(他经常不住宿舍,所以不清楚他也去面试),聊着聊着居然发现我两是同一个面试官,而且他是来二面的,也就是等一下我两就要一前一后进去面试。二面的感觉和实习二面非常像,非技术问题回答的支支吾吾,然后面试官开始质疑我说的内容,给我压力,我没有当场反驳,就说了哦,好像是这样的。因为面试官全程都绷着脸,所以我也比较紧张,很多问题没回答好。过了几天,室友和我说收到 HR 面试通知了,我去官网看了一下状态,已经变成了熟悉的不合适。这次面试失败的主要原因是自己在应对这种压力时处理地不是很好,主要体现在失去信心以及紧张。解决方法也简单,做好充分准备来保持信心,受到质疑的时候积极反驳,紧张的时候及时调整心态,可以试试深呼吸或者喝水。 - -因为实习有被捞起来的经历,所以被拒之后我特别希望能继续被捞起来,然后把简历上的面试城市改成了深圳。苦苦等到深圳场面试的前几天,在不经意的一个下午手机突然响了,我记得是短信邮件同时收到面试通知。于是又开始了新一轮被腾讯虐的面试之旅。 - -一面和之前一样也是意外地顺利,虽然问了一些 C++ 的问题,但是我都说到 Java 相关的实现上。在一些问题上确实回答的深度不够,比如网络编程里面的水平触发和边缘触发等问题。然后问了几个算法,本来要求手写,我说我实现过,所以就讲了讲思路。面试和腾讯第一场面试一样持续了一个半小时,面试官也很好,很多问题都会给提示,即使最开始回答的有问题。二面面试官也很好,问了问实习项目,然后再聊一聊一些技术,经过了之前的面试,到这次面试真的就像在聊天一样而不是面试,我们都会说一些对技术上的理解。HR 面其实面得很差,对于非技术问题的吹水能力我还是不太行。最终和我预期的一样,给了我 SP 的 Offer,因为觉得自己面得还可以,但是也不够好到给 SSP,有些 C++ 问题还是没回答的特别好。 - -## 头条意外的惊喜 - -之前看到学弟收到头条的 Offer,薪资非常诱人,所以也想去试试。也听说头条面试难度非常大,主要考察手写算法,因为自己算法方面准备得比较充分,所以觉得会比较顺利,但是也没有特别高的预期。前两面中规中矩,算法题和其它问题我都回答的比较好,到三面的时候,问了一个错排问题,其实最开始我给了正确的递推公式,但不是面试官想要的答案,所以让我再想想。我想了十几分钟还是觉得没问题,那时候觉得自己已经凉了,因为面试官一直不满意。后面的几个问题也没回答的很好,分析一个 SQL 语句的具体执行过程,比如会怎么利用索引,怎么优化之类的,虽然在他的提示下还是回答了,但是感觉并不好。面完之后我立马查了一下那个错排问题,证实了我的答案是正确的,于是写了一个详细的文档,联系 HR 让她发给面试官。出乎意料的是,HR 让我不用担心,他说面试官对我的评价很好... 不过最后还是让她把文档发给了面试官。之后收到了加面通知,头条加面有两种情况,一是三轮评级都是 4 可以评 SSP,二是面试官评价差别很大,再面一轮决定是否录用。收到加面的时候完全不知道自己属于哪一种,感觉两种情况都有可能。加面回答的也不好,主要是问项目,面了 25 分钟就草草结束,最后面试官说有些内容需要找一些文献参考参考。面完之后我觉得,即使我属于第一种要评 SSP 的情况,加面面的那么差应该也没希望了。苦苦等了好多天之后,最后确定是 SSP 之后,还是很惊喜的,感觉是对自己这么长时间复习的一个认可。 - -## 顺丰最后的保底 - -投顺丰是因为九月中旬很多公司都结束了招聘,所以那时候比较慌,就投了顺丰当做保底,顺便也练练手。最开始还担心顺丰笔试没通过,因为编程题最后一题没做出来,那题的题目都出错了,而且题目是网上直接 copy 过来的,网上的源码都不能通过,更别说我自己的实现了。顺丰面试主要问了数据库的内容,而且问的特别深,几乎把每种日志的实现和作用都问了一遍。面顺丰的时候也比较早,那时候有些问题的回答上没有组织好,回答得比较凌乱,虽然最后也算给了一个小 SP。 - -## 华为特别纠结的部门 - -去华为面试确实是没有压力的,因为都知道华为面试不怎么问技术,虽然还是问了我一些技术问题,不过不是问的很深。面试主要介绍项目,我对自己的实习项目还是比较有信心的,因为觉得做的确实不错,而且面了很多场了,知道该怎么介绍项目。面试官问我个人意愿,我说自己对分布式中间件等比较感兴趣,于是面试官把我推荐到了 Cloud Bu。本来没打算签华为的,现场签约也就去看看到底给我开多少。最开始其实给我开了十四级最高的薪资,我本来不是很想去,虽然对这个部门感兴趣,但是薪资确实比不上头条。然后随口问了一句可不可以给十五级,本来 HR 说是可以试着申请一下,不过最后没申请成功。 - -## 技术博客 - -最后安利一下自己的技术博客:[CS-Notes](https://github.com/CyC2018/CS-Notes),虽然现在还有很多不完善的地方,但以后会不断改进。 - -## 小结 - -很多人都说,面试和考试一样,要背很多没用的东西。最开始我也认同这种看法,可是参加了几场面试之后,我就不这么认为了。因为面试出的问题,有很多是实际开发中碰到的,所以准备面试相当于提前做入职准备。而且面试中考察的思维能力、交流表达能力、应对压力能力,都是真正工作中所需要的。 - -我觉得自己比别人做的好的地方是,有很强烈的想找到好工作的意愿,才驱使我不断学习,所以态度很重要。 - -信心源自于充分准备,有了信心,面试的时候才能游刃有余。而毫无依据的自我感觉良好,在每次失败之后都看不到自身的不足,而是怪罪于外界因素。 - -做好自己的简历,我在简历上花了很长时间,只要允许,我都会用这个简历给面试官演示:[个人简历](https://cyc2018.github.io)。 From 348f6335ebe2cf651871fbe09c1fcefcc7e3a043 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sun, 20 Jan 2019 09:58:10 +0800 Subject: [PATCH 010/100] =?UTF-8?q?Update=20=E7=9B=B8=E5=85=B3=E5=86=85?= =?UTF-8?q?=E5=AE=B9.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 相关内容.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/相关内容.md b/相关内容.md index f5513fcb..986f6bfe 100644 --- a/相关内容.md +++ b/相关内容.md @@ -1,8 +1,10 @@ -# 交流群 +# QQ 群 为大家提供一个学习交流平台,在这里你可以自由地和笔者以及其他人进行技术上的交流。 -[💬](other/Group.md) +
+QQ 群 + # 知识星球 From b403735305cd570ff7d3cf3ff1dc7a3c6796b427 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sun, 20 Jan 2019 09:58:39 +0800 Subject: [PATCH 011/100] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3bc0ecfd..208fa4d7 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,9 @@ 为大家提供一个学习交流平台,在这里你可以自由地和笔者以及其他人进行技术上的交流。 -[💬](other/Group.md) +
+QQ 群 + #### 知识星球 From c66fbc3d6b8111d0c2bf72ebd3ca7d6b8907737d Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sun, 20 Jan 2019 09:59:13 +0800 Subject: [PATCH 012/100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 208fa4d7..662c69db 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ 为大家提供一个学习交流平台,在这里你可以自由地和笔者以及其他人进行技术上的交流。 -
+
QQ 群 From c505677026eee51833f59b0b64aad6485efa77c0 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sun, 20 Jan 2019 09:59:33 +0800 Subject: [PATCH 013/100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 662c69db..ecf5c5c4 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ 为大家提供一个学习交流平台,在这里你可以自由地和笔者以及其他人进行技术上的交流。 -
+
QQ 群 From 79c29f849437b2d7cbdbd13d5b7171ce9822ce0d Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sun, 20 Jan 2019 10:16:06 +0800 Subject: [PATCH 014/100] Update README.md --- README.md | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/README.md b/README.md index ecf5c5c4..8a2890cd 100644 --- a/README.md +++ b/README.md @@ -92,12 +92,6 @@ 学习笔记不是从网上到处拼凑而来,除了少部分引用书上和技术文档的原文,其余都是笔者的原创。在您引用本仓库内容或者对内容进行修改演绎时,请遵循文末的开源协议,谢谢。 -#### 如何贡献 - -笔记内容是笔者一个字一个字打上去的,难免会有一些笔误,如果发现笔误可直接对相应文档进行编辑修改。 - -如果想要提交一个仓库现在还没有的全新内容,可以先将相应的文档放到 other 目录下。 - #### 交流群 为大家提供一个学习交流平台,在这里你可以自由地和笔者以及其他人进行技术上的交流。 @@ -105,7 +99,6 @@
QQ 群 - #### 知识星球 想要向笔者提问关于学习和求职方面的建议?来知识星球,你的每个提问我都会认真回答。 @@ -116,16 +109,6 @@ [🔎](https://github.com/CyC2018/Job-Recommend) -#### 排版 - -笔记内容按照 [中文文案排版指北](https://mazhuang.org/wiki/chinese-copywriting-guidelines/) 进行排版,以保证内容的可读性。笔记不使用 `![]()` 这种方式来引用图片,而是用 `` 标签。一方面是为了能够控制图片以合适的大小显示,另一方面是因为 GFM 不支持 `
![]()
` 让图片居中显示,只能使用 `
` 达到居中的效果。笔者将自己实现的文档排版功能提取出来,放在 Github Page 中,无需下载安装即可免费使用:[Text-Typesetting](https://github.com/CyC2018/Markdown-Typesetting)。 - -#### 上传方案 - -笔者在本地使用为知笔记软件进行书写,为了方便将本地笔记内容上传到 Github 上,实现了一整套自动化上传方案,包括文本文件的导出、提取图片、Markdown 文档转换、Git 同步。进行 Markdown 文档转换是因为 Github 使用的 GFM 不支持 MathJax 公式和 TOC 标记,所以需要替换 MathJax 公式为 CodeCogs 的云服务和重新生成 TOC 目录。 - -笔者将自己实现文档转换功能提取出来,方便大家在需要将本地 Markdown 上传到 Github,或者制作项目 README 文档时生成目录时使用:[GFM-Converter](https://github.com/CyC2018/GFM-Converter)。 - #### Logo Power by [logomakr](https://logomakr.com/). From 0ae7415d6404782264327d497d4c48ef44769ebb Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sun, 20 Jan 2019 10:16:50 +0800 Subject: [PATCH 015/100] =?UTF-8?q?Update=20=E7=9B=B8=E5=85=B3=E5=86=85?= =?UTF-8?q?=E5=AE=B9.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 相关内容.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/相关内容.md b/相关内容.md index 986f6bfe..35357fcb 100644 --- a/相关内容.md +++ b/相关内容.md @@ -16,4 +16,13 @@ [🔎](https://github.com/CyC2018/Job-Recommend) +# 排版 + +笔记内容按照 [中文文案排版指北](https://mazhuang.org/wiki/chinese-copywriting-guidelines/) 进行排版,以保证内容的可读性。笔记不使用 `![]()` 这种方式来引用图片,而是用 `` 标签。一方面是为了能够控制图片以合适的大小显示,另一方面是因为 GFM 不支持 `
![]()
` 让图片居中显示,只能使用 `
` 达到居中的效果。笔者将自己实现的文档排版功能提取出来,放在 Github Page 中,无需下载安装即可免费使用:[Text-Typesetting](https://github.com/CyC2018/Markdown-Typesetting)。 + +# 上传方案 + +笔者在本地使用为知笔记软件进行书写,为了方便将本地笔记内容上传到 Github 上,实现了一整套自动化上传方案,包括文本文件的导出、提取图片、Markdown 文档转换、Git 同步。进行 Markdown 文档转换是因为 Github 使用的 GFM 不支持 MathJax 公式和 TOC 标记,所以需要替换 MathJax 公式为 CodeCogs 的云服务和重新生成 TOC 目录。 + +笔者将自己实现文档转换功能提取出来,方便大家在需要将本地 Markdown 上传到 Github,或者制作项目 README 文档时生成目录时使用:[GFM-Converter](https://github.com/CyC2018/GFM-Converter)。 From f11fcc2bb1feef7aa08c25a2a937f3d1203a0d2e Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sun, 20 Jan 2019 10:18:04 +0800 Subject: [PATCH 016/100] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 8a2890cd..b29d9432 100644 --- a/README.md +++ b/README.md @@ -92,12 +92,11 @@ 学习笔记不是从网上到处拼凑而来,除了少部分引用书上和技术文档的原文,其余都是笔者的原创。在您引用本仓库内容或者对内容进行修改演绎时,请遵循文末的开源协议,谢谢。 -#### 交流群 +#### QQ 群 为大家提供一个学习交流平台,在这里你可以自由地和笔者以及其他人进行技术上的交流。
-QQ 群 #### 知识星球 From d2822019b22a21102fc70495b3f45689e0ee9864 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sun, 20 Jan 2019 10:29:09 +0800 Subject: [PATCH 017/100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b29d9432..4477d6ef 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ - [构建工具](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/构建工具.md) - [正则表达式](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/正则表达式.md) -### :speak_no_evil: 编码实践 +### :mag_right: 编码实践 - [代码可读性](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/代码可读性.md) - [代码风格规范](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/代码风格规范.md) From 99625fc42b585c93b627174e02c58c8180fa2fb7 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sun, 20 Jan 2019 10:34:52 +0800 Subject: [PATCH 018/100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4477d6ef..9416cf04 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ - [HTTP](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/HTTP.md) - [Socket](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Socket.md) -### :couple: 面向对象 +### :unlock: 面向对象 - [设计模式](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/设计模式.md) - [面向对象思想](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/面向对象思想.md) From d222782fd10d4de5ba7e45e8a1668b16ef9f3b31 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sun, 20 Jan 2019 10:39:29 +0800 Subject: [PATCH 019/100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9416cf04..8b71bfea 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ - [缓存](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/缓存.md) - [消息队列](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/消息队列.md) -### :hammer: 工具 +### :wrench: 工具 - [Git](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Git.md) - [Docker](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Docker.md) From 5579ad3c3959ab111435a5501e270742896cfe29 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sun, 20 Jan 2019 10:42:21 +0800 Subject: [PATCH 020/100] auto commit --- docs/notes/Docker.md | 2 +- docs/notes/Git.md | 2 +- docs/notes/HTTP.md | 2 +- docs/notes/Java IO.md | 2 +- docs/notes/Java 基础.md | 2 +- docs/notes/Java 容器.md | 2 +- docs/notes/Java 并发.md | 2 +- docs/notes/Java 虚拟机.md | 2 +- docs/notes/Leetcode 题解.md | 2 +- docs/notes/Leetcode-Database 题解.md | 2 +- docs/notes/Linux.md | 2 +- docs/notes/MySQL.md | 2 +- docs/notes/Redis.md | 2 +- docs/notes/SQL.md | 2 +- docs/notes/Socket.md | 2 +- docs/notes/代码可读性.md | 2 +- docs/notes/代码风格规范.md | 2 +- docs/notes/分布式.md | 2 +- docs/notes/剑指 offer 题解.md | 2 +- docs/notes/攻击技术.md | 2 +- docs/notes/数据库系统原理.md | 2 +- docs/notes/构建工具.md | 2 +- docs/notes/正则表达式.md | 2 +- docs/notes/消息队列.md | 2 +- docs/notes/算法.md | 2 +- docs/notes/系统设计基础.md | 2 +- docs/notes/缓存.md | 2 +- docs/notes/计算机操作系统.md | 2 +- docs/notes/计算机网络.md | 2 +- docs/notes/设计模式.md | 2 +- docs/notes/集群.md | 2 +- docs/notes/面向对象思想.md | 2 +- 32 files changed, 32 insertions(+), 32 deletions(-) diff --git a/docs/notes/Docker.md b/docs/notes/Docker.md index 0e3dc2be..1c200848 100644 --- a/docs/notes/Docker.md +++ b/docs/notes/Docker.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、解决的问题](#一解决的问题) * [二、与虚拟机的比较](#二与虚拟机的比较) diff --git a/docs/notes/Git.md b/docs/notes/Git.md index 1fe30058..a594d197 100644 --- a/docs/notes/Git.md +++ b/docs/notes/Git.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [集中式与分布式](#集中式与分布式) * [中心服务器](#中心服务器) diff --git a/docs/notes/HTTP.md b/docs/notes/HTTP.md index a17faf13..67bc68ad 100644 --- a/docs/notes/HTTP.md +++ b/docs/notes/HTTP.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一 、基础概念](#一-基础概念) * [URI](#uri) diff --git a/docs/notes/Java IO.md b/docs/notes/Java IO.md index 2fdcc912..f0ae70c0 100644 --- a/docs/notes/Java IO.md +++ b/docs/notes/Java IO.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、概览](#一概览) * [二、磁盘操作](#二磁盘操作) diff --git a/docs/notes/Java 基础.md b/docs/notes/Java 基础.md index 99fa9bda..22d59c03 100644 --- a/docs/notes/Java 基础.md +++ b/docs/notes/Java 基础.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、数据类型](#一数据类型) * [包装类型](#包装类型) diff --git a/docs/notes/Java 容器.md b/docs/notes/Java 容器.md index a83674e0..04902d87 100644 --- a/docs/notes/Java 容器.md +++ b/docs/notes/Java 容器.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、概览](#一概览) * [Collection](#collection) diff --git a/docs/notes/Java 并发.md b/docs/notes/Java 并发.md index a4136cdd..b4f3693b 100644 --- a/docs/notes/Java 并发.md +++ b/docs/notes/Java 并发.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、线程状态转换](#一线程状态转换) * [新建(New)](#新建new) diff --git a/docs/notes/Java 虚拟机.md b/docs/notes/Java 虚拟机.md index 97df6d62..3eb14f67 100644 --- a/docs/notes/Java 虚拟机.md +++ b/docs/notes/Java 虚拟机.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、运行时数据区域](#一运行时数据区域) * [程序计数器](#程序计数器) diff --git a/docs/notes/Leetcode 题解.md b/docs/notes/Leetcode 题解.md index 3af938ab..9310948f 100644 --- a/docs/notes/Leetcode 题解.md +++ b/docs/notes/Leetcode 题解.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [算法思想](#算法思想) * [双指针](#双指针) diff --git a/docs/notes/Leetcode-Database 题解.md b/docs/notes/Leetcode-Database 题解.md index 404852a0..3ced862e 100644 --- a/docs/notes/Leetcode-Database 题解.md +++ b/docs/notes/Leetcode-Database 题解.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [595. Big Countries](#595-big-countries) * [627. Swap Salary](#627-swap-salary) diff --git a/docs/notes/Linux.md b/docs/notes/Linux.md index b11f38e4..3bd2affc 100644 --- a/docs/notes/Linux.md +++ b/docs/notes/Linux.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、常用操作以及概念](#一常用操作以及概念) * [快捷键](#快捷键) diff --git a/docs/notes/MySQL.md b/docs/notes/MySQL.md index 9a974080..720e476e 100644 --- a/docs/notes/MySQL.md +++ b/docs/notes/MySQL.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、索引](#一索引) * [B+ Tree 原理](#b-tree-原理) diff --git a/docs/notes/Redis.md b/docs/notes/Redis.md index e7745ace..2dc996c0 100644 --- a/docs/notes/Redis.md +++ b/docs/notes/Redis.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、概述](#一概述) * [二、数据类型](#二数据类型) diff --git a/docs/notes/SQL.md b/docs/notes/SQL.md index 8c1bbeec..8aa2bf38 100644 --- a/docs/notes/SQL.md +++ b/docs/notes/SQL.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、基础](#一基础) * [二、创建表](#二创建表) diff --git a/docs/notes/Socket.md b/docs/notes/Socket.md index 04b8e64b..92f4149f 100644 --- a/docs/notes/Socket.md +++ b/docs/notes/Socket.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、I/O 模型](#一io-模型) * [阻塞式 I/O](#阻塞式-io) diff --git a/docs/notes/代码可读性.md b/docs/notes/代码可读性.md index 06a2f19a..5486a1a7 100644 --- a/docs/notes/代码可读性.md +++ b/docs/notes/代码可读性.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、可读性的重要性](#一可读性的重要性) * [二、用名字表达代码含义](#二用名字表达代码含义) diff --git a/docs/notes/代码风格规范.md b/docs/notes/代码风格规范.md index f26bca7f..2630a604 100644 --- a/docs/notes/代码风格规范.md +++ b/docs/notes/代码风格规范.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) diff --git a/docs/notes/分布式.md b/docs/notes/分布式.md index 0ca370c1..ac7e06dc 100644 --- a/docs/notes/分布式.md +++ b/docs/notes/分布式.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、分布式锁](#一分布式锁) * [数据库的唯一索引](#数据库的唯一索引) diff --git a/docs/notes/剑指 offer 题解.md b/docs/notes/剑指 offer 题解.md index fdb7f591..68bf3267 100644 --- a/docs/notes/剑指 offer 题解.md +++ b/docs/notes/剑指 offer 题解.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [1. 前言](#1-前言) * [2. 实现 Singleton](#2-实现-singleton) diff --git a/docs/notes/攻击技术.md b/docs/notes/攻击技术.md index 64f1a5f5..2fa52c0f 100644 --- a/docs/notes/攻击技术.md +++ b/docs/notes/攻击技术.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、跨站脚本攻击](#一跨站脚本攻击) * [二、跨站请求伪造](#二跨站请求伪造) diff --git a/docs/notes/数据库系统原理.md b/docs/notes/数据库系统原理.md index d09d569c..9b64c774 100644 --- a/docs/notes/数据库系统原理.md +++ b/docs/notes/数据库系统原理.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、事务](#一事务) * [概念](#概念) diff --git a/docs/notes/构建工具.md b/docs/notes/构建工具.md index 0c90a183..cb07e5c9 100644 --- a/docs/notes/构建工具.md +++ b/docs/notes/构建工具.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、构建工具的作用](#一构建工具的作用) * [二、Java 主流构建工具](#二java-主流构建工具) diff --git a/docs/notes/正则表达式.md b/docs/notes/正则表达式.md index 812eca2f..16d96122 100644 --- a/docs/notes/正则表达式.md +++ b/docs/notes/正则表达式.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、概述](#一概述) * [二、匹配单个字符](#二匹配单个字符) diff --git a/docs/notes/消息队列.md b/docs/notes/消息队列.md index 53549724..e6fdf040 100644 --- a/docs/notes/消息队列.md +++ b/docs/notes/消息队列.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、消息模型](#一消息模型) * [点对点](#点对点) diff --git a/docs/notes/算法.md b/docs/notes/算法.md index 55cf74b6..267085e4 100644 --- a/docs/notes/算法.md +++ b/docs/notes/算法.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、前言](#一前言) * [二、算法分析](#二算法分析) diff --git a/docs/notes/系统设计基础.md b/docs/notes/系统设计基础.md index 8ded249e..4cf9cd7c 100644 --- a/docs/notes/系统设计基础.md +++ b/docs/notes/系统设计基础.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、性能](#一性能) * [二、伸缩性](#二伸缩性) diff --git a/docs/notes/缓存.md b/docs/notes/缓存.md index fd6ac52c..31fe9e20 100644 --- a/docs/notes/缓存.md +++ b/docs/notes/缓存.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、缓存特征](#一缓存特征) * [二、LRU](#二lru) diff --git a/docs/notes/计算机操作系统.md b/docs/notes/计算机操作系统.md index ae562637..a6895a66 100644 --- a/docs/notes/计算机操作系统.md +++ b/docs/notes/计算机操作系统.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、概述](#一概述) * [基本特征](#基本特征) diff --git a/docs/notes/计算机网络.md b/docs/notes/计算机网络.md index 596fc628..192596f8 100644 --- a/docs/notes/计算机网络.md +++ b/docs/notes/计算机网络.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、概述](#一概述) * [网络的网络](#网络的网络) diff --git a/docs/notes/设计模式.md b/docs/notes/设计模式.md index 9b4ddeee..a85dea0e 100644 --- a/docs/notes/设计模式.md +++ b/docs/notes/设计模式.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、概述](#一概述) * [二、创建型](#二创建型) diff --git a/docs/notes/集群.md b/docs/notes/集群.md index ba0b44fb..f42524b7 100644 --- a/docs/notes/集群.md +++ b/docs/notes/集群.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、负载均衡](#一负载均衡) * [负载均衡算法](#负载均衡算法) diff --git a/docs/notes/面向对象思想.md b/docs/notes/面向对象思想.md index 8b317b01..ba7fe359 100644 --- a/docs/notes/面向对象思想.md +++ b/docs/notes/面向对象思想.md @@ -1,4 +1,4 @@ -[🎉 面试进阶专栏限时优惠](https://xiaozhuanlan.com/CyC2018) +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) * [一、三大特性](#一三大特性) * [封装](#封装) From 880fa19db1db7b72428ce77855701db1e1232bae Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sun, 20 Jan 2019 10:46:05 +0800 Subject: [PATCH 021/100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8b71bfea..14f9f691 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ --> -[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) +
From 21f16dd08cb374fd40ee172e9477d42e6255790f Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sun, 20 Jan 2019 10:48:01 +0800 Subject: [PATCH 024/100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5d84e0f9..6bc333eb 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ --> - +
From 0b7e53ff22670821358ac94f2126f5d9b4f56eba Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sun, 20 Jan 2019 10:49:30 +0800 Subject: [PATCH 025/100] Update README.md --- README.md | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 6bc333eb..1375ca72 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,6 @@ --> - - -
@@ -26,29 +23,29 @@
-### :pencil2: 算法 +## :pencil2: 算法 - [剑指 Offer 题解](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/剑指%20offer%20题解.md) - [Leetcode 题解](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Leetcode%20题解.md) - [算法](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/算法.md) -### :computer: 操作系统 +## :computer: 操作系统 - [计算机操作系统](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/计算机操作系统.md) - [Linux](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Linux.md) -### :cloud: 网络 +## :cloud: 网络 - [计算机网络](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/计算机网络.md) - [HTTP](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/HTTP.md) - [Socket](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Socket.md) -### :unlock: 面向对象 +## :unlock: 面向对象 - [设计模式](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/设计模式.md) - [面向对象思想](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/面向对象思想.md) -### :floppy_disk: 数据库 +## :floppy_disk: 数据库 - [数据库系统原理](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/数据库系统原理.md) - [SQL](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/SQL.md) @@ -56,7 +53,7 @@ - [MySQL](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/MySQL.md) - [Redis](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Redis.md) -### :coffee: Java +## :coffee: Java - [Java 基础](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Java%20基础.md) - [Java 容器](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Java%20容器.md) @@ -64,7 +61,7 @@ - [Java 虚拟机](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Java%20虚拟机.md) - [Java I/O](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Java%20IO.md) -### :bulb: 系统设计 +## :bulb: 系统设计 - [系统设计基础](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/系统设计基础.md) - [分布式](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/分布式.md) @@ -73,53 +70,53 @@ - [缓存](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/缓存.md) - [消息队列](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/消息队列.md) -### :wrench: 工具 +## :wrench: 工具 - [Git](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Git.md) - [Docker](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Docker.md) - [构建工具](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/构建工具.md) - [正则表达式](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/正则表达式.md) -### :mag_right: 编码实践 +## :mag_right: 编码实践 - [代码可读性](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/代码可读性.md) - [代码风格规范](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/代码风格规范.md) -### :memo: 后记 +## :memo: 后记 -#### 关于 +### 关于 本仓库主要是根据计算机经典书籍以及官方技术文档进行总结的学习笔记,希望对大家有所帮助。 学习笔记不是从网上到处拼凑而来,除了少部分引用书上和技术文档的原文,其余都是笔者的原创。在您引用本仓库内容或者对内容进行修改演绎时,请遵循文末的开源协议,谢谢。 -#### QQ 群 +### QQ 群 为大家提供一个学习交流平台,在这里你可以自由地和笔者以及其他人进行技术上的交流。
-#### 知识星球 +### 知识星球 想要向笔者提问关于学习和求职方面的建议?来知识星球,你的每个提问我都会认真回答。 [🎓](other/Planet.md) -#### 内推信息 +### 内推信息 [🔎](https://github.com/CyC2018/Job-Recommend) -#### Logo +### Logo Power by [logomakr](https://logomakr.com/). -#### License +### License 在对本作品进行演绎时,请署名并以相同方式共享。 知识共享许可协议 -#### 致谢 +### 致谢 感谢以下人员对本仓库做出的贡献,当然不仅仅只有这些贡献者,这里就不一一列举了。如果你希望被添加到这个名单中,并且提交过 Issue 或者 PR,请与笔者联系。 From 53ce789fa0dbc6ff10f28cd4b3d0066afdee65d7 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sun, 20 Jan 2019 10:50:05 +0800 Subject: [PATCH 026/100] Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1375ca72..cb561228 100644 --- a/README.md +++ b/README.md @@ -110,12 +110,6 @@ Power by [logomakr](https://logomakr.com/). -### License - -在对本作品进行演绎时,请署名并以相同方式共享。 - -知识共享许可协议 - ### 致谢 感谢以下人员对本仓库做出的贡献,当然不仅仅只有这些贡献者,这里就不一一列举了。如果你希望被添加到这个名单中,并且提交过 Issue 或者 PR,请与笔者联系。 @@ -147,3 +141,9 @@ Power by [logomakr](https://logomakr.com/). + +### License + +在对本作品进行演绎时,请署名并以相同方式共享。 + +知识共享许可协议 From 23cb60bc178fcfa1e68c32e433a34c81be5b98b5 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sun, 20 Jan 2019 10:50:59 +0800 Subject: [PATCH 027/100] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index cb561228..b576163f 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ |    Java   
[:coffee:](#coffee-java) |    系统设计   
[:bulb:](#bulb-系统设计) |     工具    
[:hammer:](#hammer-工具) |    编码实践   
[:speak_no_evil:](#speak_no_evil-编码实践) |    后记    
[:memo:](#memo-后记) | --> +[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018)
From bebb900ab8cbf84f4978c20d917e71377c4df209 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sun, 20 Jan 2019 10:51:20 +0800 Subject: [PATCH 028/100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b576163f..18945410 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@
- +
From 0fad0d317e7acaedfb6b92ca00bd1c94a88acb06 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Sun, 20 Jan 2019 22:05:09 +0800 Subject: [PATCH 029/100] Update README.md --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 18945410..e82f7581 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,16 @@ [🔎](https://github.com/CyC2018/Job-Recommend) +### 排版 + +笔记内容按照 中文文案排版指北 进行排版,以保证内容的可读性。笔记不使用 ![]() 这种方式来引用图片,而是用 标签。一方面是为了能够控制图片以合适的大小显示,另一方面是因为 GFM 不支持 `
![]()
` 让图片居中显示,只能使用 `
` 达到居中的效果。笔者将自己实现的文档排版功能提取出来,放在 Github Page 中,无需下载安装即可免费使用:Text-Typesetting。 + +### 上传方案 + +笔者在本地使用为知笔记软件进行书写,为了方便将本地笔记内容上传到 Github 上,实现了一整套自动化上传方案,包括文本文件的导出、提取图片、Markdown 文档转换、Git 同步。进行 Markdown 文档转换是因为 Github 使用的 GFM 不支持 MathJax 公式和 TOC 标记,所以需要替换 MathJax 公式为 CodeCogs 的云服务和重新生成 TOC 目录。 + +笔者将自己实现文档转换功能提取出来,方便大家在需要将本地 Markdown 上传到 Github,或者制作项目 README 文档时生成目录时使用:GFM-Converter。 + ### Logo Power by [logomakr](https://logomakr.com/). From e7c2bb6fecc3186e5919f47b5714e2b306d0646a Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Mon, 21 Jan 2019 23:17:18 +0800 Subject: [PATCH 030/100] auto commit --- docs/notes/pics/1.gif | Bin 0 -> 19456 bytes docs/notes/剑指 offer 题解.md | 15 +++++---------- 2 files changed, 5 insertions(+), 10 deletions(-) create mode 100644 docs/notes/pics/1.gif diff --git a/docs/notes/pics/1.gif b/docs/notes/pics/1.gif new file mode 100644 index 0000000000000000000000000000000000000000..fd2173a57a0499ab0f0e38d65a595dab42c8fa28 GIT binary patch literal 19456 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 Y?YTQ@RyTp%bE%)}|L6Mu&sO;V0rZn9761SM literal 0 HcmV?d00001 diff --git a/docs/notes/剑指 offer 题解.md b/docs/notes/剑指 offer 题解.md index 68bf3267..b30c78c4 100644 --- a/docs/notes/剑指 offer 题解.md +++ b/docs/notes/剑指 offer 题解.md @@ -84,11 +84,6 @@ # 1. 前言 -本文的绘图可通过以下途径免费获得并使用: - -- [ProcessOn](https://www.processon.com/view/5a3e4c7be4b0909c1aa18b49) -- [DrawIO](https://drive.google.com/file/d/1nSSCpPUC05MFoeFuf_aeTtkm7dG5-bJ1/view?usp=sharing) - 本文内容可在微信小程序中阅读:

@@ -115,11 +110,13 @@ Output: ## 解题思路 -要求复杂度为 O(N) + O(1),也就是时间复杂度 O(N),空间复杂度 O(1)。因此不能使用排序的方法,也不能使用额外的标记数组。牛客网讨论区这一题的首票答案使用 nums[i] + length 来将元素标记,这么做会有加法溢出问题。 +要求复杂度为 O(N) + O(1),也就是时间复杂度 O(N),空间复杂度 O(1)。因此不能使用排序的方法,也不能使用额外的标记数组。 -这种数组元素在 [0, n-1] 范围内的问题,可以将值为 i 的元素调整到第 i 个位置上。 +对于这种数组元素在 [0, n-1] 范围内的问题,可以将值为 i 的元素调整到第 i 个位置上进行求解。 -以 (2, 3, 1, 0, 2, 5) 为例: +以 (2, 3, 1, 0, 2, 5) 为例,遍历到位置 4 时,该位置上的数为 2,但是第 2 个位置上已经有一个 2 的值了,因此可以知道 2 重复: + +

```text position-0 : (2,3,1,0,2,5) // 2 <-> 1 @@ -132,8 +129,6 @@ position-3 : (0,1,2,3,2,5) // already in position position-4 : (0,1,2,3,2,5) // nums[i] == nums[nums[i]], exit ``` -遍历到位置 4 时,该位置上的数为 2,但是第 2 个位置上已经有一个 2 的值了,因此可以知道 2 重复。 - ```java public boolean duplicate(int[] nums, int length, int[] duplication) { if (nums == null || length <= 0) From 6d906577b9aa121c277f12c411f8e8e0f82f4e62 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Mon, 21 Jan 2019 23:35:47 +0800 Subject: [PATCH 031/100] auto commit --- docs/notes/pics/11548084848123.gif | Bin 0 -> 25600 bytes docs/notes/剑指 offer 题解.md | 12 +----------- 2 files changed, 1 insertion(+), 11 deletions(-) create mode 100644 docs/notes/pics/11548084848123.gif diff --git a/docs/notes/pics/11548084848123.gif b/docs/notes/pics/11548084848123.gif new file mode 100644 index 0000000000000000000000000000000000000000..278e51f5b5b6d6709ed37b84aeb3e74194d4e685 GIT binary patch literal 25600 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 Rl(YWk|C|4B{{N5s{||p&oNWLA literal 0 HcmV?d00001 diff --git a/docs/notes/剑指 offer 题解.md b/docs/notes/剑指 offer 题解.md index b30c78c4..e63bcb23 100644 --- a/docs/notes/剑指 offer 题解.md +++ b/docs/notes/剑指 offer 题解.md @@ -116,18 +116,8 @@ Output: 以 (2, 3, 1, 0, 2, 5) 为例,遍历到位置 4 时,该位置上的数为 2,但是第 2 个位置上已经有一个 2 的值了,因此可以知道 2 重复: -

+

-```text -position-0 : (2,3,1,0,2,5) // 2 <-> 1 - (1,3,2,0,2,5) // 1 <-> 3 - (3,1,2,0,2,5) // 3 <-> 0 - (0,1,2,3,2,5) // already in position -position-1 : (0,1,2,3,2,5) // already in position -position-2 : (0,1,2,3,2,5) // already in position -position-3 : (0,1,2,3,2,5) // already in position -position-4 : (0,1,2,3,2,5) // nums[i] == nums[nums[i]], exit -``` ```java public boolean duplicate(int[] nums, int length, int[] duplication) { From c527a32f9dc8b5ab616b85dec68f8cd2010918d5 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Mon, 21 Jan 2019 23:43:27 +0800 Subject: [PATCH 032/100] auto commit --- docs/pics/1.gif | Bin 0 -> 19456 bytes docs/pics/11548084848123.gif | Bin 0 -> 25600 bytes .../d5d3b7ae-2712-412e-98f1-633ce6ec5955.png | Bin 0 -> 29696 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/pics/1.gif create mode 100644 docs/pics/11548084848123.gif create mode 100644 docs/pics/d5d3b7ae-2712-412e-98f1-633ce6ec5955.png diff --git a/docs/pics/1.gif b/docs/pics/1.gif new file mode 100644 index 0000000000000000000000000000000000000000..fd2173a57a0499ab0f0e38d65a595dab42c8fa28 GIT binary patch literal 19456 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 Y?YTQ@RyTp%bE%)}|L6Mu&sO;V0rZn9761SM literal 0 HcmV?d00001 diff --git a/docs/pics/11548084848123.gif b/docs/pics/11548084848123.gif new file mode 100644 index 0000000000000000000000000000000000000000..278e51f5b5b6d6709ed37b84aeb3e74194d4e685 GIT binary patch literal 25600 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 Rl(YWk|C|4B{{N5s{||p&oNWLA literal 0 HcmV?d00001 diff --git a/docs/pics/d5d3b7ae-2712-412e-98f1-633ce6ec5955.png b/docs/pics/d5d3b7ae-2712-412e-98f1-633ce6ec5955.png new file mode 100644 index 0000000000000000000000000000000000000000..2dde7dab4d2a0d20c439fd5d550811b9b9af5595 GIT binary patch literal 29696 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 literal 0 HcmV?d00001 From d5e16e997c894e18326602a7116274414b3757c7 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Mon, 21 Jan 2019 23:55:19 +0800 Subject: [PATCH 033/100] =?UTF-8?q?Update=20=E5=89=91=E6=8C=87=20offer=20?= =?UTF-8?q?=E9=A2=98=E8=A7=A3.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/notes/剑指 offer 题解.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/notes/剑指 offer 题解.md b/docs/notes/剑指 offer 题解.md index e63bcb23..9cfdaa1a 100644 --- a/docs/notes/剑指 offer 题解.md +++ b/docs/notes/剑指 offer 题解.md @@ -116,7 +116,7 @@ Output: 以 (2, 3, 1, 0, 2, 5) 为例,遍历到位置 4 时,该位置上的数为 2,但是第 2 个位置上已经有一个 2 的值了,因此可以知道 2 重复: -

+

```java From dd65ec57d3ab8f9b78b8b13075247d8d7fefc1f4 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Tue, 22 Jan 2019 00:01:54 +0800 Subject: [PATCH 034/100] =?UTF-8?q?Update=20=E5=89=91=E6=8C=87=20offer=20?= =?UTF-8?q?=E9=A2=98=E8=A7=A3.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/notes/剑指 offer 题解.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/notes/剑指 offer 题解.md b/docs/notes/剑指 offer 题解.md index 9cfdaa1a..140023c7 100644 --- a/docs/notes/剑指 offer 题解.md +++ b/docs/notes/剑指 offer 题解.md @@ -116,7 +116,7 @@ Output: 以 (2, 3, 1, 0, 2, 5) 为例,遍历到位置 4 时,该位置上的数为 2,但是第 2 个位置上已经有一个 2 的值了,因此可以知道 2 重复: -

+

```java From d7df1f556f3f8c1293e88279edaa991049c22649 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Tue, 22 Jan 2019 18:26:48 +0800 Subject: [PATCH 035/100] Update _coverpage.md --- docs/_coverpage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_coverpage.md b/docs/_coverpage.md index 473cd765..933e845d 100644 --- a/docs/_coverpage.md +++ b/docs/_coverpage.md @@ -2,7 +2,7 @@ # CS-Notes -> Computer Sicence Learning Notes. +> Computer Science Learning Notes. - Algorithm - Computer Networks From 315655827b0a8ad267a809ff2a883857e15b7f4b Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Thu, 24 Jan 2019 09:06:42 +0800 Subject: [PATCH 036/100] auto commit --- docs/notes/Docker.md | 90 +- docs/notes/Git.md | 143 +- docs/notes/HTTP.md | 950 ++- docs/notes/Java IO.md | 708 +- docs/notes/Java 基础.md | 1439 ++--- docs/notes/Java 容器.md | 1456 +++-- docs/notes/Java 并发.md | 1809 +++--- docs/notes/Java 虚拟机.md | 750 ++- docs/notes/Leetcode 题解.md | 8858 +++++++++++++------------- docs/notes/Leetcode-Database 题解.md | 904 ++- docs/notes/Linux.md | 1262 ++-- docs/notes/MySQL.md | 393 +- docs/notes/Redis.md | 720 +-- docs/notes/SQL.md | 767 ++- docs/notes/Socket.md | 374 +- docs/notes/代码可读性.md | 325 +- docs/notes/代码风格规范.md | 11 +- docs/notes/分布式.md | 336 +- docs/notes/剑指 offer 题解.md | 3561 +++++------ docs/notes/攻击技术.md | 152 +- docs/notes/数据库系统原理.md | 577 +- docs/notes/构建工具.md | 147 +- docs/notes/正则表达式.md | 346 +- docs/notes/消息队列.md | 62 +- docs/notes/算法.md | 2939 ++++----- docs/notes/系统设计基础.md | 73 +- docs/notes/缓存.md | 307 +- docs/notes/计算机操作系统.md | 1092 ++-- docs/notes/计算机网络.md | 898 ++- docs/notes/设计模式.md | 3579 +++++------ docs/notes/集群.md | 152 +- docs/notes/面向对象思想.md | 342 +- 32 files changed, 17431 insertions(+), 18091 deletions(-) diff --git a/docs/notes/Docker.md b/docs/notes/Docker.md index 1c200848..ffbadb83 100644 --- a/docs/notes/Docker.md +++ b/docs/notes/Docker.md @@ -1,92 +1,86 @@ -[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) - -* [一、解决的问题](#一解决的问题) -* [二、与虚拟机的比较](#二与虚拟机的比较) -* [三、优势](#三优势) -* [四、使用场景](#四使用场景) -* [五、镜像与容器](#五镜像与容器) -* [参考资料](#参考资料) - - - -# 一、解决的问题 +# 一、解决的问题 由于不同的机器有不同的操作系统,以及不同的库和组件,在将一个应用部署到多台机器上需要进行大量的环境配置操作。 -Docker 主要解决环境配置问题,它是一种虚拟化技术,对进程进行隔离,被隔离的进程独立于宿主操作系统和其它隔离的进程。使用 Docker 可以不修改应用程序代码,不需要开发人员学习特定环境下的技术,就能够将现有的应用程序部署在其他机器中。 +Docker 主要解决环境配置问题,它是一种虚拟化技术,对进程进行隔离,被隔离的进程独立于宿主操作系统和其它隔离的进程。使用 Docker 可以不修改应用程序代码,不需要开发人员学习特定环境下的技术,就能够将现有的应用程序部署在其他机器中。 -

+ -# 二、与虚拟机的比较 +# 二、与虚拟机的比较 -虚拟机也是一种虚拟化技术,它与 Docker 最大的区别在于它是通过模拟硬件,并在硬件上安装操作系统来实现。 +虚拟机也是一种虚拟化技术,它与 Docker 最大的区别在于它是通过模拟硬件,并在硬件上安装操作系统来实现。 -

+ -

+ -## 启动速度 +## 启动速度 启动虚拟机需要启动虚拟机的操作系统,再启动应用,这个过程非常慢; -而启动 Docker 相当于启动宿主操作系统上的一个进程。 +而启动 Docker 相当于启动宿主操作系统上的一个进程。 -## 占用资源 +## 占用资源 -虚拟机是一个完整的操作系统,需要占用大量的磁盘、内存和 CPU,一台机器只能开启几十个的虚拟机。 +虚拟机是一个完整的操作系统,需要占用大量的磁盘、内存和 CPU,一台机器只能开启几十个的虚拟机。 -而 Docker 只是一个进程,只需要将应用以及相关的组件打包,在运行时占用很少的资源,一台机器可以开启成千上万个 Docker。 +而 Docker 只是一个进程,只需要将应用以及相关的组件打包,在运行时占用很少的资源,一台机器可以开启成千上万个 Docker。 -# 三、优势 +# 三、优势 -除了启动速度快以及占用资源少之外,Docker 具有以下优势: +除了启动速度快以及占用资源少之外,Docker 具有以下优势: -## 更容易迁移 +## 更容易迁移 提供一致性的运行环境,可以在不同的机器上进行迁移,而不用担心环境变化导致无法运行。 -## 更容易维护 +## 更容易维护 使用分层技术和镜像,使得应用可以更容易复用重复部分。复用程度越高,维护工作也越容易。 -## 更容易扩展 +## 更容易扩展 可以使用基础镜像进一步扩展得到新的镜像,并且官方和开源社区提供了大量的镜像,通过扩展这些镜像可以非常容易得到我们想要的镜像。 -# 四、使用场景 +# 四、使用场景 -## 持续集成 +## 持续集成 持续集成指的是频繁地将代码集成到主干上,这样能够更快地发现错误。 -Docker 具有轻量级以及隔离性的特点,在将代码集成到一个 Docker 中不会对其它 Docker 产生影响。 +Docker 具有轻量级以及隔离性的特点,在将代码集成到一个 Docker 中不会对其它 Docker 产生影响。 -## 提供可伸缩的云服务 +## 提供可伸缩的云服务 -根据应用的负载情况,可以很容易地增加或者减少 Docker。 +根据应用的负载情况,可以很容易地增加或者减少 Docker。 -## 搭建微服务架构 +## 搭建微服务架构 -Docker 轻量级的特点使得它很适合用于部署、维护、组合微服务。 +Docker 轻量级的特点使得它很适合用于部署、维护、组合微服务。 -# 五、镜像与容器 +# 五、镜像与容器 镜像是一种静态的结构,可以看成面向对象里面的类,而容器是镜像的一个实例。 -镜像包含着容器运行时所需要的代码以及其它组件,它是一种分层结构,每一层都是只读的(read-only layers)。构建镜像时,会一层一层构建,前一层是后一层的基础。镜像的这种分层存储结构很适合镜像的复用以及定制。 +镜像包含着容器运行时所需要的代码以及其它组件,它是一种分层结构,每一层都是只读的(read-only layers)。构建镜像时,会一层一层构建,前一层是后一层的基础。镜像的这种分层存储结构很适合镜像的复用以及定制。 -构建容器时,通过在镜像的基础上添加一个可写层(writable layer),用来保存着容器运行过程中的修改。 +构建容器时,通过在镜像的基础上添加一个可写层(writable layer),用来保存着容器运行过程中的修改。 -

+![](pics/docker-filesystems-busyboxrw.png) -# 参考资料 +# 参考资料 -- [DOCKER 101: INTRODUCTION TO DOCKER WEBINAR RECAP](https://blog.docker.com/2017/08/docker-101-introduction-docker-webinar-recap/) -- [Docker 入门教程](http://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html) -- [Docker container vs Virtual machine](http://www.bogotobogo.com/DevOps/Docker/Docker_Container_vs_Virtual_Machine.php) -- [How to Create Docker Container using Dockerfile](https://linoxide.com/linux-how-to/dockerfile-create-docker-container/) -- [理解 Docker(2):Docker 镜像](http://www.cnblogs.com/sammyliu/p/5877964.html) -- [为什么要使用 Docker?](https://yeasy.gitbooks.io/docker_practice/introduction/why.html) -- [What is Docker](https://www.docker.com/what-docker) -- [持续集成是什么?](http://www.ruanyifeng.com/blog/2015/09/continuous-integration.html) +- [DOCKER 101: INTRODUCTION TO DOCKER WEBINAR RECAP](https://blog.docker.com/2017/08/docker-101-introduction-docker-webinar-recap/) +- [Docker 入门教程](http://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html) +- [Docker container vs Virtual machine](http://www.bogotobogo.com/DevOps/Docker/Docker_Container_vs_Virtual_Machine.php) +- [How to Create Docker Container using Dockerfile](https://linoxide.com/linux-how-to/dockerfile-create-docker-container/) +- [理解 Docker(2):Docker 镜像](http://www.cnblogs.com/sammyliu/p/5877964.html) +- [为什么要使用 Docker?](https://yeasy.gitbooks.io/docker_practice/introduction/why.html) +- [What is Docker](https://www.docker.com/what-docker) +- [持续集成是什么?](http://www.ruanyifeng.com/blog/2015/09/continuous-integration.html) +---bottom---CyC--- +![](pics/011f3ef6-d824-4d43-8b2c-36dab8eaaa72-1.png) +![](pics/71f61bc3-582d-4c27-8bdd-dc7fb135bf8f.png) +![](pics/7e873b60-44dc-4911-b080-defd5b8f0b49.png) +![](pics/docker-filesystems-busyboxrw.png) diff --git a/docs/notes/Git.md b/docs/notes/Git.md index a594d197..0c6a29fa 100644 --- a/docs/notes/Git.md +++ b/docs/notes/Git.md @@ -1,23 +1,6 @@ -[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) - -* [集中式与分布式](#集中式与分布式) -* [中心服务器](#中心服务器) -* [工作流](#工作流) -* [分支实现](#分支实现) -* [冲突](#冲突) -* [Fast forward](#fast-forward) -* [分支管理策略](#分支管理策略) -* [储藏(Stashing)](#储藏stashing) -* [SSH 传输设置](#ssh-传输设置) -* [.gitignore 文件](#gitignore-文件) -* [Git 命令一览](#git-命令一览) -* [参考资料](#参考资料) - +# 集中式与分布式 - -# 集中式与分布式 - -Git 属于分布式版本控制系统,而 SVN 属于集中式。 +Git 属于分布式版本控制系统,而 SVN 属于集中式。 集中式版本控制只有中心服务器拥有一份代码,而分布式版本控制每个人的电脑上就有一份完整的代码。 @@ -27,133 +10,133 @@ Git 属于分布式版本控制系统,而 SVN 属于集中式。 分布式版本控制新建分支、合并分支操作速度非常快,而集中式版本控制新建一个分支相当于复制一份完整代码。 -# 中心服务器 +# 中心服务器 -中心服务器用来交换每个用户的修改,没有中心服务器也能工作,但是中心服务器能够 24 小时保持开机状态,这样就能更方便的交换修改。 +中心服务器用来交换每个用户的修改,没有中心服务器也能工作,但是中心服务器能够 24 小时保持开机状态,这样就能更方便的交换修改。 -Github 就是一个中心服务器。 +Github 就是一个中心服务器。 -# 工作流 +# 工作流 -

+![](index_files/a1198642-9159-4d88-8aec-c3b04e7a2563.jpg) -新建一个仓库之后,当前目录就成为了工作区,工作区下有一个隐藏目录 .git,它属于 Git 的版本库。 +新建一个仓库之后,当前目录就成为了工作区,工作区下有一个隐藏目录 .git,它属于 Git 的版本库。 -Git 版本库有一个称为 stage 的暂存区,还有自动创建的 master 分支以及指向分支的 HEAD 指针。 +Git 版本库有一个称为 stage 的暂存区,还有自动创建的 master 分支以及指向分支的 HEAD 指针。 -

+![](index_files/46f66e88-e65a-4ad0-a060-3c63fe22947c.png) -- git add files 把文件的修改添加到暂存区 -- git commit 把暂存区的修改提交到当前分支,提交之后暂存区就被清空了 -- git reset -- files 使用当前分支上的修改覆盖暂存区,用来撤销最后一次 git add files -- git checkout -- files 使用暂存区的修改覆盖工作目录,用来撤销本地修改 +- git add files 把文件的修改添加到暂存区 +- git commit 把暂存区的修改提交到当前分支,提交之后暂存区就被清空了 +- git reset -- files 使用当前分支上的修改覆盖暂存区,用来撤销最后一次 git add files +- git checkout -- files 使用暂存区的修改覆盖工作目录,用来撤销本地修改 -

+![](index_files/17976404-95f5-480e-9cb4-250e6aa1d55f.png) 可以跳过暂存区域直接从分支中取出修改,或者直接提交修改到分支中。 -- git commit -a 直接把所有文件的修改添加到暂存区然后执行提交 -- git checkout HEAD -- files 取出最后一次修改,可以用来进行回滚操作 +- git commit -a 直接把所有文件的修改添加到暂存区然后执行提交 +- git checkout HEAD -- files 取出最后一次修改,可以用来进行回滚操作 -# 分支实现 +# 分支实现 -使用指针将每个提交连接成一条时间线,HEAD 指针指向当前分支指针。 +使用指针将每个提交连接成一条时间线,HEAD 指针指向当前分支指针。 -

+![](index_files/fb546e12-e1fb-4b72-a1fb-8a7f5000dce6.jpg) -新建分支是新建一个指针指向时间线的最后一个节点,并让 HEAD 指针指向新分支表示新分支成为当前分支。 +新建分支是新建一个指针指向时间线的最后一个节点,并让 HEAD 指针指向新分支表示新分支成为当前分支。 -

+![](index_files/bc775758-89ab-4805-9f9c-78b8739cf780.jpg) 每次提交只会让当前分支指针向前移动,而其它分支指针不会移动。 -

+![](index_files/5292faa6-0141-4638-bf0f-bb95b081dcba.jpg) 合并分支也只需要改变指针即可。 -

+![](index_files/1164a71f-413d-494a-9cc8-679fb6a2613d.jpg) -# 冲突 +# 冲突 当两个分支都对同一个文件的同一行进行了修改,在分支合并时就会产生冲突。 -

+![](index_files/58e57a21-6b6b-40b6-af85-956dd4e0f55a.jpg) -Git 会使用 <<<<<<< ,======= ,>>>>>>> 标记出不同分支的内容,只需要把不同分支中冲突部分修改成一样就能解决冲突。 +Git 会使用 <<<<<<< ,======= ,>>>>>>> 标记出不同分支的内容,只需要把不同分支中冲突部分修改成一样就能解决冲突。 ``` -<<<<<<< HEAD -Creating a new branch is quick & simple. +<<<<<<< HEAD +Creating a new branch is quick & simple. ======= -Creating a new branch is quick AND simple. ->>>>>>> feature1 +Creating a new branch is quick AND simple. +>>>>>>> feature1 ``` -# Fast forward +# Fast forward -"快进式合并"(fast-farward merge),会直接将 master 分支指向合并的分支,这种模式下进行分支合并会丢失分支信息,也就不能在分支历史上看出分支信息。 +"快进式合并"(fast-farward merge),会直接将 master 分支指向合并的分支,这种模式下进行分支合并会丢失分支信息,也就不能在分支历史上看出分支信息。 -可以在合并时加上 --no-ff 参数来禁用 Fast forward 模式,并且加上 -m 参数让合并时产生一个新的 commit。 +可以在合并时加上 --no-ff 参数来禁用 Fast forward 模式,并且加上 -m 参数让合并时产生一个新的 commit。 ``` -$ git merge --no-ff -m "merge with no-ff" dev +$ git merge --no-ff -m "merge with no-ff" dev ``` -

+![](index_files/dd78a1fe-1ff3-4bcf-a56f-8c003995beb6.jpg) -# 分支管理策略 +# 分支管理策略 -master 分支应该是非常稳定的,只用来发布新版本; +master 分支应该是非常稳定的,只用来发布新版本; -日常开发在开发分支 dev 上进行。 +日常开发在开发分支 dev 上进行。 -

+![](index_files/245fd2fb-209c-4ad5-bc5e-eb5664966a0e.jpg) -# 储藏(Stashing) +# 储藏(Stashing) 在一个分支上操作之后,如果还没有将修改提交到分支上,此时进行切换分支,那么另一个分支上也能看到新的修改。这是因为所有分支都共用一个工作区的缘故。 -可以使用 git stash 将当前分支的修改储藏起来,此时当前工作区的所有修改都会被存到栈上,也就是说当前工作区是干净的,没有任何未提交的修改。此时就可以安全的切换到其它分支上了。 +可以使用 git stash 将当前分支的修改储藏起来,此时当前工作区的所有修改都会被存到栈上,也就是说当前工作区是干净的,没有任何未提交的修改。此时就可以安全的切换到其它分支上了。 ``` -$ git stash -Saved working directory and index state \ "WIP on master: 049d078 added the index file" -HEAD is now at 049d078 added the index file (To restore them type "git stash apply") +$ git stash +Saved working directory and index state \ "WIP on master: 049d078 added the index file" +HEAD is now at 049d078 added the index file (To restore them type "git stash apply") ``` -该功能可以用于 bug 分支的实现。如果当前正在 dev 分支上进行开发,但是此时 master 上有个 bug 需要修复,但是 dev 分支上的开发还未完成,不想立即提交。在新建 bug 分支并切换到 bug 分支之前就需要使用 git stash 将 dev 分支的未提交修改储藏起来。 +该功能可以用于 bug 分支的实现。如果当前正在 dev 分支上进行开发,但是此时 master 上有个 bug 需要修复,但是 dev 分支上的开发还未完成,不想立即提交。在新建 bug 分支并切换到 bug 分支之前就需要使用 git stash 将 dev 分支的未提交修改储藏起来。 -# SSH 传输设置 +# SSH 传输设置 -Git 仓库和 Github 中心仓库之间的传输是通过 SSH 加密。 +Git 仓库和 Github 中心仓库之间的传输是通过 SSH 加密。 -如果工作区下没有 .ssh 目录,或者该目录下没有 id_rsa 和 id_rsa.pub 这两个文件,可以通过以下命令来创建 SSH Key: +如果工作区下没有 .ssh 目录,或者该目录下没有 id_rsa 和 id_rsa.pub 这两个文件,可以通过以下命令来创建 SSH Key: ``` -$ ssh-keygen -t rsa -C "youremail@example.com" +$ ssh-keygen -t rsa -C "youremail@example.com" ``` -然后把公钥 id_rsa.pub 的内容复制到 Github "Account settings" 的 SSH Keys 中。 +然后把公钥 id_rsa.pub 的内容复制到 Github "Account settings" 的 SSH Keys 中。 -# .gitignore 文件 +# .gitignore 文件 忽略以下文件: -- 操作系统自动生成的文件,比如缩略图; -- 编译生成的中间文件,比如 Java 编译产生的 .class 文件; -- 自己的敏感信息,比如存放口令的配置文件。 +- 操作系统自动生成的文件,比如缩略图; +- 编译生成的中间文件,比如 Java 编译产生的 .class 文件; +- 自己的敏感信息,比如存放口令的配置文件。 -不需要全部自己编写,可以到 [https://github.com/github/gitignore](https://github.com/github/gitignore) 中进行查询。 +不需要全部自己编写,可以到 [https://github.com/github/gitignore](https://github.com/github/gitignore) 中进行查询。 -# Git 命令一览 +# Git 命令一览 -

+![](index_files/7a29acce-f243-4914-9f00-f2988c528412.jpg) 比较详细的地址:http://www.cheat-sheets.org/saved-copy/git-cheat-sheet.pdf -# 参考资料 +# 参考资料 -- [Git - 简明指南](http://rogerdudler.github.io/git-guide/index.zh.html) -- [图解 Git](http://marklodato.github.io/visual-git-guide/index-zh-cn.html) -- [廖雪峰 : Git 教程](https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000) -- [Learn Git Branching](https://learngitbranching.js.org/) +- [Git - 简明指南](http://rogerdudler.github.io/git-guide/index.zh.html) +- [图解 Git](http://marklodato.github.io/visual-git-guide/index-zh-cn.html) +- [廖雪峰 : Git 教程](https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000) +- [Learn Git Branching](https://learngitbranching.js.org/) diff --git a/docs/notes/HTTP.md b/docs/notes/HTTP.md index 67bc68ad..aa833d6f 100644 --- a/docs/notes/HTTP.md +++ b/docs/notes/HTTP.md @@ -1,884 +1,842 @@ -[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) - -* [一 、基础概念](#一-基础概念) - * [URI](#uri) - * [请求和响应报文](#请求和响应报文) -* [二、HTTP 方法](#二http-方法) - * [GET](#get) - * [HEAD](#head) - * [POST](#post) - * [PUT](#put) - * [PATCH](#patch) - * [DELETE](#delete) - * [OPTIONS](#options) - * [CONNECT](#connect) - * [TRACE](#trace) -* [三、HTTP 状态码](#三http-状态码) - * [1XX 信息](#1xx-信息) - * [2XX 成功](#2xx-成功) - * [3XX 重定向](#3xx-重定向) - * [4XX 客户端错误](#4xx-客户端错误) - * [5XX 服务器错误](#5xx-服务器错误) -* [四、HTTP 首部](#四http-首部) - * [通用首部字段](#通用首部字段) - * [请求首部字段](#请求首部字段) - * [响应首部字段](#响应首部字段) - * [实体首部字段](#实体首部字段) -* [五、具体应用](#五具体应用) - * [连接管理](#连接管理) - * [Cookie](#cookie) - * [缓存](#缓存) - * [内容协商](#内容协商) - * [内容编码](#内容编码) - * [范围请求](#范围请求) - * [分块传输编码](#分块传输编码) - * [多部分对象集合](#多部分对象集合) - * [虚拟主机](#虚拟主机) - * [通信数据转发](#通信数据转发) -* [六、HTTPs](#六https) - * [加密](#加密) - * [认证](#认证) - * [完整性保护](#完整性保护) - * [HTTPs 的缺点](#https-的缺点) -* [七、HTTP/2.0](#七http20) - * [HTTP/1.x 缺陷](#http1x-缺陷) - * [二进制分帧层](#二进制分帧层) - * [服务端推送](#服务端推送) - * [首部压缩](#首部压缩) -* [八、HTTP/1.1 新特性](#八http11-新特性) -* [九、GET 和 POST 比较](#九get-和-post-比较) - * [作用](#作用) - * [参数](#参数) - * [安全](#安全) - * [幂等性](#幂等性) - * [可缓存](#可缓存) - * [XMLHttpRequest](#xmlhttprequest) -* [参考资料](#参考资料) - +# 一 、基础概念 +## URI -# 一 、基础概念 +URI 包含 URL 和 URN。 -## URI +- URI(Uniform Resource Identifier,统一资源标识符) +- URL(Uniform Resource Locator,统一资源定位符) +- URN(Uniform Resource Name,统一资源名称) -URI 包含 URL 和 URN。 + -- URI(Uniform Resource Identifier,统一资源标识符) -- URL(Uniform Resource Locator,统一资源定位符) -- URN(Uniform Resource Name,统一资源名称) +## 请求和响应报文 -

+### 1. 请求报文 -## 请求和响应报文 + -### 1. 请求报文 +### 2. 响应报文 -

+ -### 2. 响应报文 +# 二、HTTP 方法 -

+客户端发送的 **请求报文** 第一行为请求行,包含了方法字段。 -# 二、HTTP 方法 +## GET -客户端发送的 **请求报文** 第一行为请求行,包含了方法字段。 +> 获取资源 -## GET +当前网络请求中,绝大部分使用的是 GET 方法。 -> 获取资源 +## HEAD -当前网络请求中,绝大部分使用的是 GET 方法。 +> 获取报文首部 -## HEAD +和 GET 方法类似,但是不返回报文实体主体部分。 -> 获取报文首部 +主要用于确认 URL 的有效性以及资源更新的日期时间等。 -和 GET 方法类似,但是不返回报文实体主体部分。 +## POST -主要用于确认 URL 的有效性以及资源更新的日期时间等。 +> 传输实体主体 -## POST +POST 主要用来传输数据,而 GET 主要用来获取资源。 -> 传输实体主体 +更多 POST 与 GET 的比较请见第九章。 -POST 主要用来传输数据,而 GET 主要用来获取资源。 +## PUT -更多 POST 与 GET 的比较请见第九章。 - -## PUT - -> 上传文件 +> 上传文件 由于自身不带验证机制,任何人都可以上传文件,因此存在安全性问题,一般不使用该方法。 ```html -PUT /new.html HTTP/1.1 -Host: example.com -Content-type: text/html -Content-length: 16 +PUT /new.html HTTP/1.1 +Host: example.com +Content-type: text/html +Content-length: 16 -

New File

+

New File

``` -## PATCH +## PATCH -> 对资源进行部分修改 +> 对资源进行部分修改 -PUT 也可以用于修改资源,但是只能完全替代原始资源,PATCH 允许部分修改。 +PUT 也可以用于修改资源,但是只能完全替代原始资源,PATCH 允许部分修改。 ```html -PATCH /file.txt HTTP/1.1 -Host: www.example.com -Content-Type: application/example -If-Match: "e0023aa4e" -Content-Length: 100 +PATCH /file.txt HTTP/1.1 +Host: www.example.com +Content-Type: application/example +If-Match: "e0023aa4e" +Content-Length: 100 -[description of changes] +[description of changes] ``` -## DELETE +## DELETE -> 删除文件 +> 删除文件 -与 PUT 功能相反,并且同样不带验证机制。 +与 PUT 功能相反,并且同样不带验证机制。 ```html -DELETE /file.html HTTP/1.1 +DELETE /file.html HTTP/1.1 ``` -## OPTIONS +## OPTIONS -> 查询支持的方法 +> 查询支持的方法 -查询指定的 URL 能够支持的方法。 +查询指定的 URL 能够支持的方法。 -会返回 `Allow: GET, POST, HEAD, OPTIONS` 这样的内容。 +会返回 `Allow: GET, POST, HEAD, OPTIONS` 这样的内容。 -## CONNECT +## CONNECT -> 要求在与代理服务器通信时建立隧道 +> 要求在与代理服务器通信时建立隧道 -使用 SSL(Secure Sockets Layer,安全套接层)和 TLS(Transport Layer Security,传输层安全)协议把通信内容加密后经网络隧道传输。 +使用 SSL(Secure Sockets Layer,安全套接层)和 TLS(Transport Layer Security,传输层安全)协议把通信内容加密后经网络隧道传输。 ```html -CONNECT www.example.com:443 HTTP/1.1 +CONNECT www.example.com:443 HTTP/1.1 ``` -

+ -## TRACE +## TRACE -> 追踪路径 +> 追踪路径 服务器会将通信路径返回给客户端。 -发送请求时,在 Max-Forwards 首部字段中填入数值,每经过一个服务器就会减 1,当数值为 0 时就停止传输。 +发送请求时,在 Max-Forwards 首部字段中填入数值,每经过一个服务器就会减 1,当数值为 0 时就停止传输。 -通常不会使用 TRACE,并且它容易受到 XST 攻击(Cross-Site Tracing,跨站追踪)。 +通常不会使用 TRACE,并且它容易受到 XST 攻击(Cross-Site Tracing,跨站追踪)。 -# 三、HTTP 状态码 +# 三、HTTP 状态码 -服务器返回的 **响应报文** 中第一行为状态行,包含了状态码以及原因短语,用来告知客户端请求的结果。 +服务器返回的 **响应报文** 中第一行为状态行,包含了状态码以及原因短语,用来告知客户端请求的结果。 -| 状态码 | 类别 | 含义 | -| :---: | :---: | :---: | -| 1XX | Informational(信息性状态码) | 接收的请求正在处理 | -| 2XX | Success(成功状态码) | 请求正常处理完毕 | -| 3XX | Redirection(重定向状态码) | 需要进行附加操作以完成请求 | -| 4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 | -| 5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 | +| 状态码 | 类别 | 含义 | +| :---: | :---: | :---: | +| 1XX | Informational(信息性状态码) | 接收的请求正在处理 | +| 2XX | Success(成功状态码) | 请求正常处理完毕 | +| 3XX | Redirection(重定向状态码) | 需要进行附加操作以完成请求 | +| 4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 | +| 5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 | -## 1XX 信息 +## 1XX 信息 -- **100 Continue** :表明到目前为止都很正常,客户端可以继续发送请求或者忽略这个响应。 +- **100 Continue**:表明到目前为止都很正常,客户端可以继续发送请求或者忽略这个响应。 -## 2XX 成功 +## 2XX 成功 -- **200 OK** +- **200 OK** -- **204 No Content** :请求已经成功处理,但是返回的响应报文不包含实体的主体部分。一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用。 +- **204 No Content**:请求已经成功处理,但是返回的响应报文不包含实体的主体部分。一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用。 -- **206 Partial Content** :表示客户端进行了范围请求,响应报文包含由 Content-Range 指定范围的实体内容。 +- **206 Partial Content**:表示客户端进行了范围请求,响应报文包含由 Content-Range 指定范围的实体内容。 -## 3XX 重定向 +## 3XX 重定向 -- **301 Moved Permanently** :永久性重定向 +- **301 Moved Permanently**:永久性重定向 -- **302 Found** :临时性重定向 +- **302 Found**:临时性重定向 -- **303 See Other** :和 302 有着相同的功能,但是 303 明确要求客户端应该采用 GET 方法获取资源。 +- **303 See Other**:和 302 有着相同的功能,但是 303 明确要求客户端应该采用 GET 方法获取资源。 -- 注:虽然 HTTP 协议规定 301、302 状态下重定向时不允许把 POST 方法改成 GET 方法,但是大多数浏览器都会在 301、302 和 303 状态下的重定向把 POST 方法改成 GET 方法。 +- 注:虽然 HTTP 协议规定 301、302 状态下重定向时不允许把 POST 方法改成 GET 方法,但是大多数浏览器都会在 301、302 和 303 状态下的重定向把 POST 方法改成 GET 方法。 -- **304 Not Modified** :如果请求报文首部包含一些条件,例如:If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,如果不满足条件,则服务器会返回 304 状态码。 +- **304 Not Modified**:如果请求报文首部包含一些条件,例如:If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,如果不满足条件,则服务器会返回 304 状态码。 -- **307 Temporary Redirect** :临时重定向,与 302 的含义类似,但是 307 要求浏览器不会把重定向请求的 POST 方法改成 GET 方法。 +- **307 Temporary Redirect**:临时重定向,与 302 的含义类似,但是 307 要求浏览器不会把重定向请求的 POST 方法改成 GET 方法。 -## 4XX 客户端错误 +## 4XX 客户端错误 -- **400 Bad Request** :请求报文中存在语法错误。 +- **400 Bad Request**:请求报文中存在语法错误。 -- **401 Unauthorized** :该状态码表示发送的请求需要有认证信息(BASIC 认证、DIGEST 认证)。如果之前已进行过一次请求,则表示用户认证失败。 +- **401 Unauthorized**:该状态码表示发送的请求需要有认证信息(BASIC 认证、DIGEST 认证)。如果之前已进行过一次请求,则表示用户认证失败。 -- **403 Forbidden** :请求被拒绝。 +- **403 Forbidden**:请求被拒绝。 -- **404 Not Found** +- **404 Not Found** -## 5XX 服务器错误 +## 5XX 服务器错误 -- **500 Internal Server Error** :服务器正在执行请求时发生错误。 +- **500 Internal Server Error**:服务器正在执行请求时发生错误。 -- **503 Service Unavailable** :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。 +- **503 Service Unavailable**:服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。 -# 四、HTTP 首部 +# 四、HTTP 首部 -有 4 种类型的首部字段:通用首部字段、请求首部字段、响应首部字段和实体首部字段。 +有 4 种类型的首部字段:通用首部字段、请求首部字段、响应首部字段和实体首部字段。 各种首部字段及其含义如下(不需要全记,仅供查阅): -## 通用首部字段 +## 通用首部字段 -| 首部字段名 | 说明 | -| :--: | :--: | -| Cache-Control | 控制缓存的行为 | -| Connection | 控制不再转发给代理的首部字段、管理持久连接| -| Date | 创建报文的日期时间 | -| Pragma | 报文指令 | -| Trailer | 报文末端的首部一览 | -| Transfer-Encoding | 指定报文主体的传输编码方式 | -| Upgrade | 升级为其他协议 | -| Via | 代理服务器的相关信息 | -| Warning | 错误通知 | +| 首部字段名 | 说明 | +| :--: | :--: | +| Cache-Control | 控制缓存的行为 | +| Connection | 控制不再转发给代理的首部字段、管理持久连接| +| Date | 创建报文的日期时间 | +| Pragma | 报文指令 | +| Trailer | 报文末端的首部一览 | +| Transfer-Encoding | 指定报文主体的传输编码方式 | +| Upgrade | 升级为其他协议 | +| Via | 代理服务器的相关信息 | +| Warning | 错误通知 | -## 请求首部字段 +## 请求首部字段 -| 首部字段名 | 说明 | -| :--: | :--: | -| Accept | 用户代理可处理的媒体类型 | -| Accept-Charset | 优先的字符集 | -| Accept-Encoding | 优先的内容编码 | -| Accept-Language | 优先的语言(自然语言) | -| Authorization | Web 认证信息 | -| Expect | 期待服务器的特定行为 | -| From | 用户的电子邮箱地址 | -| Host | 请求资源所在服务器 | -| If-Match | 比较实体标记(ETag) | -| If-Modified-Since | 比较资源的更新时间 | -| If-None-Match | 比较实体标记(与 If-Match 相反) | -| If-Range | 资源未更新时发送实体 Byte 的范围请求 | -| If-Unmodified-Since | 比较资源的更新时间(与 If-Modified-Since 相反) | -| Max-Forwards | 最大传输逐跳数 | -| Proxy-Authorization | 代理服务器要求客户端的认证信息 | -| Range | 实体的字节范围请求 | -| Referer | 对请求中 URI 的原始获取方 | -| TE | 传输编码的优先级 | -| User-Agent | HTTP 客户端程序的信息 | +| 首部字段名 | 说明 | +| :--: | :--: | +| Accept | 用户代理可处理的媒体类型 | +| Accept-Charset | 优先的字符集 | +| Accept-Encoding | 优先的内容编码 | +| Accept-Language | 优先的语言(自然语言) | +| Authorization | Web 认证信息 | +| Expect | 期待服务器的特定行为 | +| From | 用户的电子邮箱地址 | +| Host | 请求资源所在服务器 | +| If-Match | 比较实体标记(ETag) | +| If-Modified-Since | 比较资源的更新时间 | +| If-None-Match | 比较实体标记(与 If-Match 相反) | +| If-Range | 资源未更新时发送实体 Byte 的范围请求 | +| If-Unmodified-Since | 比较资源的更新时间(与 If-Modified-Since 相反) | +| Max-Forwards | 最大传输逐跳数 | +| Proxy-Authorization | 代理服务器要求客户端的认证信息 | +| Range | 实体的字节范围请求 | +| Referer | 对请求中 URI 的原始获取方 | +| TE | 传输编码的优先级 | +| User-Agent | HTTP 客户端程序的信息 | -## 响应首部字段 +## 响应首部字段 -| 首部字段名 | 说明 | -| :--: | :--: | -| Accept-Ranges | 是否接受字节范围请求 | -| Age | 推算资源创建经过时间 | -| ETag | 资源的匹配信息 | -| Location | 令客户端重定向至指定 URI | -| Proxy-Authenticate | 代理服务器对客户端的认证信息 | -| Retry-After | 对再次发起请求的时机要求 | -| Server | HTTP 服务器的安装信息 | -| Vary | 代理服务器缓存的管理信息 | -| WWW-Authenticate | 服务器对客户端的认证信息 | +| 首部字段名 | 说明 | +| :--: | :--: | +| Accept-Ranges | 是否接受字节范围请求 | +| Age | 推算资源创建经过时间 | +| ETag | 资源的匹配信息 | +| Location | 令客户端重定向至指定 URI | +| Proxy-Authenticate | 代理服务器对客户端的认证信息 | +| Retry-After | 对再次发起请求的时机要求 | +| Server | HTTP 服务器的安装信息 | +| Vary | 代理服务器缓存的管理信息 | +| WWW-Authenticate | 服务器对客户端的认证信息 | -## 实体首部字段 +## 实体首部字段 -| 首部字段名 | 说明 | -| :--: | :--: | -| Allow | 资源可支持的 HTTP 方法 | -| Content-Encoding | 实体主体适用的编码方式 | -| Content-Language | 实体主体的自然语言 | -| Content-Length | 实体主体的大小 | -| Content-Location | 替代对应资源的 URI | -| Content-MD5 | 实体主体的报文摘要 | -| Content-Range | 实体主体的位置范围 | -| Content-Type | 实体主体的媒体类型 | -| Expires | 实体主体过期的日期时间 | -| Last-Modified | 资源的最后修改日期时间 | +| 首部字段名 | 说明 | +| :--: | :--: | +| Allow | 资源可支持的 HTTP 方法 | +| Content-Encoding | 实体主体适用的编码方式 | +| Content-Language | 实体主体的自然语言 | +| Content-Length | 实体主体的大小 | +| Content-Location | 替代对应资源的 URI | +| Content-MD5 | 实体主体的报文摘要 | +| Content-Range | 实体主体的位置范围 | +| Content-Type | 实体主体的媒体类型 | +| Expires | 实体主体过期的日期时间 | +| Last-Modified | 资源的最后修改日期时间 | -# 五、具体应用 +# 五、具体应用 -## 连接管理 +## 连接管理 -

+ -### 1. 短连接与长连接 +### 1. 短连接与长连接 -当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问 HTML 页面资源,还会请求图片资源。如果每进行一次 HTTP 通信就要新建一个 TCP 连接,那么开销会很大。 +当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问 HTML 页面资源,还会请求图片资源。如果每进行一次 HTTP 通信就要新建一个 TCP 连接,那么开销会很大。 -长连接只需要建立一次 TCP 连接就能进行多次 HTTP 通信。 +长连接只需要建立一次 TCP 连接就能进行多次 HTTP 通信。 -- 从 HTTP/1.1 开始默认是长连接的,如果要断开连接,需要由客户端或者服务器端提出断开,使用 `Connection : close`; -- 在 HTTP/1.1 之前默认是短连接的,如果需要使用长连接,则使用 `Connection : Keep-Alive`。 +- 从 HTTP/1.1 开始默认是长连接的,如果要断开连接,需要由客户端或者服务器端提出断开,使用 `Connection : close`; +- 在 HTTP/1.1 之前默认是短连接的,如果需要使用长连接,则使用 `Connection : Keep-Alive`。 -### 2. 流水线 +### 2. 流水线 -默认情况下,HTTP 请求是按顺序发出的,下一个请求只有在当前请求收到响应之后才会被发出。由于会受到网络延迟和带宽的限制,在下一个请求被发送到服务器之前,可能需要等待很长时间。 +默认情况下,HTTP 请求是按顺序发出的,下一个请求只有在当前请求收到响应之后才会被发出。由于会受到网络延迟和带宽的限制,在下一个请求被发送到服务器之前,可能需要等待很长时间。 流水线是在同一条长连接上发出连续的请求,而不用等待响应返回,这样可以避免连接延迟。 -## Cookie +## Cookie -HTTP 协议是无状态的,主要是为了让 HTTP 协议尽可能简单,使得它能够处理大量事务。HTTP/1.1 引入 Cookie 来保存状态信息。 +HTTP 协议是无状态的,主要是为了让 HTTP 协议尽可能简单,使得它能够处理大量事务。HTTP/1.1 引入 Cookie 来保存状态信息。 -Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器之后向同一服务器再次发起请求时被携带上,用于告知服务端两个请求是否来自同一浏览器。由于之后每次请求都会需要携带 Cookie 数据,因此会带来额外的性能开销(尤其是在移动环境下)。 +Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器之后向同一服务器再次发起请求时被携带上,用于告知服务端两个请求是否来自同一浏览器。由于之后每次请求都会需要携带 Cookie 数据,因此会带来额外的性能开销(尤其是在移动环境下)。 -Cookie 曾一度用于客户端数据的存储,因为当时并没有其它合适的存储办法而作为唯一的存储手段,但现在随着现代浏览器开始支持各种各样的存储方式,Cookie 渐渐被淘汰。新的浏览器 API 已经允许开发者直接将数据存储到本地,如使用 Web storage API(本地存储和会话存储)或 IndexedDB。 +Cookie 曾一度用于客户端数据的存储,因为当时并没有其它合适的存储办法而作为唯一的存储手段,但现在随着现代浏览器开始支持各种各样的存储方式,Cookie 渐渐被淘汰。新的浏览器 API 已经允许开发者直接将数据存储到本地,如使用 Web storage API(本地存储和会话存储)或 IndexedDB。 -### 1. 用途 +### 1. 用途 -- 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息) -- 个性化设置(如用户自定义设置、主题等) -- 浏览器行为跟踪(如跟踪分析用户行为等) +- 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息) +- 个性化设置(如用户自定义设置、主题等) +- 浏览器行为跟踪(如跟踪分析用户行为等) -### 2. 创建过程 +### 2. 创建过程 -服务器发送的响应报文包含 Set-Cookie 首部字段,客户端得到响应报文后把 Cookie 内容保存到浏览器中。 +服务器发送的响应报文包含 Set-Cookie 首部字段,客户端得到响应报文后把 Cookie 内容保存到浏览器中。 ```html -HTTP/1.0 200 OK -Content-type: text/html -Set-Cookie: yummy_cookie=choco -Set-Cookie: tasty_cookie=strawberry +HTTP/1.0 200 OK +Content-type: text/html +Set-Cookie: yummy_cookie=choco +Set-Cookie: tasty_cookie=strawberry -[page content] +[page content] ``` -客户端之后对同一个服务器发送请求时,会从浏览器中取出 Cookie 信息并通过 Cookie 请求首部字段发送给服务器。 +客户端之后对同一个服务器发送请求时,会从浏览器中取出 Cookie 信息并通过 Cookie 请求首部字段发送给服务器。 ```html -GET /sample_page.html HTTP/1.1 -Host: www.example.org -Cookie: yummy_cookie=choco; tasty_cookie=strawberry +GET /sample_page.html HTTP/1.1 +Host: www.example.org +Cookie: yummy_cookie=choco; tasty_cookie=strawberry ``` -### 3. 分类 +### 3. 分类 -- 会话期 Cookie:浏览器关闭之后它会被自动删除,也就是说它仅在会话期内有效。 -- 持久性 Cookie:指定一个特定的过期时间(Expires)或有效期(max-age)之后就成为了持久性的 Cookie。 +- 会话期 Cookie:浏览器关闭之后它会被自动删除,也就是说它仅在会话期内有效。 +- 持久性 Cookie:指定一个特定的过期时间(Expires)或有效期(max-age)之后就成为了持久性的 Cookie。 ```html -Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; +Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; ``` -### 4. 作用域 +### 4. 作用域 -Domain 标识指定了哪些主机可以接受 Cookie。如果不指定,默认为当前文档的主机(不包含子域名)。如果指定了 Domain,则一般包含子域名。例如,如果设置 Domain=mozilla.org,则 Cookie 也包含在子域名中(如 developer.mozilla.org)。 +Domain 标识指定了哪些主机可以接受 Cookie。如果不指定,默认为当前文档的主机(不包含子域名)。如果指定了 Domain,则一般包含子域名。例如,如果设置 Domain=mozilla.org,则 Cookie 也包含在子域名中(如 developer.mozilla.org)。 -Path 标识指定了主机下的哪些路径可以接受 Cookie(该 URL 路径必须存在于请求 URL 中)。以字符 %x2F ("/") 作为路径分隔符,子路径也会被匹配。例如,设置 Path=/docs,则以下地址都会匹配: +Path 标识指定了主机下的哪些路径可以接受 Cookie(该 URL 路径必须存在于请求 URL 中)。以字符 %x2F ("/") 作为路径分隔符,子路径也会被匹配。例如,设置 Path=/docs,则以下地址都会匹配: -- /docs -- /docs/Web/ -- /docs/Web/HTTP +- /docs +- /docs/Web/ +- /docs/Web/HTTP -### 5. JavaScript +### 5. JavaScript -通过 `document.cookie` 属性可创建新的 Cookie,也可通过该属性访问非 HttpOnly 标记的 Cookie。 +通过 `document.cookie` 属性可创建新的 Cookie,也可通过该属性访问非 HttpOnly 标记的 Cookie。 ```html -document.cookie = "yummy_cookie=choco"; -document.cookie = "tasty_cookie=strawberry"; +document.cookie = "yummy_cookie=choco"; +document.cookie = "tasty_cookie=strawberry"; console.log(document.cookie); ``` -### 6. HttpOnly +### 6. HttpOnly -标记为 HttpOnly 的 Cookie 不能被 JavaScript 脚本调用。跨站脚本攻击 (XSS) 常常使用 JavaScript 的 `document.cookie` API 窃取用户的 Cookie 信息,因此使用 HttpOnly 标记可以在一定程度上避免 XSS 攻击。 +标记为 HttpOnly 的 Cookie 不能被 JavaScript 脚本调用。跨站脚本攻击 (XSS) 常常使用 JavaScript 的 `document.cookie` API 窃取用户的 Cookie 信息,因此使用 HttpOnly 标记可以在一定程度上避免 XSS 攻击。 ```html -Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly +Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly ``` -### 7. Secure +### 7. Secure -标记为 Secure 的 Cookie 只能通过被 HTTPS 协议加密过的请求发送给服务端。但即便设置了 Secure 标记,敏感信息也不应该通过 Cookie 传输,因为 Cookie 有其固有的不安全性,Secure 标记也无法提供确实的安全保障。 +标记为 Secure 的 Cookie 只能通过被 HTTPS 协议加密过的请求发送给服务端。但即便设置了 Secure 标记,敏感信息也不应该通过 Cookie 传输,因为 Cookie 有其固有的不安全性,Secure 标记也无法提供确实的安全保障。 -### 8. Session +### 8. Session -除了可以将用户信息通过 Cookie 存储在用户浏览器中,也可以利用 Session 存储在服务器端,存储在服务器端的信息更加安全。 +除了可以将用户信息通过 Cookie 存储在用户浏览器中,也可以利用 Session 存储在服务器端,存储在服务器端的信息更加安全。 -Session 可以存储在服务器上的文件、数据库或者内存中。也可以将 Session 存储在 Redis 这种内存型数据库中,效率会更高。 +Session 可以存储在服务器上的文件、数据库或者内存中。也可以将 Session 存储在 Redis 这种内存型数据库中,效率会更高。 -使用 Session 维护用户登录状态的过程如下: +使用 Session 维护用户登录状态的过程如下: -- 用户进行登录时,用户提交包含用户名和密码的表单,放入 HTTP 请求报文中; -- 服务器验证该用户名和密码,如果正确则把用户信息存储到 Redis 中,它在 Redis 中的 Key 称为 Session ID; -- 服务器返回的响应报文的 Set-Cookie 首部字段包含了这个 Session ID,客户端收到响应报文之后将该 Cookie 值存入浏览器中; -- 客户端之后对同一个服务器进行请求时会包含该 Cookie 值,服务器收到之后提取出 Session ID,从 Redis 中取出用户信息,继续之前的业务操作。 +- 用户进行登录时,用户提交包含用户名和密码的表单,放入 HTTP 请求报文中; +- 服务器验证该用户名和密码,如果正确则把用户信息存储到 Redis 中,它在 Redis 中的 Key 称为 Session ID; +- 服务器返回的响应报文的 Set-Cookie 首部字段包含了这个 Session ID,客户端收到响应报文之后将该 Cookie 值存入浏览器中; +- 客户端之后对同一个服务器进行请求时会包含该 Cookie 值,服务器收到之后提取出 Session ID,从 Redis 中取出用户信息,继续之前的业务操作。 -应该注意 Session ID 的安全性问题,不能让它被恶意攻击者轻易获取,那么就不能产生一个容易被猜到的 Session ID 值。此外,还需要经常重新生成 Session ID。在对安全性要求极高的场景下,例如转账等操作,除了使用 Session 管理用户状态之外,还需要对用户进行重新验证,比如重新输入密码,或者使用短信验证码等方式。 +应该注意 Session ID 的安全性问题,不能让它被恶意攻击者轻易获取,那么就不能产生一个容易被猜到的 Session ID 值。此外,还需要经常重新生成 Session ID。在对安全性要求极高的场景下,例如转账等操作,除了使用 Session 管理用户状态之外,还需要对用户进行重新验证,比如重新输入密码,或者使用短信验证码等方式。 -### 9. 浏览器禁用 Cookie +### 9. 浏览器禁用 Cookie -此时无法使用 Cookie 来保存用户信息,只能使用 Session。除此之外,不能再将 Session ID 存放到 Cookie 中,而是使用 URL 重写技术,将 Session ID 作为 URL 的参数进行传递。 +此时无法使用 Cookie 来保存用户信息,只能使用 Session。除此之外,不能再将 Session ID 存放到 Cookie 中,而是使用 URL 重写技术,将 Session ID 作为 URL 的参数进行传递。 -### 10. Cookie 与 Session 选择 +### 10. Cookie 与 Session 选择 -- Cookie 只能存储 ASCII 码字符串,而 Session 则可以存取任何类型的数据,因此在考虑数据复杂性时首选 Session; -- Cookie 存储在浏览器中,容易被恶意查看。如果非要将一些隐私数据存在 Cookie 中,可以将 Cookie 值进行加密,然后在服务器进行解密; -- 对于大型网站,如果用户所有的信息都存储在 Session 中,那么开销是非常大的,因此不建议将所有的用户信息都存储到 Session 中。 +- Cookie 只能存储 ASCII 码字符串,而 Session 则可以存取任何类型的数据,因此在考虑数据复杂性时首选 Session; +- Cookie 存储在浏览器中,容易被恶意查看。如果非要将一些隐私数据存在 Cookie 中,可以将 Cookie 值进行加密,然后在服务器进行解密; +- 对于大型网站,如果用户所有的信息都存储在 Session 中,那么开销是非常大的,因此不建议将所有的用户信息都存储到 Session 中。 -## 缓存 +## 缓存 -### 1. 优点 +### 1. 优点 -- 缓解服务器压力; -- 降低客户端获取资源的延迟:缓存通常位于内存中,读取缓存的速度更快。并且缓存在地理位置上也有可能比源服务器来得近,例如浏览器缓存。 +- 缓解服务器压力; +- 降低客户端获取资源的延迟:缓存通常位于内存中,读取缓存的速度更快。并且缓存在地理位置上也有可能比源服务器来得近,例如浏览器缓存。 -### 2. 实现方法 +### 2. 实现方法 -- 让代理服务器进行缓存; -- 让客户端浏览器进行缓存。 +- 让代理服务器进行缓存; +- 让客户端浏览器进行缓存。 -### 3. Cache-Control +### 3. Cache-Control -HTTP/1.1 通过 Cache-Control 首部字段来控制缓存。 +HTTP/1.1 通过 Cache-Control 首部字段来控制缓存。 -**3.1 禁止进行缓存** +**3.1 禁止进行缓存** -no-store 指令规定不能对请求或响应的任何一部分进行缓存。 +no-store 指令规定不能对请求或响应的任何一部分进行缓存。 ```html -Cache-Control: no-store +Cache-Control: no-store ``` -**3.2 强制确认缓存** +**3.2 强制确认缓存** -no-cache 指令规定缓存服务器需要先向源服务器验证缓存资源的有效性,只有当缓存资源有效才将能使用该缓存对客户端的请求进行响应。 +no-cache 指令规定缓存服务器需要先向源服务器验证缓存资源的有效性,只有当缓存资源有效才将能使用该缓存对客户端的请求进行响应。 ```html -Cache-Control: no-cache +Cache-Control: no-cache ``` -**3.3 私有缓存和公共缓存** +**3.3 私有缓存和公共缓存** -private 指令规定了将资源作为私有缓存,只能被单独用户所使用,一般存储在用户浏览器中。 +private 指令规定了将资源作为私有缓存,只能被单独用户所使用,一般存储在用户浏览器中。 ```html -Cache-Control: private +Cache-Control: private ``` -public 指令规定了将资源作为公共缓存,可以被多个用户所使用,一般存储在代理服务器中。 +public 指令规定了将资源作为公共缓存,可以被多个用户所使用,一般存储在代理服务器中。 ```html -Cache-Control: public +Cache-Control: public ``` -**3.4 缓存过期机制** +**3.4 缓存过期机制** -max-age 指令出现在请求报文中,并且缓存资源的缓存时间小于该指令指定的时间,那么就能接受该缓存。 +max-age 指令出现在请求报文中,并且缓存资源的缓存时间小于该指令指定的时间,那么就能接受该缓存。 -max-age 指令出现在响应报文中,表示缓存资源在缓存服务器中保存的时间。 +max-age 指令出现在响应报文中,表示缓存资源在缓存服务器中保存的时间。 ```html -Cache-Control: max-age=31536000 +Cache-Control: max-age=31536000 ``` -Expires 首部字段也可以用于告知缓存服务器该资源什么时候会过期。 +Expires 首部字段也可以用于告知缓存服务器该资源什么时候会过期。 ```html -Expires: Wed, 04 Jul 2012 08:26:05 GMT +Expires: Wed, 04 Jul 2012 08:26:05 GMT ``` -- 在 HTTP/1.1 中,会优先处理 max-age 指令; -- 在 HTTP/1.0 中,max-age 指令会被忽略掉。 +- 在 HTTP/1.1 中,会优先处理 max-age 指令; +- 在 HTTP/1.0 中,max-age 指令会被忽略掉。 -### 4. 缓存验证 +### 4. 缓存验证 -需要先了解 ETag 首部字段的含义,它是资源的唯一标识。URL 不能唯一表示资源,例如 `http://www.google.com/` 有中文和英文两个资源,只有 ETag 才能对这两个资源进行唯一标识。 +需要先了解 ETag 首部字段的含义,它是资源的唯一标识。URL 不能唯一表示资源,例如 `http://www.google.com/` 有中文和英文两个资源,只有 ETag 才能对这两个资源进行唯一标识。 ```html -ETag: "82e22293907ce725faf67773957acd12" +ETag: "82e22293907ce725faf67773957acd12" ``` -可以将缓存资源的 ETag 值放入 If-None-Match 首部,服务器收到该请求后,判断缓存资源的 ETag 值和资源的最新 ETag 值是否一致,如果一致则表示缓存资源有效,返回 304 Not Modified。 +可以将缓存资源的 ETag 值放入 If-None-Match 首部,服务器收到该请求后,判断缓存资源的 ETag 值和资源的最新 ETag 值是否一致,如果一致则表示缓存资源有效,返回 304 Not Modified。 ```html -If-None-Match: "82e22293907ce725faf67773957acd12" +If-None-Match: "82e22293907ce725faf67773957acd12" ``` -Last-Modified 首部字段也可以用于缓存验证,它包含在源服务器发送的响应报文中,指示源服务器对资源的最后修改时间。但是它是一种弱校验器,因为只能精确到一秒,所以它通常作为 ETag 的备用方案。如果响应首部字段里含有这个信息,客户端可以在后续的请求中带上 If-Modified-Since 来验证缓存。服务器只在所请求的资源在给定的日期时间之后对内容进行过修改的情况下才会将资源返回,状态码为 200 OK。如果请求的资源从那时起未经修改,那么返回一个不带有消息主体的 304 Not Modified 响应。 +Last-Modified 首部字段也可以用于缓存验证,它包含在源服务器发送的响应报文中,指示源服务器对资源的最后修改时间。但是它是一种弱校验器,因为只能精确到一秒,所以它通常作为 ETag 的备用方案。如果响应首部字段里含有这个信息,客户端可以在后续的请求中带上 If-Modified-Since 来验证缓存。服务器只在所请求的资源在给定的日期时间之后对内容进行过修改的情况下才会将资源返回,状态码为 200 OK。如果请求的资源从那时起未经修改,那么返回一个不带有消息主体的 304 Not Modified 响应。 ```html -Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT +Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT ``` ```html -If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT +If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT ``` -## 内容协商 +## 内容协商 通过内容协商返回最合适的内容,例如根据浏览器的默认语言选择返回中文界面还是英文界面。 -### 1. 类型 +### 1. 类型 -**1.1 服务端驱动型** +**1.1 服务端驱动型** -客户端设置特定的 HTTP 首部字段,例如 Accept、Accept-Charset、Accept-Encoding、Accept-Language,服务器根据这些字段返回特定的资源。 +客户端设置特定的 HTTP 首部字段,例如 Accept、Accept-Charset、Accept-Encoding、Accept-Language,服务器根据这些字段返回特定的资源。 它存在以下问题: -- 服务器很难知道客户端浏览器的全部信息; -- 客户端提供的信息相当冗长(HTTP/2 协议的首部压缩机制缓解了这个问题),并且存在隐私风险(HTTP 指纹识别技术); -- 给定的资源需要返回不同的展现形式,共享缓存的效率会降低,而服务器端的实现会越来越复杂。 +- 服务器很难知道客户端浏览器的全部信息; +- 客户端提供的信息相当冗长(HTTP/2 协议的首部压缩机制缓解了这个问题),并且存在隐私风险(HTTP 指纹识别技术); +- 给定的资源需要返回不同的展现形式,共享缓存的效率会降低,而服务器端的实现会越来越复杂。 -**1.2 代理驱动型** +**1.2 代理驱动型** -服务器返回 300 Multiple Choices 或者 406 Not Acceptable,客户端从中选出最合适的那个资源。 +服务器返回 300 Multiple Choices 或者 406 Not Acceptable,客户端从中选出最合适的那个资源。 -### 2. Vary +### 2. Vary ```html -Vary: Accept-Language +Vary: Accept-Language ``` 在使用内容协商的情况下,只有当缓存服务器中的缓存满足内容协商条件时,才能使用该缓存,否则应该向源服务器请求该资源。 -例如,一个客户端发送了一个包含 Accept-Language 首部字段的请求之后,源服务器返回的响应包含 `Vary: Accept-Language` 内容,缓存服务器对这个响应进行缓存之后,在客户端下一次访问同一个 URL 资源,并且 Accept-Language 与缓存中的对应的值相同时才会返回该缓存。 +例如,一个客户端发送了一个包含 Accept-Language 首部字段的请求之后,源服务器返回的响应包含 `Vary: Accept-Language` 内容,缓存服务器对这个响应进行缓存之后,在客户端下一次访问同一个 URL 资源,并且 Accept-Language 与缓存中的对应的值相同时才会返回该缓存。 -## 内容编码 +## 内容编码 内容编码将实体主体进行压缩,从而减少传输的数据量。 常用的内容编码有:gzip、compress、deflate、identity。 -浏览器发送 Accept-Encoding 首部,其中包含有它所支持的压缩算法,以及各自的优先级。服务器则从中选择一种,使用该算法对响应的消息主体进行压缩,并且发送 Content-Encoding 首部来告知浏览器它选择了哪一种算法。由于该内容协商过程是基于编码类型来选择资源的展现形式的,在响应的 Vary 首部至少要包含 Content-Encoding。 +浏览器发送 Accept-Encoding 首部,其中包含有它所支持的压缩算法,以及各自的优先级。服务器则从中选择一种,使用该算法对响应的消息主体进行压缩,并且发送 Content-Encoding 首部来告知浏览器它选择了哪一种算法。由于该内容协商过程是基于编码类型来选择资源的展现形式的,在响应的 Vary 首部至少要包含 Content-Encoding。 -## 范围请求 +## 范围请求 如果网络出现中断,服务器只发送了一部分数据,范围请求可以使得客户端只请求服务器未发送的那部分数据,从而避免服务器重新发送所有数据。 -### 1. Range +### 1. Range -在请求报文中添加 Range 首部字段指定请求的范围。 +在请求报文中添加 Range 首部字段指定请求的范围。 ```html -GET /z4d4kWk.jpg HTTP/1.1 -Host: i.imgur.com -Range: bytes=0-1023 +GET /z4d4kWk.jpg HTTP/1.1 +Host: i.imgur.com +Range: bytes=0-1023 ``` -请求成功的话服务器返回的响应包含 206 Partial Content 状态码。 +请求成功的话服务器返回的响应包含 206 Partial Content 状态码。 ```html -HTTP/1.1 206 Partial Content -Content-Range: bytes 0-1023/146515 -Content-Length: 1024 +HTTP/1.1 206 Partial Content +Content-Range: bytes 0-1023/146515 +Content-Length: 1024 ... -(binary content) +(binary content) ``` -### 2. Accept-Ranges +### 2. Accept-Ranges -响应首部字段 Accept-Ranges 用于告知客户端是否能处理范围请求,可以处理使用 bytes,否则使用 none。 +响应首部字段 Accept-Ranges 用于告知客户端是否能处理范围请求,可以处理使用 bytes,否则使用 none。 ```html -Accept-Ranges: bytes +Accept-Ranges: bytes ``` -### 3. 响应状态码 +### 3. 响应状态码 -- 在请求成功的情况下,服务器会返回 206 Partial Content 状态码。 -- 在请求的范围越界的情况下,服务器会返回 416 Requested Range Not Satisfiable 状态码。 -- 在不支持范围请求的情况下,服务器会返回 200 OK 状态码。 +- 在请求成功的情况下,服务器会返回 206 Partial Content 状态码。 +- 在请求的范围越界的情况下,服务器会返回 416 Requested Range Not Satisfiable 状态码。 +- 在不支持范围请求的情况下,服务器会返回 200 OK 状态码。 -## 分块传输编码 +## 分块传输编码 -Chunked Transfer Coding,可以把数据分割成多块,让浏览器逐步显示页面。 +Chunked Transfer Coding,可以把数据分割成多块,让浏览器逐步显示页面。 -## 多部分对象集合 +## 多部分对象集合 -一份报文主体内可含有多种类型的实体同时发送,每个部分之间用 boundary 字段定义的分隔符进行分隔,每个部分都可以有首部字段。 +一份报文主体内可含有多种类型的实体同时发送,每个部分之间用 boundary 字段定义的分隔符进行分隔,每个部分都可以有首部字段。 例如,上传多个表单时可以使用如下方式: ```html -Content-Type: multipart/form-data; boundary=AaB03x +Content-Type: multipart/form-data; boundary=AaB03x --AaB03x -Content-Disposition: form-data; name="submit-name" +Content-Disposition: form-data; name="submit-name" Larry --AaB03x -Content-Disposition: form-data; name="files"; filename="file1.txt" -Content-Type: text/plain +Content-Disposition: form-data; name="files"; filename="file1.txt" +Content-Type: text/plain -... contents of file1.txt ... +... contents of file1.txt ... --AaB03x-- ``` -## 虚拟主机 +## 虚拟主机 -HTTP/1.1 使用虚拟主机技术,使得一台服务器拥有多个域名,并且在逻辑上可以看成多个服务器。 +HTTP/1.1 使用虚拟主机技术,使得一台服务器拥有多个域名,并且在逻辑上可以看成多个服务器。 -## 通信数据转发 +## 通信数据转发 -### 1. 代理 +### 1. 代理 代理服务器接受客户端的请求,并且转发给其它服务器。 使用代理的主要目的是: -- 缓存 -- 负载均衡 -- 网络访问控制 -- 访问日志记录 +- 缓存 +- 负载均衡 +- 网络访问控制 +- 访问日志记录 代理服务器分为正向代理和反向代理两种: -- 用户察觉得到正向代理的存在。 +- 用户察觉得到正向代理的存在。 -

+ -- 而反向代理一般位于内部网络中,用户察觉不到。 +- 而反向代理一般位于内部网络中,用户察觉不到。 -

+ -### 2. 网关 +### 2. 网关 -与代理服务器不同的是,网关服务器会将 HTTP 转化为其它协议进行通信,从而请求其它非 HTTP 服务器的服务。 +与代理服务器不同的是,网关服务器会将 HTTP 转化为其它协议进行通信,从而请求其它非 HTTP 服务器的服务。 -### 3. 隧道 +### 3. 隧道 -使用 SSL 等加密手段,在客户端和服务器之间建立一条安全的通信线路。 +使用 SSL 等加密手段,在客户端和服务器之间建立一条安全的通信线路。 -# 六、HTTPs +# 六、HTTPs -HTTP 有以下安全性问题: +HTTP 有以下安全性问题: -- 使用明文进行通信,内容可能会被窃听; -- 不验证通信方的身份,通信方的身份有可能遭遇伪装; -- 无法证明报文的完整性,报文有可能遭篡改。 +- 使用明文进行通信,内容可能会被窃听; +- 不验证通信方的身份,通信方的身份有可能遭遇伪装; +- 无法证明报文的完整性,报文有可能遭篡改。 -HTTPs 并不是新协议,而是让 HTTP 先和 SSL(Secure Sockets Layer)通信,再由 SSL 和 TCP 通信,也就是说 HTTPs 使用了隧道进行通信。 +HTTPs 并不是新协议,而是让 HTTP 先和 SSL(Secure Sockets Layer)通信,再由 SSL 和 TCP 通信,也就是说 HTTPs 使用了隧道进行通信。 -通过使用 SSL,HTTPs 具有了加密(防窃听)、认证(防伪装)和完整性保护(防篡改)。 +通过使用 SSL,HTTPs 具有了加密(防窃听)、认证(防伪装)和完整性保护(防篡改)。 -

+ -## 加密 +## 加密 -### 1. 对称密钥加密 +### 1. 对称密钥加密 -对称密钥加密(Symmetric-Key Encryption),加密和解密使用同一密钥。 +对称密钥加密(Symmetric-Key Encryption),加密和解密使用同一密钥。 -- 优点:运算速度快; -- 缺点:无法安全地将密钥传输给通信方。 +- 优点:运算速度快; +- 缺点:无法安全地将密钥传输给通信方。 -

+ -### 2.非对称密钥加密 +### 2.非对称密钥加密 -非对称密钥加密,又称公开密钥加密(Public-Key Encryption),加密和解密使用不同的密钥。 +非对称密钥加密,又称公开密钥加密(Public-Key Encryption),加密和解密使用不同的密钥。 公开密钥所有人都可以获得,通信发送方获得接收方的公开密钥之后,就可以使用公开密钥进行加密,接收方收到通信内容后使用私有密钥解密。 非对称密钥除了用来加密,还可以用来进行签名。因为私有密钥无法被其他人获取,因此通信发送方使用其私有密钥进行签名,通信接收方使用发送方的公开密钥对签名进行解密,就能判断这个签名是否正确。 -- 优点:可以更安全地将公开密钥传输给通信发送方; -- 缺点:运算速度慢。 +- 优点:可以更安全地将公开密钥传输给通信发送方; +- 缺点:运算速度慢。 -

+ -### 3. HTTPs 采用的加密方式 +### 3. HTTPs 采用的加密方式 -HTTPs 采用混合的加密机制,使用非对称密钥加密用于传输对称密钥来保证传输过程的安全性,之后使用对称密钥加密进行通信来保证通信过程的效率。(下图中的 Session Key 就是对称密钥) +HTTPs 采用混合的加密机制,使用非对称密钥加密用于传输对称密钥来保证传输过程的安全性,之后使用对称密钥加密进行通信来保证通信过程的效率。(下图中的 Session Key 就是对称密钥) -

+ -## 认证 +## 认证 -通过使用 **证书** 来对通信方进行认证。 +通过使用 **证书** 来对通信方进行认证。 -数字证书认证机构(CA,Certificate Authority)是客户端与服务器双方都可信赖的第三方机构。 +数字证书认证机构(CA,Certificate Authority)是客户端与服务器双方都可信赖的第三方机构。 -服务器的运营人员向 CA 提出公开密钥的申请,CA 在判明提出申请者的身份之后,会对已申请的公开密钥做数字签名,然后分配这个已签名的公开密钥,并将该公开密钥放入公开密钥证书后绑定在一起。 +服务器的运营人员向 CA 提出公开密钥的申请,CA 在判明提出申请者的身份之后,会对已申请的公开密钥做数字签名,然后分配这个已签名的公开密钥,并将该公开密钥放入公开密钥证书后绑定在一起。 -进行 HTTPs 通信时,服务器会把证书发送给客户端。客户端取得其中的公开密钥之后,先使用数字签名进行验证,如果验证通过,就可以开始通信了。 +进行 HTTPs 通信时,服务器会把证书发送给客户端。客户端取得其中的公开密钥之后,先使用数字签名进行验证,如果验证通过,就可以开始通信了。 -

+ -## 完整性保护 +## 完整性保护 -SSL 提供报文摘要功能来进行完整性保护。 +SSL 提供报文摘要功能来进行完整性保护。 -HTTP 也提供了 MD5 报文摘要功能,但不是安全的。例如报文内容被篡改之后,同时重新计算 MD5 的值,通信接收方是无法意识到发生了篡改。 +HTTP 也提供了 MD5 报文摘要功能,但不是安全的。例如报文内容被篡改之后,同时重新计算 MD5 的值,通信接收方是无法意识到发生了篡改。 -HTTPs 的报文摘要功能之所以安全,是因为它结合了加密和认证这两个操作。试想一下,加密之后的报文,遭到篡改之后,也很难重新计算报文摘要,因为无法轻易获取明文。 +HTTPs 的报文摘要功能之所以安全,是因为它结合了加密和认证这两个操作。试想一下,加密之后的报文,遭到篡改之后,也很难重新计算报文摘要,因为无法轻易获取明文。 -## HTTPs 的缺点 +## HTTPs 的缺点 -- 因为需要进行加密解密等过程,因此速度会更慢; -- 需要支付证书授权的高额费用。 +- 因为需要进行加密解密等过程,因此速度会更慢; +- 需要支付证书授权的高额费用。 -# 七、HTTP/2.0 +# 七、HTTP/2.0 -## HTTP/1.x 缺陷 +## HTTP/1.x 缺陷 -HTTP/1.x 实现简单是以牺牲性能为代价的: +HTTP/1.x 实现简单是以牺牲性能为代价的: -- 客户端需要使用多个连接才能实现并发和缩短延迟; -- 不会压缩请求和响应首部,从而导致不必要的网络流量; -- 不支持有效的资源优先级,致使底层 TCP 连接的利用率低下。 +- 客户端需要使用多个连接才能实现并发和缩短延迟; +- 不会压缩请求和响应首部,从而导致不必要的网络流量; +- 不支持有效的资源优先级,致使底层 TCP 连接的利用率低下。 -## 二进制分帧层 +## 二进制分帧层 -HTTP/2.0 将报文分成 HEADERS 帧和 DATA 帧,它们都是二进制格式的。 +HTTP/2.0 将报文分成 HEADERS 帧和 DATA 帧,它们都是二进制格式的。 -

+ -在通信过程中,只会有一个 TCP 连接存在,它承载了任意数量的双向数据流(Stream)。 +在通信过程中,只会有一个 TCP 连接存在,它承载了任意数量的双向数据流(Stream)。 -- 一个数据流(Stream)都有一个唯一标识符和可选的优先级信息,用于承载双向信息。 -- 消息(Message)是与逻辑请求或响应对应的完整的一系列帧。 -- 帧(Frame)是最小的通信单位,来自不同数据流的帧可以交错发送,然后再根据每个帧头的数据流标识符重新组装。 +- 一个数据流(Stream)都有一个唯一标识符和可选的优先级信息,用于承载双向信息。 +- 消息(Message)是与逻辑请求或响应对应的完整的一系列帧。 +- 帧(Frame)是最小的通信单位,来自不同数据流的帧可以交错发送,然后再根据每个帧头的数据流标识符重新组装。 -

+ -## 服务端推送 +## 服务端推送 -HTTP/2.0 在客户端请求一个资源时,会把相关的资源一起发送给客户端,客户端就不需要再次发起请求了。例如客户端请求 page.html 页面,服务端就把 script.js 和 style.css 等与之相关的资源一起发给客户端。 +HTTP/2.0 在客户端请求一个资源时,会把相关的资源一起发送给客户端,客户端就不需要再次发起请求了。例如客户端请求 page.html 页面,服务端就把 script.js 和 style.css 等与之相关的资源一起发给客户端。 -

+ -## 首部压缩 +## 首部压缩 -HTTP/1.1 的首部带有大量信息,而且每次都要重复发送。 +HTTP/1.1 的首部带有大量信息,而且每次都要重复发送。 -HTTP/2.0 要求客户端和服务器同时维护和更新一个包含之前见过的首部字段表,从而避免了重复传输。 +HTTP/2.0 要求客户端和服务器同时维护和更新一个包含之前见过的首部字段表,从而避免了重复传输。 -不仅如此,HTTP/2.0 也使用 Huffman 编码对首部字段进行压缩。 +不仅如此,HTTP/2.0 也使用 Huffman 编码对首部字段进行压缩。 -

+ -# 八、HTTP/1.1 新特性 +# 八、HTTP/1.1 新特性 详细内容请见上文 -- 默认是长连接 -- 支持流水线 -- 支持同时打开多个 TCP 连接 -- 支持虚拟主机 -- 新增状态码 100 -- 支持分块传输编码 -- 新增缓存处理指令 max-age +- 默认是长连接 +- 支持流水线 +- 支持同时打开多个 TCP 连接 +- 支持虚拟主机 +- 新增状态码 100 +- 支持分块传输编码 +- 新增缓存处理指令 max-age -# 九、GET 和 POST 比较 +# 九、GET 和 POST 比较 -## 作用 +## 作用 -GET 用于获取资源,而 POST 用于传输实体主体。 +GET 用于获取资源,而 POST 用于传输实体主体。 -## 参数 +## 参数 -GET 和 POST 的请求都能使用额外的参数,但是 GET 的参数是以查询字符串出现在 URL 中,而 POST 的参数存储在实体主体中。不能因为 POST 参数存储在实体主体中就认为它的安全性更高,因为照样可以通过一些抓包工具(Fiddler)查看。 +GET 和 POST 的请求都能使用额外的参数,但是 GET 的参数是以查询字符串出现在 URL 中,而 POST 的参数存储在实体主体中。不能因为 POST 参数存储在实体主体中就认为它的安全性更高,因为照样可以通过一些抓包工具(Fiddler)查看。 -因为 URL 只支持 ASCII 码,因此 GET 的参数中如果存在中文等字符就需要先进行编码。例如 `中文` 会转换为 `%E4%B8%AD%E6%96%87`,而空格会转换为 `%20`。POST 参考支持标准字符集。 +因为 URL 只支持 ASCII 码,因此 GET 的参数中如果存在中文等字符就需要先进行编码。例如 `中文` 会转换为 `%E4%B8%AD%E6%96%87`,而空格会转换为 `%20`。POST 参考支持标准字符集。 ``` -GET /test/demo_form.asp?name1=value1&name2=value2 HTTP/1.1 +GET /test/demo_form.asp?name1=value1&name2=value2 HTTP/1.1 ``` ``` -POST /test/demo_form.asp HTTP/1.1 -Host: w3schools.com +POST /test/demo_form.asp HTTP/1.1 +Host: w3schools.com name1=value1&name2=value2 ``` -## 安全 +## 安全 -安全的 HTTP 方法不会改变服务器状态,也就是说它只是可读的。 +安全的 HTTP 方法不会改变服务器状态,也就是说它只是可读的。 -GET 方法是安全的,而 POST 却不是,因为 POST 的目的是传送实体主体内容,这个内容可能是用户上传的表单数据,上传成功之后,服务器可能把这个数据存储到数据库中,因此状态也就发生了改变。 +GET 方法是安全的,而 POST 却不是,因为 POST 的目的是传送实体主体内容,这个内容可能是用户上传的表单数据,上传成功之后,服务器可能把这个数据存储到数据库中,因此状态也就发生了改变。 -安全的方法除了 GET 之外还有:HEAD、OPTIONS。 +安全的方法除了 GET 之外还有:HEAD、OPTIONS。 -不安全的方法除了 POST 之外还有 PUT、DELETE。 +不安全的方法除了 POST 之外还有 PUT、DELETE。 -## 幂等性 +## 幂等性 -幂等的 HTTP 方法,同样的请求被执行一次与连续执行多次的效果是一样的,服务器的状态也是一样的。换句话说就是,幂等方法不应该具有副作用(统计用途除外)。 +幂等的 HTTP 方法,同样的请求被执行一次与连续执行多次的效果是一样的,服务器的状态也是一样的。换句话说就是,幂等方法不应该具有副作用(统计用途除外)。 所有的安全方法也都是幂等的。 -在正确实现的条件下,GET,HEAD,PUT 和 DELETE 等方法都是幂等的,而 POST 方法不是。 +在正确实现的条件下,GET,HEAD,PUT 和 DELETE 等方法都是幂等的,而 POST 方法不是。 -GET /pageX HTTP/1.1 是幂等的,连续调用多次,客户端接收到的结果都是一样的: +GET /pageX HTTP/1.1 是幂等的,连续调用多次,客户端接收到的结果都是一样的: ``` -GET /pageX HTTP/1.1 -GET /pageX HTTP/1.1 -GET /pageX HTTP/1.1 -GET /pageX HTTP/1.1 +GET /pageX HTTP/1.1 +GET /pageX HTTP/1.1 +GET /pageX HTTP/1.1 +GET /pageX HTTP/1.1 ``` -POST /add_row HTTP/1.1 不是幂等的,如果调用多次,就会增加多行记录: +POST /add_row HTTP/1.1 不是幂等的,如果调用多次,就会增加多行记录: ``` -POST /add_row HTTP/1.1 -> Adds a 1nd row -POST /add_row HTTP/1.1 -> Adds a 2nd row -POST /add_row HTTP/1.1 -> Adds a 3rd row +POST /add_row HTTP/1.1   -> Adds a 1nd row +POST /add_row HTTP/1.1   -> Adds a 2nd row +POST /add_row HTTP/1.1   -> Adds a 3rd row ``` -DELETE /idX/delete HTTP/1.1 是幂等的,即便不同的请求接收到的状态码不一样: +DELETE /idX/delete HTTP/1.1 是幂等的,即便不同的请求接收到的状态码不一样: ``` -DELETE /idX/delete HTTP/1.1 -> Returns 200 if idX exists -DELETE /idX/delete HTTP/1.1 -> Returns 404 as it just got deleted -DELETE /idX/delete HTTP/1.1 -> Returns 404 +DELETE /idX/delete HTTP/1.1   -> Returns 200 if idX exists +DELETE /idX/delete HTTP/1.1   -> Returns 404 as it just got deleted +DELETE /idX/delete HTTP/1.1   -> Returns 404 ``` -## 可缓存 +## 可缓存 如果要对响应进行缓存,需要满足以下条件: -- 请求报文的 HTTP 方法本身是可缓存的,包括 GET 和 HEAD,但是 PUT 和 DELETE 不可缓存,POST 在多数情况下不可缓存的。 -- 响应报文的状态码是可缓存的,包括:200, 203, 204, 206, 300, 301, 404, 405, 410, 414, and 501。 -- 响应报文的 Cache-Control 首部字段没有指定不进行缓存。 +- 请求报文的 HTTP 方法本身是可缓存的,包括 GET 和 HEAD,但是 PUT 和 DELETE 不可缓存,POST 在多数情况下不可缓存的。 +- 响应报文的状态码是可缓存的,包括:200, 203, 204, 206, 300, 301, 404, 405, 410, 414, and 501。 +- 响应报文的 Cache-Control 首部字段没有指定不进行缓存。 -## XMLHttpRequest +## XMLHttpRequest -为了阐述 POST 和 GET 的另一个区别,需要先了解 XMLHttpRequest: +为了阐述 POST 和 GET 的另一个区别,需要先了解 XMLHttpRequest: -> XMLHttpRequest 是一个 API,它为客户端提供了在客户端和服务器之间传输数据的功能。它提供了一个通过 URL 来获取数据的简单方式,并且不会使整个页面刷新。这使得网页只更新一部分页面而不会打扰到用户。XMLHttpRequest 在 AJAX 中被大量使用。 +> XMLHttpRequest 是一个 API,它为客户端提供了在客户端和服务器之间传输数据的功能。它提供了一个通过 URL 来获取数据的简单方式,并且不会使整个页面刷新。这使得网页只更新一部分页面而不会打扰到用户。XMLHttpRequest 在 AJAX 中被大量使用。 -- 在使用 XMLHttpRequest 的 POST 方法时,浏览器会先发送 Header 再发送 Data。但并不是所有浏览器会这么做,例如火狐就不会。 -- 而 GET 方法 Header 和 Data 会一起发送。 +- 在使用 XMLHttpRequest 的 POST 方法时,浏览器会先发送 Header 再发送 Data。但并不是所有浏览器会这么做,例如火狐就不会。 +- 而 GET 方法 Header 和 Data 会一起发送。 -# 参考资料 +# 参考资料 -- 上野宣. 图解 HTTP[M]. 人民邮电出版社, 2014. -- [MDN : HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP) -- [HTTP/2 简介](https://developers.google.com/web/fundamentals/performance/http2/?hl=zh-cn) -- [htmlspecialchars](http://php.net/manual/zh/function.htmlspecialchars.php) -- [Difference between file URI and URL in java](http://java2db.com/java-io/how-to-get-and-the-difference-between-file-uri-and-url-in-java) -- [How to Fix SQL Injection Using Java PreparedStatement & CallableStatement](https://software-security.sans.org/developer-how-to/fix-sql-injection-in-java-using-prepared-callable-statement) -- [浅谈 HTTP 中 Get 与 Post 的区别](https://www.cnblogs.com/hyddd/archive/2009/03/31/1426026.html) -- [Are http:// and www really necessary?](https://www.webdancers.com/are-http-and-www-necesary/) -- [HTTP (HyperText Transfer Protocol)](https://www.ntu.edu.sg/home/ehchua/programming/webprogramming/HTTP_Basics.html) -- [Web-VPN: Secure Proxies with SPDY & Chrome](https://www.igvita.com/2011/12/01/web-vpn-secure-proxies-with-spdy-chrome/) -- [File:HTTP persistent connection.svg](http://en.wikipedia.org/wiki/File:HTTP_persistent_connection.svg) -- [Proxy server](https://en.wikipedia.org/wiki/Proxy_server) -- [What Is This HTTPS/SSL Thing And Why Should You Care?](https://www.x-cart.com/blog/what-is-https-and-ssl.html) -- [What is SSL Offloading?](https://securebox.comodo.com/ssl-sniffing/ssl-offloading/) -- [Sun Directory Server Enterprise Edition 7.0 Reference - Key Encryption](https://docs.oracle.com/cd/E19424-01/820-4811/6ng8i26bn/index.html) -- [An Introduction to Mutual SSL Authentication](https://www.codeproject.com/Articles/326574/An-Introduction-to-Mutual-SSL-Authentication) -- [The Difference Between URLs and URIs](https://danielmiessler.com/study/url-uri/) -- [Cookie 与 Session 的区别](https://juejin.im/entry/5766c29d6be3ff006a31b84e#comment) -- [COOKIE 和 SESSION 有什么区别](https://www.zhihu.com/question/19786827) -- [Cookie/Session 的机制与安全](https://harttle.land/2015/08/10/cookie-session.html) -- [HTTPS 证书原理](https://shijianan.com/2017/06/11/https/) -- [What is the difference between a URI, a URL and a URN?](https://stackoverflow.com/questions/176264/what-is-the-difference-between-a-uri-a-url-and-a-urn) -- [XMLHttpRequest](https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest) -- [XMLHttpRequest (XHR) Uses Multiple Packets for HTTP POST?](https://blog.josephscott.org/2009/08/27/xmlhttprequest-xhr-uses-multiple-packets-for-http-post/) -- [Symmetric vs. Asymmetric Encryption – What are differences?](https://www.ssl2buy.com/wiki/symmetric-vs-asymmetric-encryption-what-are-differences) -- [Web 性能优化与 HTTP/2](https://www.kancloud.cn/digest/web-performance-http2) -- [HTTP/2 简介](https://developers.google.com/web/fundamentals/performance/http2/?hl=zh-cn) +- 上野宣. 图解 HTTP[M]. 人民邮电出版社, 2014. +- [MDN : HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP) +- [HTTP/2 简介](https://developers.google.com/web/fundamentals/performance/http2/?hl=zh-cn) +- [htmlspecialchars](http://php.net/manual/zh/function.htmlspecialchars.php) +- [Difference between file URI and URL in java](http://java2db.com/java-io/how-to-get-and-the-difference-between-file-uri-and-url-in-java) +- [How to Fix SQL Injection Using Java PreparedStatement & CallableStatement](https://software-security.sans.org/developer-how-to/fix-sql-injection-in-java-using-prepared-callable-statement) +- [浅谈 HTTP 中 Get 与 Post 的区别](https://www.cnblogs.com/hyddd/archive/2009/03/31/1426026.html) +- [Are http:// and www really necessary?](https://www.webdancers.com/are-http-and-www-necesary/) +- [HTTP (HyperText Transfer Protocol)](https://www.ntu.edu.sg/home/ehchua/programming/webprogramming/HTTP_Basics.html) +- [Web-VPN: Secure Proxies with SPDY & Chrome](https://www.igvita.com/2011/12/01/web-vpn-secure-proxies-with-spdy-chrome/) +- [File:HTTP persistent connection.svg](http://en.wikipedia.org/wiki/File:HTTP_persistent_connection.svg) +- [Proxy server](https://en.wikipedia.org/wiki/Proxy_server) +- [What Is This HTTPS/SSL Thing And Why Should You Care?](https://www.x-cart.com/blog/what-is-https-and-ssl.html) +- [What is SSL Offloading?](https://securebox.comodo.com/ssl-sniffing/ssl-offloading/) +- [Sun Directory Server Enterprise Edition 7.0 Reference - Key Encryption](https://docs.oracle.com/cd/E19424-01/820-4811/6ng8i26bn/index.html) +- [An Introduction to Mutual SSL Authentication](https://www.codeproject.com/Articles/326574/An-Introduction-to-Mutual-SSL-Authentication) +- [The Difference Between URLs and URIs](https://danielmiessler.com/study/url-uri/) +- [Cookie 与 Session 的区别](https://juejin.im/entry/5766c29d6be3ff006a31b84e#comment) +- [COOKIE 和 SESSION 有什么区别](https://www.zhihu.com/question/19786827) +- [Cookie/Session 的机制与安全](https://harttle.land/2015/08/10/cookie-session.html) +- [HTTPS 证书原理](https://shijianan.com/2017/06/11/https/) +- [What is the difference between a URI, a URL and a URN?](https://stackoverflow.com/questions/176264/what-is-the-difference-between-a-uri-a-url-and-a-urn) +- [XMLHttpRequest](https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest) +- [XMLHttpRequest (XHR) Uses Multiple Packets for HTTP POST?](https://blog.josephscott.org/2009/08/27/xmlhttprequest-xhr-uses-multiple-packets-for-http-post/) +- [Symmetric vs. Asymmetric Encryption – What are differences?](https://www.ssl2buy.com/wiki/symmetric-vs-asymmetric-encryption-what-are-differences) +- [Web 性能优化与 HTTP/2](https://www.kancloud.cn/digest/web-performance-http2) +- [HTTP/2 简介](https://developers.google.com/web/fundamentals/performance/http2/?hl=zh-cn) +---bottom---CyC--- +![](index_files/766d401b-3cf6-475c-8ced-ea8c8db8edc5.png) +![](index_files/HTTP_RequestMessageExample.png) +![](index_files/HTTP_ResponseMessageExample.png) +![](index_files/dc00f70e-c5c8-4d20-baf1-2d70014a97e3.jpg) +![](index_files/HTTP1_x_Connections.png) +![](index_files/a314bb79-5b18-4e63-a976-3448bffa6f1b.png) +![](index_files/2d09a847-b854-439c-9198-b29c65810944.png) +![](index_files/ssl-offloading.jpg) +![](index_files/7fffa4b8-b36d-471f-ad0c-a88ee763bb76.png) +![](index_files/39ccb299-ee99-4dd1-b8b4-2f9ec9495cb4.png) +![](index_files/How-HTTPS-Works.png) +![](index_files/2017-06-11-ca.png) +![](index_files/86e6a91d-a285-447a-9345-c5484b8d0c47.png) +![](index_files/af198da1-2480-4043-b07f-a3b91a88b815.png) +![](index_files/e3f1657c-80fc-4dfa-9643-bf51abd201c6.png) +![](index_files/_u4E0B_u8F7D.png) diff --git a/docs/notes/Java IO.md b/docs/notes/Java IO.md index f0ae70c0..47c0e06d 100644 --- a/docs/notes/Java IO.md +++ b/docs/notes/Java IO.md @@ -1,320 +1,286 @@ -[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) - -* [一、概览](#一概览) -* [二、磁盘操作](#二磁盘操作) -* [三、字节操作](#三字节操作) - * [实现文件复制](#实现文件复制) - * [装饰者模式](#装饰者模式) -* [四、字符操作](#四字符操作) - * [编码与解码](#编码与解码) - * [String 的编码方式](#string-的编码方式) - * [Reader 与 Writer](#reader-与-writer) - * [实现逐行输出文本文件的内容](#实现逐行输出文本文件的内容) -* [五、对象操作](#五对象操作) - * [序列化](#序列化) - * [Serializable](#serializable) - * [transient](#transient) -* [六、网络操作](#六网络操作) - * [InetAddress](#inetaddress) - * [URL](#url) - * [Sockets](#sockets) - * [Datagram](#datagram) -* [七、NIO](#七nio) - * [流与块](#流与块) - * [通道与缓冲区](#通道与缓冲区) - * [缓冲区状态变量](#缓冲区状态变量) - * [文件 NIO 实例](#文件-nio-实例) - * [选择器](#选择器) - * [套接字 NIO 实例](#套接字-nio-实例) - * [内存映射文件](#内存映射文件) - * [对比](#对比) -* [八、参考资料](#八参考资料) - +# 一、概览 +Java 的 I/O 大概可以分成以下几类: -# 一、概览 +- 磁盘操作:File +- 字节操作:InputStream 和 OutputStream +- 字符操作:Reader 和 Writer +- 对象操作:Serializable +- 网络操作:Socket +- 新的输入/输出:NIO -Java 的 I/O 大概可以分成以下几类: +# 二、磁盘操作 -- 磁盘操作:File -- 字节操作:InputStream 和 OutputStream -- 字符操作:Reader 和 Writer -- 对象操作:Serializable -- 网络操作:Socket -- 新的输入/输出:NIO - -# 二、磁盘操作 - -File 类可以用于表示文件和目录的信息,但是它不表示文件的内容。 +File 类可以用于表示文件和目录的信息,但是它不表示文件的内容。 递归地列出一个目录下所有文件: ```java -public static void listAllFiles(File dir) { - if (dir == null || !dir.exists()) { - return; - } - if (dir.isFile()) { - System.out.println(dir.getName()); - return; - } - for (File file : dir.listFiles()) { - listAllFiles(file); - } +public static void listAllFiles(File dir) { +    if (dir == null || !dir.exists()) { +        return; +    } +    if (dir.isFile()) { +        System.out.println(dir.getName()); +        return; +    } +    for (File file : dir.listFiles()) { +        listAllFiles(file); +    } } ``` -从 Java7 开始,可以使用 Paths 和 Files 代替 File。 +从 Java7 开始,可以使用 Paths 和 Files 代替 File。 -# 三、字节操作 +# 三、字节操作 -## 实现文件复制 +## 实现文件复制 ```java -public static void copyFile(String src, String dist) throws IOException { - FileInputStream in = new FileInputStream(src); - FileOutputStream out = new FileOutputStream(dist); +public static void copyFile(String src, String dist) throws IOException { +    FileInputStream in = new FileInputStream(src); +    FileOutputStream out = new FileOutputStream(dist); - byte[] buffer = new byte[20 * 1024]; - int cnt; +    byte[] buffer = new byte[20 * 1024]; +    int cnt; - // read() 最多读取 buffer.length 个字节 - // 返回的是实际读取的个数 - // 返回 -1 的时候表示读到 eof,即文件尾 - while ((cnt = in.read(buffer, 0, buffer.length)) != -1) { - out.write(buffer, 0, cnt); - } +    // read() 最多读取 buffer.length 个字节 +    // 返回的是实际读取的个数 +    // 返回 -1 的时候表示读到 eof,即文件尾 +    while ((cnt = in.read(buffer, 0, buffer.length)) != -1) { +        out.write(buffer, 0, cnt); +    } - in.close(); - out.close(); +    in.close(); +    out.close(); } ``` -## 装饰者模式 +## 装饰者模式 -Java I/O 使用了装饰者模式来实现。以 InputStream 为例, +Java I/O 使用了装饰者模式来实现。以 InputStream 为例, -- InputStream 是抽象组件; -- FileInputStream 是 InputStream 的子类,属于具体组件,提供了字节流的输入操作; -- FilterInputStream 属于抽象装饰者,装饰者用于装饰组件,为组件提供额外的功能。例如 BufferedInputStream 为 FileInputStream 提供缓存的功能。 +- InputStream 是抽象组件; +- FileInputStream 是 InputStream 的子类,属于具体组件,提供了字节流的输入操作; +- FilterInputStream 属于抽象装饰者,装饰者用于装饰组件,为组件提供额外的功能。例如 BufferedInputStream 为 FileInputStream 提供缓存的功能。 -

+ -实例化一个具有缓存功能的字节流对象时,只需要在 FileInputStream 对象上再套一层 BufferedInputStream 对象即可。 +实例化一个具有缓存功能的字节流对象时,只需要在 FileInputStream 对象上再套一层 BufferedInputStream 对象即可。 ```java -FileInputStream fileInputStream = new FileInputStream(filePath); -BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); +FileInputStream fileInputStream = new FileInputStream(filePath); +BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); ``` -DataInputStream 装饰者提供了对更多数据类型进行输入的操作,比如 int、double 等基本类型。 +DataInputStream 装饰者提供了对更多数据类型进行输入的操作,比如 int、double 等基本类型。 -# 四、字符操作 +# 四、字符操作 -## 编码与解码 +## 编码与解码 编码就是把字符转换为字节,而解码是把字节重新组合成字符。 如果编码和解码过程使用不同的编码方式那么就出现了乱码。 -- GBK 编码中,中文字符占 2 个字节,英文字符占 1 个字节; -- UTF-8 编码中,中文字符占 3 个字节,英文字符占 1 个字节; -- UTF-16be 编码中,中文字符和英文字符都占 2 个字节。 +- GBK 编码中,中文字符占 2 个字节,英文字符占 1 个字节; +- UTF-8 编码中,中文字符占 3 个字节,英文字符占 1 个字节; +- UTF-16be 编码中,中文字符和英文字符都占 2 个字节。 -UTF-16be 中的 be 指的是 Big Endian,也就是大端。相应地也有 UTF-16le,le 指的是 Little Endian,也就是小端。 +UTF-16be 中的 be 指的是 Big Endian,也就是大端。相应地也有 UTF-16le,le 指的是 Little Endian,也就是小端。 -Java 的内存编码使用双字节编码 UTF-16be,这不是指 Java 只支持这一种编码方式,而是说 char 这种类型使用 UTF-16be 进行编码。char 类型占 16 位,也就是两个字节,Java 使用这种双字节编码是为了让一个中文或者一个英文都能使用一个 char 来存储。 +Java 的内存编码使用双字节编码 UTF-16be,这不是指 Java 只支持这一种编码方式,而是说 char 这种类型使用 UTF-16be 进行编码。char 类型占 16 位,也就是两个字节,Java 使用这种双字节编码是为了让一个中文或者一个英文都能使用一个 char 来存储。 -## String 的编码方式 +## String 的编码方式 -String 可以看成一个字符序列,可以指定一个编码方式将它编码为字节序列,也可以指定一个编码方式将一个字节序列解码为 String。 +String 可以看成一个字符序列,可以指定一个编码方式将它编码为字节序列,也可以指定一个编码方式将一个字节序列解码为 String。 ```java -String str1 = "中文"; -byte[] bytes = str1.getBytes("UTF-8"); -String str2 = new String(bytes, "UTF-8"); +String str1 = "中文"; +byte[] bytes = str1.getBytes("UTF-8"); +String str2 = new String(bytes, "UTF-8"); System.out.println(str2); ``` -在调用无参数 getBytes() 方法时,默认的编码方式不是 UTF-16be。双字节编码的好处是可以使用一个 char 存储中文和英文,而将 String 转为 bytes[] 字节数组就不再需要这个好处,因此也就不再需要双字节编码。getBytes() 的默认编码方式与平台有关,一般为 UTF-8。 +在调用无参数 getBytes() 方法时,默认的编码方式不是 UTF-16be。双字节编码的好处是可以使用一个 char 存储中文和英文,而将 String 转为 bytes[] 字节数组就不再需要这个好处,因此也就不再需要双字节编码。getBytes() 的默认编码方式与平台有关,一般为 UTF-8。 ```java -byte[] bytes = str1.getBytes(); +byte[] bytes = str1.getBytes(); ``` -## Reader 与 Writer +## Reader 与 Writer 不管是磁盘还是网络传输,最小的存储单元都是字节,而不是字符。但是在程序中操作的通常是字符形式的数据,因此需要提供对字符进行操作的方法。 -- InputStreamReader 实现从字节流解码成字符流; -- OutputStreamWriter 实现字符流编码成为字节流。 +- InputStreamReader 实现从字节流解码成字符流; +- OutputStreamWriter 实现字符流编码成为字节流。 -## 实现逐行输出文本文件的内容 +## 实现逐行输出文本文件的内容 ```java -public static void readFileContent(String filePath) throws IOException { +public static void readFileContent(String filePath) throws IOException { - FileReader fileReader = new FileReader(filePath); - BufferedReader bufferedReader = new BufferedReader(fileReader); +    FileReader fileReader = new FileReader(filePath); +    BufferedReader bufferedReader = new BufferedReader(fileReader); - String line; - while ((line = bufferedReader.readLine()) != null) { - System.out.println(line); - } +    String line; +    while ((line = bufferedReader.readLine()) != null) { +        System.out.println(line); +    } - // 装饰者模式使得 BufferedReader 组合了一个 Reader 对象 - // 在调用 BufferedReader 的 close() 方法时会去调用 Reader 的 close() 方法 - // 因此只要一个 close() 调用即可 - bufferedReader.close(); +    // 装饰者模式使得 BufferedReader 组合了一个 Reader 对象 +    // 在调用 BufferedReader 的 close() 方法时会去调用 Reader 的 close() 方法 +    // 因此只要一个 close() 调用即可 +    bufferedReader.close(); } ``` -# 五、对象操作 +# 五、对象操作 -## 序列化 +## 序列化 序列化就是将一个对象转换成字节序列,方便存储和传输。 -- 序列化:ObjectOutputStream.writeObject() -- 反序列化:ObjectInputStream.readObject() +- 序列化:ObjectOutputStream.writeObject() +- 反序列化:ObjectInputStream.readObject() 不会对静态变量进行序列化,因为序列化只是保存对象的状态,静态变量属于类的状态。 -## Serializable +## Serializable -序列化的类需要实现 Serializable 接口,它只是一个标准,没有任何方法需要实现,但是如果不去实现它的话而进行序列化,会抛出异常。 +序列化的类需要实现 Serializable 接口,它只是一个标准,没有任何方法需要实现,但是如果不去实现它的话而进行序列化,会抛出异常。 ```java -public static void main(String[] args) throws IOException, ClassNotFoundException { +public static void main(String[] args) throws IOException, ClassNotFoundException { - A a1 = new A(123, "abc"); - String objectFile = "file/a1"; +    A a1 = new A(123, "abc"); +    String objectFile = "file/a1"; - ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(objectFile)); - objectOutputStream.writeObject(a1); - objectOutputStream.close(); +    ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(objectFile)); +    objectOutputStream.writeObject(a1); +    objectOutputStream.close(); - ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(objectFile)); - A a2 = (A) objectInputStream.readObject(); - objectInputStream.close(); - System.out.println(a2); +    ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(objectFile)); +    A a2 = (A) objectInputStream.readObject(); +    objectInputStream.close(); +    System.out.println(a2); } -private static class A implements Serializable { +private static class A implements Serializable { - private int x; - private String y; +    private int x; +    private String y; - A(int x, String y) { - this.x = x; - this.y = y; - } +    A(int x, String y) { +        this.x = x; +        this.y = y; +    } - @Override - public String toString() { - return "x = " + x + " " + "y = " + y; - } +    @Override +    public String toString() { +        return "x = " + x + "  " + "y = " + y; +    } } ``` -## transient +## transient -transient 关键字可以使一些属性不会被序列化。 +transient 关键字可以使一些属性不会被序列化。 -ArrayList 中存储数据的数组 elementData 是用 transient 修饰的,因为这个数组是动态扩展的,并不是所有的空间都被使用,因此就不需要所有的内容都被序列化。通过重写序列化和反序列化方法,使得可以只序列化数组中有内容的那部分数据。 +ArrayList 中存储数据的数组 elementData 是用 transient 修饰的,因为这个数组是动态扩展的,并不是所有的空间都被使用,因此就不需要所有的内容都被序列化。通过重写序列化和反序列化方法,使得可以只序列化数组中有内容的那部分数据。 ```java -private transient Object[] elementData; +private transient Object[] elementData; ``` -# 六、网络操作 +# 六、网络操作 -Java 中的网络支持: +Java 中的网络支持: -- InetAddress:用于表示网络上的硬件资源,即 IP 地址; -- URL:统一资源定位符; -- Sockets:使用 TCP 协议实现网络通信; -- Datagram:使用 UDP 协议实现网络通信。 +- InetAddress:用于表示网络上的硬件资源,即 IP 地址; +- URL:统一资源定位符; +- Sockets:使用 TCP 协议实现网络通信; +- Datagram:使用 UDP 协议实现网络通信。 -## InetAddress +## InetAddress 没有公有的构造函数,只能通过静态方法来创建实例。 ```java -InetAddress.getByName(String host); -InetAddress.getByAddress(byte[] address); +InetAddress.getByName(String host); +InetAddress.getByAddress(byte[] address); ``` -## URL +## URL -可以直接从 URL 中读取字节流数据。 +可以直接从 URL 中读取字节流数据。 ```java -public static void main(String[] args) throws IOException { +public static void main(String[] args) throws IOException { - URL url = new URL("http://www.baidu.com"); +    URL url = new URL("http://www.baidu.com"); - /* 字节流 */ - InputStream is = url.openStream(); +    /* 字节流 */ +    InputStream is = url.openStream(); - /* 字符流 */ - InputStreamReader isr = new InputStreamReader(is, "utf-8"); +    /* 字符流 */ +    InputStreamReader isr = new InputStreamReader(is, "utf-8"); - /* 提供缓存功能 */ - BufferedReader br = new BufferedReader(isr); +    /* 提供缓存功能 */ +    BufferedReader br = new BufferedReader(isr); - String line; - while ((line = br.readLine()) != null) { - System.out.println(line); - } +    String line; +    while ((line = br.readLine()) != null) { +        System.out.println(line); +    } - br.close(); +    br.close(); } ``` -## Sockets +## Sockets -- ServerSocket:服务器端类 -- Socket:客户端类 -- 服务器和客户端通过 InputStream 和 OutputStream 进行输入输出。 +- ServerSocket:服务器端类 +- Socket:客户端类 +- 服务器和客户端通过 InputStream 和 OutputStream 进行输入输出。 -

+![](index_files/ClienteServidorSockets1521731145260.jpg) -## Datagram +## Datagram -- DatagramSocket:通信类 -- DatagramPacket:数据包类 +- DatagramSocket:通信类 +- DatagramPacket:数据包类 -# 七、NIO +# 七、NIO -新的输入/输出 (NIO) 库是在 JDK 1.4 中引入的,弥补了原来的 I/O 的不足,提供了高速的、面向块的 I/O。 +新的输入/输出 (NIO) 库是在 JDK 1.4 中引入的,弥补了原来的 I/O 的不足,提供了高速的、面向块的 I/O。 -## 流与块 +## 流与块 -I/O 与 NIO 最重要的区别是数据打包和传输的方式,I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。 +I/O 与 NIO 最重要的区别是数据打包和传输的方式,I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。 -面向流的 I/O 一次处理一个字节数据:一个输入流产生一个字节数据,一个输出流消费一个字节数据。为流式数据创建过滤器非常容易,链接几个过滤器,以便每个过滤器只负责复杂处理机制的一部分。不利的一面是,面向流的 I/O 通常相当慢。 +面向流的 I/O 一次处理一个字节数据:一个输入流产生一个字节数据,一个输出流消费一个字节数据。为流式数据创建过滤器非常容易,链接几个过滤器,以便每个过滤器只负责复杂处理机制的一部分。不利的一面是,面向流的 I/O 通常相当慢。 -面向块的 I/O 一次处理一个数据块,按块处理数据比按流处理数据要快得多。但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性。 +面向块的 I/O 一次处理一个数据块,按块处理数据比按流处理数据要快得多。但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性。 -I/O 包和 NIO 已经很好地集成了,java.io.\* 已经以 NIO 为基础重新实现了,所以现在它可以利用 NIO 的一些特性。例如,java.io.\* 包中的一些类包含以块的形式读写数据的方法,这使得即使在面向流的系统中,处理速度也会更快。 +I/O 包和 NIO 已经很好地集成了,java.io.\* 已经以 NIO 为基础重新实现了,所以现在它可以利用 NIO 的一些特性。例如,java.io.\* 包中的一些类包含以块的形式读写数据的方法,这使得即使在面向流的系统中,处理速度也会更快。 -## 通道与缓冲区 +## 通道与缓冲区 -### 1. 通道 +### 1. 通道 -通道 Channel 是对原 I/O 包中的流的模拟,可以通过它读取和写入数据。 +通道 Channel 是对原 I/O 包中的流的模拟,可以通过它读取和写入数据。 -通道与流的不同之处在于,流只能在一个方向上移动(一个流必须是 InputStream 或者 OutputStream 的子类),而通道是双向的,可以用于读、写或者同时用于读写。 +通道与流的不同之处在于,流只能在一个方向上移动(一个流必须是 InputStream 或者 OutputStream 的子类),而通道是双向的,可以用于读、写或者同时用于读写。 通道包括以下类型: -- FileChannel:从文件中读写数据; -- DatagramChannel:通过 UDP 读写网络中数据; -- SocketChannel:通过 TCP 读写网络中数据; -- ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel。 +- FileChannel:从文件中读写数据; +- DatagramChannel:通过 UDP 读写网络中数据; +- SocketChannel:通过 TCP 读写网络中数据; +- ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel。 -### 2. 缓冲区 +### 2. 缓冲区 发送给一个通道的所有数据都必须首先放到缓冲区中,同样地,从通道中读取的任何数据都要先读到缓冲区中。也就是说,不会直接对通道进行读写数据,而是要先经过缓冲区。 @@ -322,300 +288,308 @@ I/O 包和 NIO 已经很好地集成了,java.io.\* 已经以 NIO 为基础重 缓冲区包括以下类型: -- ByteBuffer -- CharBuffer -- ShortBuffer -- IntBuffer -- LongBuffer -- FloatBuffer -- DoubleBuffer +- ByteBuffer +- CharBuffer +- ShortBuffer +- IntBuffer +- LongBuffer +- FloatBuffer +- DoubleBuffer -## 缓冲区状态变量 +## 缓冲区状态变量 -- capacity:最大容量; -- position:当前已经读写的字节数; -- limit:还可以读写的字节数。 +- capacity:最大容量; +- position:当前已经读写的字节数; +- limit:还可以读写的字节数。 状态变量的改变过程举例: -① 新建一个大小为 8 个字节的缓冲区,此时 position 为 0,而 limit = capacity = 8。capacity 变量不会改变,下面的讨论会忽略它。 +① 新建一个大小为 8 个字节的缓冲区,此时 position 为 0,而 limit = capacity = 8。capacity 变量不会改变,下面的讨论会忽略它。 -

+![](index_files/1bea398f-17a7-4f67-a90b-9e2d243eaa9a.png) -② 从输入通道中读取 5 个字节数据写入缓冲区中,此时 position 为 5,limit 保持不变。 +② 从输入通道中读取 5 个字节数据写入缓冲区中,此时 position 为 5,limit 保持不变。 -

+![](index_files/80804f52-8815-4096-b506-48eef3eed5c6.png) -③ 在将缓冲区的数据写到输出通道之前,需要先调用 flip() 方法,这个方法将 limit 设置为当前 position,并将 position 设置为 0。 +③ 在将缓冲区的数据写到输出通道之前,需要先调用 flip() 方法,这个方法将 limit 设置为当前 position,并将 position 设置为 0。 -

+![](index_files/952e06bd-5a65-4cab-82e4-dd1536462f38.png) -④ 从缓冲区中取 4 个字节到输出缓冲中,此时 position 设为 4。 +④ 从缓冲区中取 4 个字节到输出缓冲中,此时 position 设为 4。 -

+![](index_files/b5bdcbe2-b958-4aef-9151-6ad963cb28b4.png) -⑤ 最后需要调用 clear() 方法来清空缓冲区,此时 position 和 limit 都被设置为最初位置。 +⑤ 最后需要调用 clear() 方法来清空缓冲区,此时 position 和 limit 都被设置为最初位置。 -

+![](index_files/67bf5487-c45d-49b6-b9c0-a058d8c68902.png) -## 文件 NIO 实例 +## 文件 NIO 实例 -以下展示了使用 NIO 快速复制文件的实例: +以下展示了使用 NIO 快速复制文件的实例: ```java -public static void fastCopy(String src, String dist) throws IOException { +public static void fastCopy(String src, String dist) throws IOException { - /* 获得源文件的输入字节流 */ - FileInputStream fin = new FileInputStream(src); +    /* 获得源文件的输入字节流 */ +    FileInputStream fin = new FileInputStream(src); - /* 获取输入字节流的文件通道 */ - FileChannel fcin = fin.getChannel(); +    /* 获取输入字节流的文件通道 */ +    FileChannel fcin = fin.getChannel(); - /* 获取目标文件的输出字节流 */ - FileOutputStream fout = new FileOutputStream(dist); +    /* 获取目标文件的输出字节流 */ +    FileOutputStream fout = new FileOutputStream(dist); - /* 获取输出字节流的文件通道 */ - FileChannel fcout = fout.getChannel(); +    /* 获取输出字节流的文件通道 */ +    FileChannel fcout = fout.getChannel(); - /* 为缓冲区分配 1024 个字节 */ - ByteBuffer buffer = ByteBuffer.allocateDirect(1024); +    /* 为缓冲区分配 1024 个字节 */ +    ByteBuffer buffer = ByteBuffer.allocateDirect(1024); - while (true) { +    while (true) { - /* 从输入通道中读取数据到缓冲区中 */ - int r = fcin.read(buffer); +        /* 从输入通道中读取数据到缓冲区中 */ +        int r = fcin.read(buffer); - /* read() 返回 -1 表示 EOF */ - if (r == -1) { - break; - } +        /* read() 返回 -1 表示 EOF */ +        if (r == -1) { +            break; +        } - /* 切换读写 */ - buffer.flip(); +        /* 切换读写 */ +        buffer.flip(); - /* 把缓冲区的内容写入输出文件中 */ - fcout.write(buffer); +        /* 把缓冲区的内容写入输出文件中 */ +        fcout.write(buffer); - /* 清空缓冲区 */ - buffer.clear(); - } +        /* 清空缓冲区 */ +        buffer.clear(); +    } } ``` -## 选择器 +## 选择器 -NIO 常常被叫做非阻塞 IO,主要是因为 NIO 在网络通信中的非阻塞特性被广泛使用。 +NIO 常常被叫做非阻塞 IO,主要是因为 NIO 在网络通信中的非阻塞特性被广泛使用。 -NIO 实现了 IO 多路复用中的 Reactor 模型,一个线程 Thread 使用一个选择器 Selector 通过轮询的方式去监听多个通道 Channel 上的事件,从而让一个线程就可以处理多个事件。 +NIO 实现了 IO 多路复用中的 Reactor 模型,一个线程 Thread 使用一个选择器 Selector 通过轮询的方式去监听多个通道 Channel 上的事件,从而让一个线程就可以处理多个事件。 -通过配置监听的通道 Channel 为非阻塞,那么当 Channel 上的 IO 事件还未到达时,就不会进入阻塞状态一直等待,而是继续轮询其它 Channel,找到 IO 事件已经到达的 Channel 执行。 +通过配置监听的通道 Channel 为非阻塞,那么当 Channel 上的 IO 事件还未到达时,就不会进入阻塞状态一直等待,而是继续轮询其它 Channel,找到 IO 事件已经到达的 Channel 执行。 -因为创建和切换线程的开销很大,因此使用一个线程来处理多个事件而不是一个线程处理一个事件,对于 IO 密集型的应用具有很好地性能。 +因为创建和切换线程的开销很大,因此使用一个线程来处理多个事件而不是一个线程处理一个事件,对于 IO 密集型的应用具有很好地性能。 -应该注意的是,只有套接字 Channel 才能配置为非阻塞,而 FileChannel 不能,为 FileChannel 配置非阻塞也没有意义。 +应该注意的是,只有套接字 Channel 才能配置为非阻塞,而 FileChannel 不能,为 FileChannel 配置非阻塞也没有意义。 -

+![](index_files/4d930e22-f493-49ae-8dff-ea21cd6895dc.png) -### 1. 创建选择器 +### 1. 创建选择器 ```java -Selector selector = Selector.open(); +Selector selector = Selector.open(); ``` -### 2. 将通道注册到选择器上 +### 2. 将通道注册到选择器上 ```java -ServerSocketChannel ssChannel = ServerSocketChannel.open(); +ServerSocketChannel ssChannel = ServerSocketChannel.open(); ssChannel.configureBlocking(false); -ssChannel.register(selector, SelectionKey.OP_ACCEPT); +ssChannel.register(selector, SelectionKey.OP_ACCEPT); ``` 通道必须配置为非阻塞模式,否则使用选择器就没有任何意义了,因为如果通道在某个事件上被阻塞,那么服务器就不能响应其它事件,必须等待这个事件处理完毕才能去处理其它事件,显然这和选择器的作用背道而驰。 在将通道注册到选择器上时,还需要指定要注册的具体事件,主要有以下几类: -- SelectionKey.OP_CONNECT -- SelectionKey.OP_ACCEPT -- SelectionKey.OP_READ -- SelectionKey.OP_WRITE +- SelectionKey.OP_CONNECT +- SelectionKey.OP_ACCEPT +- SelectionKey.OP_READ +- SelectionKey.OP_WRITE -它们在 SelectionKey 的定义如下: +它们在 SelectionKey 的定义如下: ```java -public static final int OP_READ = 1 << 0; -public static final int OP_WRITE = 1 << 2; -public static final int OP_CONNECT = 1 << 3; -public static final int OP_ACCEPT = 1 << 4; +public static final int OP_READ = 1 << 0; +public static final int OP_WRITE = 1 << 2; +public static final int OP_CONNECT = 1 << 3; +public static final int OP_ACCEPT = 1 << 4; ``` 可以看出每个事件可以被当成一个位域,从而组成事件集整数。例如: ```java -int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE; +int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE; ``` -### 3. 监听事件 +### 3. 监听事件 ```java -int num = selector.select(); +int num = selector.select(); ``` -使用 select() 来监听到达的事件,它会一直阻塞直到有至少一个事件到达。 +使用 select() 来监听到达的事件,它会一直阻塞直到有至少一个事件到达。 -### 4. 获取到达的事件 +### 4. 获取到达的事件 ```java -Set keys = selector.selectedKeys(); -Iterator keyIterator = keys.iterator(); -while (keyIterator.hasNext()) { - SelectionKey key = keyIterator.next(); - if (key.isAcceptable()) { - // ... - } else if (key.isReadable()) { - // ... - } - keyIterator.remove(); +Set keys = selector.selectedKeys(); +Iterator keyIterator = keys.iterator(); +while (keyIterator.hasNext()) { +    SelectionKey key = keyIterator.next(); +    if (key.isAcceptable()) { +        // ... +    } else if (key.isReadable()) { +        // ... +    } +    keyIterator.remove(); } ``` -### 5. 事件循环 +### 5. 事件循环 -因为一次 select() 调用不能处理完所有的事件,并且服务器端有可能需要一直监听事件,因此服务器端处理事件的代码一般会放在一个死循环内。 +因为一次 select() 调用不能处理完所有的事件,并且服务器端有可能需要一直监听事件,因此服务器端处理事件的代码一般会放在一个死循环内。 ```java -while (true) { - int num = selector.select(); - Set keys = selector.selectedKeys(); - Iterator keyIterator = keys.iterator(); - while (keyIterator.hasNext()) { - SelectionKey key = keyIterator.next(); - if (key.isAcceptable()) { - // ... - } else if (key.isReadable()) { - // ... - } - keyIterator.remove(); - } +while (true) { +    int num = selector.select(); +    Set keys = selector.selectedKeys(); +    Iterator keyIterator = keys.iterator(); +    while (keyIterator.hasNext()) { +        SelectionKey key = keyIterator.next(); +        if (key.isAcceptable()) { +            // ... +        } else if (key.isReadable()) { +            // ... +        } +        keyIterator.remove(); +    } } ``` -## 套接字 NIO 实例 +## 套接字 NIO 实例 ```java -public class NIOServer { +public class NIOServer { - public static void main(String[] args) throws IOException { +    public static void main(String[] args) throws IOException { - Selector selector = Selector.open(); +        Selector selector = Selector.open(); - ServerSocketChannel ssChannel = ServerSocketChannel.open(); - ssChannel.configureBlocking(false); - ssChannel.register(selector, SelectionKey.OP_ACCEPT); +        ServerSocketChannel ssChannel = ServerSocketChannel.open(); +        ssChannel.configureBlocking(false); +        ssChannel.register(selector, SelectionKey.OP_ACCEPT); - ServerSocket serverSocket = ssChannel.socket(); - InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8888); - serverSocket.bind(address); +        ServerSocket serverSocket = ssChannel.socket(); +        InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8888); +        serverSocket.bind(address); - while (true) { +        while (true) { - selector.select(); - Set keys = selector.selectedKeys(); - Iterator keyIterator = keys.iterator(); +            selector.select(); +            Set keys = selector.selectedKeys(); +            Iterator keyIterator = keys.iterator(); - while (keyIterator.hasNext()) { +            while (keyIterator.hasNext()) { - SelectionKey key = keyIterator.next(); +                SelectionKey key = keyIterator.next(); - if (key.isAcceptable()) { +                if (key.isAcceptable()) { - ServerSocketChannel ssChannel1 = (ServerSocketChannel) key.channel(); +                    ServerSocketChannel ssChannel1 = (ServerSocketChannel) key.channel(); - // 服务器会为每个新连接创建一个 SocketChannel - SocketChannel sChannel = ssChannel1.accept(); - sChannel.configureBlocking(false); +                    // 服务器会为每个新连接创建一个 SocketChannel +                    SocketChannel sChannel = ssChannel1.accept(); +                    sChannel.configureBlocking(false); - // 这个新连接主要用于从客户端读取数据 - sChannel.register(selector, SelectionKey.OP_READ); +                    // 这个新连接主要用于从客户端读取数据 +                    sChannel.register(selector, SelectionKey.OP_READ); - } else if (key.isReadable()) { +                } else if (key.isReadable()) { - SocketChannel sChannel = (SocketChannel) key.channel(); - System.out.println(readDataFromSocketChannel(sChannel)); - sChannel.close(); - } +                    SocketChannel sChannel = (SocketChannel) key.channel(); +                    System.out.println(readDataFromSocketChannel(sChannel)); +                    sChannel.close(); +                } - keyIterator.remove(); - } - } - } +                keyIterator.remove(); +            } +        } +    } - private static String readDataFromSocketChannel(SocketChannel sChannel) throws IOException { +    private static String readDataFromSocketChannel(SocketChannel sChannel) throws IOException { - ByteBuffer buffer = ByteBuffer.allocate(1024); - StringBuilder data = new StringBuilder(); +        ByteBuffer buffer = ByteBuffer.allocate(1024); +        StringBuilder data = new StringBuilder(); - while (true) { +        while (true) { - buffer.clear(); - int n = sChannel.read(buffer); - if (n == -1) { - break; - } - buffer.flip(); - int limit = buffer.limit(); - char[] dst = new char[limit]; - for (int i = 0; i < limit; i++) { - dst[i] = (char) buffer.get(i); - } - data.append(dst); - buffer.clear(); - } - return data.toString(); - } +            buffer.clear(); +            int n = sChannel.read(buffer); +            if (n == -1) { +                break; +            } +            buffer.flip(); +            int limit = buffer.limit(); +            char[] dst = new char[limit]; +            for (int i = 0; i < limit; i++) { +                dst[i] = (char) buffer.get(i); +            } +            data.append(dst); +            buffer.clear(); +        } +        return data.toString(); +    } } ``` ```java -public class NIOClient { +public class NIOClient { - public static void main(String[] args) throws IOException { - Socket socket = new Socket("127.0.0.1", 8888); - OutputStream out = socket.getOutputStream(); - String s = "hello world"; - out.write(s.getBytes()); - out.close(); - } +    public static void main(String[] args) throws IOException { +        Socket socket = new Socket("127.0.0.1", 8888); +        OutputStream out = socket.getOutputStream(); +        String s = "hello world"; +        out.write(s.getBytes()); +        out.close(); +    } } ``` -## 内存映射文件 +## 内存映射文件 -内存映射文件 I/O 是一种读和写文件数据的方法,它可以比常规的基于流或者基于通道的 I/O 快得多。 +内存映射文件 I/O 是一种读和写文件数据的方法,它可以比常规的基于流或者基于通道的 I/O 快得多。 向内存映射文件写入可能是危险的,只是改变数组的单个元素这样的简单操作,就可能会直接修改磁盘上的文件。修改数据与将数据保存到磁盘是没有分开的。 -下面代码行将文件的前 1024 个字节映射到内存中,map() 方法返回一个 MappedByteBuffer,它是 ByteBuffer 的子类。因此,可以像使用其他任何 ByteBuffer 一样使用新映射的缓冲区,操作系统会在需要时负责执行映射。 +下面代码行将文件的前 1024 个字节映射到内存中,map() 方法返回一个 MappedByteBuffer,它是 ByteBuffer 的子类。因此,可以像使用其他任何 ByteBuffer 一样使用新映射的缓冲区,操作系统会在需要时负责执行映射。 ```java -MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024); +MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024); ``` -## 对比 +## 对比 -NIO 与普通 I/O 的区别主要有以下两点: +NIO 与普通 I/O 的区别主要有以下两点: -- NIO 是非阻塞的; -- NIO 面向块,I/O 面向流。 +- NIO 是非阻塞的; +- NIO 面向块,I/O 面向流。 -# 八、参考资料 +# 八、参考资料 -- Eckel B, 埃克尔, 昊鹏, 等. Java 编程思想 [M]. 机械工业出版社, 2002. -- [IBM: NIO 入门](https://www.ibm.com/developerworks/cn/education/java/j-nio/j-nio.html) -- [Java NIO Tutorial](http://tutorials.jenkov.com/java-nio/index.html) -- [Java NIO 浅析](https://tech.meituan.com/nio.html) -- [IBM: 深入分析 Java I/O 的工作机制](https://www.ibm.com/developerworks/cn/java/j-lo-javaio/index.html) -- [IBM: 深入分析 Java 中的中文编码问题](https://www.ibm.com/developerworks/cn/java/j-lo-chinesecoding/index.html) -- [IBM: Java 序列化的高级认识](https://www.ibm.com/developerworks/cn/java/j-lo-serial/index.html) -- [NIO 与传统 IO 的区别](http://blog.csdn.net/shimiso/article/details/24990499) -- [Decorator Design Pattern](http://stg-tud.github.io/sedc/Lecture/ws13-14/5.3-Decorator.html#mode=document) -- [Socket Multicast](http://labojava.blogspot.com/2012/12/socket-multicast.html) +- Eckel B, 埃克尔, 昊鹏, 等. Java 编程思想 [M]. 机械工业出版社, 2002. +- [IBM: NIO 入门](https://www.ibm.com/developerworks/cn/education/java/j-nio/j-nio.html) +- [Java NIO Tutorial](http://tutorials.jenkov.com/java-nio/index.html) +- [Java NIO 浅析](https://tech.meituan.com/nio.html) +- [IBM: 深入分析 Java I/O 的工作机制](https://www.ibm.com/developerworks/cn/java/j-lo-javaio/index.html) +- [IBM: 深入分析 Java 中的中文编码问题](https://www.ibm.com/developerworks/cn/java/j-lo-chinesecoding/index.html) +- [IBM: Java 序列化的高级认识](https://www.ibm.com/developerworks/cn/java/j-lo-serial/index.html) +- [NIO 与传统 IO 的区别](http://blog.csdn.net/shimiso/article/details/24990499) +- [Decorator Design Pattern](http://stg-tud.github.io/sedc/Lecture/ws13-14/5.3-Decorator.html#mode=document) +- [Socket Multicast](http://labojava.blogspot.com/2012/12/socket-multicast.html) +---bottom---CyC--- +![](index_files/DP-Decorator-java.io.png) +![](index_files/ClienteServidorSockets1521731145260.jpg) +![](index_files/1bea398f-17a7-4f67-a90b-9e2d243eaa9a.png) +![](index_files/4628274c-25b6-4053-97cf-d1239b44c43d.png) +![](index_files/952e06bd-5a65-4cab-82e4-dd1536462f38.png) +![](index_files/b5bdcbe2-b958-4aef-9151-6ad963cb28b4.png) +![](index_files/67bf5487-c45d-49b6-b9c0-a058d8c68902.png) diff --git a/docs/notes/Java 基础.md b/docs/notes/Java 基础.md index 22d59c03..e7052210 100644 --- a/docs/notes/Java 基础.md +++ b/docs/notes/Java 基础.md @@ -1,652 +1,610 @@ -[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) - -* [一、数据类型](#一数据类型) - * [包装类型](#包装类型) - * [缓存池](#缓存池) -* [二、String](#二string) - * [概览](#概览) - * [不可变的好处](#不可变的好处) - * [String, StringBuffer and StringBuilder](#string,-stringbuffer-and-stringbuilder) - * [String Pool](#string-pool) - * [new String("abc")](#new-string"abc") -* [三、运算](#三运算) - * [参数传递](#参数传递) - * [float 与 double](#float-与-double) - * [隐式类型转换](#隐式类型转换) - * [switch](#switch) -* [四、继承](#四继承) - * [访问权限](#访问权限) - * [抽象类与接口](#抽象类与接口) - * [super](#super) - * [重写与重载](#重写与重载) -* [五、Object 通用方法](#五object-通用方法) - * [概览](#概览) - * [equals()](#equals) - * [hashCode()](#hashcode) - * [toString()](#tostring) - * [clone()](#clone) -* [六、关键字](#六关键字) - * [final](#final) - * [static](#static) -* [七、反射](#七反射) -* [八、异常](#八异常) -* [九、泛型](#九泛型) -* [十、注解](#十注解) -* [十一、特性](#十一特性) - * [Java 各版本的新特性](#java-各版本的新特性) - * [Java 与 C++ 的区别](#java-与-c-的区别) - * [JRE or JDK](#jre-or-jdk) -* [参考资料](#参考资料) - +# 一、数据类型 - -# 一、数据类型 - -## 包装类型 +## 包装类型 八个基本类型: -- boolean/1 -- byte/8 -- char/16 -- short/16 -- int/32 -- float/32 -- long/64 -- double/64 +- boolean/1 +- byte/8 +- char/16 +- short/16 +- int/32 +- float/32 +- long/64 +- double/64 基本类型都有对应的包装类型,基本类型与其对应的包装类型之间的赋值使用自动装箱与拆箱完成。 ```java -Integer x = 2; // 装箱 -int y = x; // 拆箱 +Integer x = 2;     // 装箱 +int y = x;         // 拆箱 ``` -## 缓存池 +## 缓存池 -new Integer(123) 与 Integer.valueOf(123) 的区别在于: +new Integer(123) 与 Integer.valueOf(123) 的区别在于: -- new Integer(123) 每次都会新建一个对象; -- Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用。 +- new Integer(123) 每次都会新建一个对象; +- Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用。 ```java -Integer x = new Integer(123); -Integer y = new Integer(123); -System.out.println(x == y); // false -Integer z = Integer.valueOf(123); -Integer k = Integer.valueOf(123); -System.out.println(z == k); // true +Integer x = new Integer(123); +Integer y = new Integer(123); +System.out.println(x == y);    // false +Integer z = Integer.valueOf(123); +Integer k = Integer.valueOf(123); +System.out.println(z == k);   // true ``` -valueOf() 方法的实现比较简单,就是先判断值是否在缓存池中,如果在的话就直接返回缓存池的内容。 +valueOf() 方法的实现比较简单,就是先判断值是否在缓存池中,如果在的话就直接返回缓存池的内容。 ```java -public static Integer valueOf(int i) { - if (i >= IntegerCache.low && i <= IntegerCache.high) - return IntegerCache.cache[i + (-IntegerCache.low)]; - return new Integer(i); +public static Integer valueOf(int i) { +    if (i >= IntegerCache.low && i <= IntegerCache.high) +        return IntegerCache.cache[i + (-IntegerCache.low)]; +    return new Integer(i); } ``` -在 Java 8 中,Integer 缓存池的大小默认为 -128\~127。 +在 Java 8 中,Integer 缓存池的大小默认为 -128~127。 ```java -static final int low = -128; -static final int high; -static final Integer cache[]; +static final int low = -128; +static final int high; +static final Integer cache[]; -static { - // high value may be configured by property - int h = 127; - String integerCacheHighPropValue = - sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); - if (integerCacheHighPropValue != null) { - try { - int i = parseInt(integerCacheHighPropValue); - i = Math.max(i, 127); - // Maximum array size is Integer.MAX_VALUE - h = Math.min(i, Integer.MAX_VALUE - (-low) -1); - } catch( NumberFormatException nfe) { - // If the property cannot be parsed into an int, ignore it. - } - } - high = h; +static { +    // high value may be configured by property +    int h = 127; +    String integerCacheHighPropValue = +        sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); +    if (integerCacheHighPropValue != null) { +        try { +            int i = parseInt(integerCacheHighPropValue); +            i = Math.max(i, 127); +            // Maximum array size is Integer.MAX_VALUE +            h = Math.min(i, Integer.MAX_VALUE - (-low) -1); +        } catch( NumberFormatException nfe) { +            // If the property cannot be parsed into an int, ignore it. +        } +    } +    high = h; - cache = new Integer[(high - low) + 1]; - int j = low; - for(int k = 0; k < cache.length; k++) - cache[k] = new Integer(j++); +    cache = new Integer[(high - low) + 1]; +    int j = low; +    for(int k = 0; k < cache.length; k++) +        cache[k] = new Integer(j++); - // range [-128, 127] must be interned (JLS7 5.1.7) - assert IntegerCache.high >= 127; +    // range [-128, 127] must be interned (JLS7 5.1.7) +    assert IntegerCache.high >= 127; } ``` -编译器会在自动装箱过程调用 valueOf() 方法,因此多个 Integer 实例使用自动装箱来创建并且值相同,那么就会引用相同的对象。 +编译器会在自动装箱过程调用 valueOf() 方法,因此多个 Integer 实例使用自动装箱来创建并且值相同,那么就会引用相同的对象。 ```java -Integer m = 123; -Integer n = 123; -System.out.println(m == n); // true +Integer m = 123; +Integer n = 123; +System.out.println(m == n); // true ``` 基本类型对应的缓冲池如下: -- boolean values true and false -- all byte values -- short values between -128 and 127 -- int values between -128 and 127 -- char in the range \u0000 to \u007F +- boolean values true and false +- all byte values +- short values between -128 and 127 +- int values between -128 and 127 +- char in the range \u0000 to \u007F 在使用这些基本类型对应的包装类型时,就可以直接使用缓冲池中的对象。 -[StackOverflow : Differences between new Integer(123), Integer.valueOf(123) and just 123 +[StackOverflow : Differences between new Integer(123), Integer.valueOf(123) and just 123 ](https://stackoverflow.com/questions/9030817/differences-between-new-integer123-integer-valueof123-and-just-123) -# 二、String +# 二、String -## 概览 +## 概览 -String 被声明为 final,因此它不可被继承。 +String 被声明为 final,因此它不可被继承。 -在 Java 8 中,String 内部使用 char 数组存储数据。 +在 Java 8 中,String 内部使用 char 数组存储数据。 ```java -public final class String - implements java.io.Serializable, Comparable, CharSequence { - /** The value is used for character storage. */ - private final char value[]; +public final class String +    implements java.io.Serializable, Comparable, CharSequence { +    /** The value is used for character storage. */ +    private final char value[]; } ``` -在 Java 9 之后,String 类的实现改用 byte 数组存储字符串,同时使用 `coder` 来标识使用了哪种编码。 +在 Java 9 之后,String 类的实现改用 byte 数组存储字符串,同时使用 `coder` 来标识使用了哪种编码。 ```java -public final class String - implements java.io.Serializable, Comparable, CharSequence { - /** The value is used for character storage. */ - private final byte[] value; +public final class String +    implements java.io.Serializable, Comparable, CharSequence { +    /** The value is used for character storage. */ +    private final byte[] value; - /** The identifier of the encoding used to encode the bytes in {@code value}. */ - private final byte coder; +    /** The identifier of the encoding used to encode the bytes in {@code value}. */ +    private final byte coder; } ``` -value 数组被声明为 final,这意味着 value 数组初始化之后就不能再引用其它数组。并且 String 内部没有改变 value 数组的方法,因此可以保证 String 不可变。 +value 数组被声明为 final,这意味着 value 数组初始化之后就不能再引用其它数组。并且 String 内部没有改变 value 数组的方法,因此可以保证 String 不可变。 -## 不可变的好处 +## 不可变的好处 -**1. 可以缓存 hash 值** +**1. 可以缓存 hash 值** -因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。 +因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。 -**2. String Pool 的需要** +**2. String Pool 的需要** -如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。 +如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。 -

+ -**3. 安全性** +**3. 安全性** -String 经常作为参数,String 不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 对象的那一方以为现在连接的是其它主机,而实际情况却不一定是。 +String 经常作为参数,String 不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 对象的那一方以为现在连接的是其它主机,而实际情况却不一定是。 -**4. 线程安全** +**4. 线程安全** -String 不可变性天生具备线程安全,可以在多个线程中安全地使用。 +String 不可变性天生具备线程安全,可以在多个线程中安全地使用。 -[Program Creek : Why String is immutable in Java?](https://www.programcreek.com/2013/04/why-string-is-immutable-in-java/) +[Program Creek : Why String is immutable in Java?](https://www.programcreek.com/2013/04/why-string-is-immutable-in-java/) -## String, StringBuffer and StringBuilder +## String, StringBuffer and StringBuilder -**1. 可变性** +**1. 可变性** -- String 不可变 -- StringBuffer 和 StringBuilder 可变 +- String 不可变 +- StringBuffer 和 StringBuilder 可变 -**2. 线程安全** +**2. 线程安全** -- String 不可变,因此是线程安全的 -- StringBuilder 不是线程安全的 -- StringBuffer 是线程安全的,内部使用 synchronized 进行同步 +- String 不可变,因此是线程安全的 +- StringBuilder 不是线程安全的 +- StringBuffer 是线程安全的,内部使用 synchronized 进行同步 -[StackOverflow : String, StringBuffer, and StringBuilder](https://stackoverflow.com/questions/2971315/string-stringbuffer-and-stringbuilder) +[StackOverflow : String, StringBuffer, and StringBuilder](https://stackoverflow.com/questions/2971315/string-stringbuffer-and-stringbuilder) -## String Pool +## String Pool -字符串常量池(String Pool)保存着所有字符串字面量(literal strings),这些字面量在编译时期就确定。不仅如此,还可以使用 String 的 intern() 方法在运行过程中将字符串添加到 String Pool 中。 +字符串常量池(String Pool)保存着所有字符串字面量(literal strings),这些字面量在编译时期就确定。不仅如此,还可以使用 String 的 intern() 方法在运行过程中将字符串添加到 String Pool 中。 -当一个字符串调用 intern() 方法时,如果 String Pool 中已经存在一个字符串和该字符串值相等(使用 equals() 方法进行确定),那么就会返回 String Pool 中字符串的引用;否则,就会在 String Pool 中添加一个新的字符串,并返回这个新字符串的引用。 +当一个字符串调用 intern() 方法时,如果 String Pool 中已经存在一个字符串和该字符串值相等(使用 equals() 方法进行确定),那么就会返回 String Pool 中字符串的引用;否则,就会在 String Pool 中添加一个新的字符串,并返回这个新字符串的引用。 -下面示例中,s1 和 s2 采用 new String() 的方式新建了两个不同字符串,而 s3 和 s4 是通过 s1.intern() 方法取得一个字符串引用。intern() 首先把 s1 引用的字符串放到 String Pool 中,然后返回这个字符串引用。因此 s3 和 s4 引用的是同一个字符串。 +下面示例中,s1 和 s2 采用 new String() 的方式新建了两个不同字符串,而 s3 和 s4 是通过 s1.intern() 方法取得一个字符串引用。intern() 首先把 s1 引用的字符串放到 String Pool 中,然后返回这个字符串引用。因此 s3 和 s4 引用的是同一个字符串。 ```java -String s1 = new String("aaa"); -String s2 = new String("aaa"); -System.out.println(s1 == s2); // false -String s3 = s1.intern(); -String s4 = s1.intern(); -System.out.println(s3 == s4); // true +String s1 = new String("aaa"); +String s2 = new String("aaa"); +System.out.println(s1 == s2);           // false +String s3 = s1.intern(); +String s4 = s1.intern(); +System.out.println(s3 == s4);           // true ``` -如果是采用 "bbb" 这种字面量的形式创建字符串,会自动地将字符串放入 String Pool 中。 +如果是采用 "bbb" 这种字面量的形式创建字符串,会自动地将字符串放入 String Pool 中。 ```java -String s5 = "bbb"; -String s6 = "bbb"; -System.out.println(s5 == s6); // true +String s5 = "bbb"; +String s6 = "bbb"; +System.out.println(s5 == s6);  // true ``` -在 Java 7 之前,String Pool 被放在运行时常量池中,它属于永久代。而在 Java 7,String Pool 被移到堆中。这是因为永久代的空间有限,在大量使用字符串的场景下会导致 OutOfMemoryError 错误。 +在 Java 7 之前,String Pool 被放在运行时常量池中,它属于永久代。而在 Java 7,String Pool 被移到堆中。这是因为永久代的空间有限,在大量使用字符串的场景下会导致 OutOfMemoryError 错误。 -- [StackOverflow : What is String interning?](https://stackoverflow.com/questions/10578984/what-is-string-interning) -- [深入解析 String#intern](https://tech.meituan.com/in_depth_understanding_string_intern.html) +- [StackOverflow : What is String interning?](https://stackoverflow.com/questions/10578984/what-is-string-interning) +- [深入解析 String#intern](https://tech.meituan.com/in_depth_understanding_string_intern.html) -## new String("abc") +## new String("abc") -使用这种方式一共会创建两个字符串对象(前提是 String Pool 中还没有 "abc" 字符串对象)。 +使用这种方式一共会创建两个字符串对象(前提是 String Pool 中还没有 "abc" 字符串对象)。 -- "abc" 属于字符串字面量,因此编译时期会在 String Pool 中创建一个字符串对象,指向这个 "abc" 字符串字面量; -- 而使用 new 的方式会在堆中创建一个字符串对象。 +- "abc" 属于字符串字面量,因此编译时期会在 String Pool 中创建一个字符串对象,指向这个 "abc" 字符串字面量; +- 而使用 new 的方式会在堆中创建一个字符串对象。 -创建一个测试类,其 main 方法中使用这种方式来创建字符串对象。 +创建一个测试类,其 main 方法中使用这种方式来创建字符串对象。 ```java -public class NewStringTest { - public static void main(String[] args) { - String s = new String("abc"); - } +public class NewStringTest { +    public static void main(String[] args) { +        String s = new String("abc"); +    } } ``` -使用 javap -verbose 进行反编译,得到以下内容: +使用 javap -verbose 进行反编译,得到以下内容: ```java -// ... -Constant pool: -// ... - #2 = Class #18 // java/lang/String - #3 = String #19 // abc -// ... - #18 = Utf8 java/lang/String - #19 = Utf8 abc -// ... +// ... +Constant pool: +// ... +   #2 = Class              #18            // java/lang/String +   #3 = String             #19            // abc +// ... +  #18 = Utf8               java/lang/String +  #19 = Utf8               abc +// ... - public static void main(java.lang.String[]); - descriptor: ([Ljava/lang/String;)V - flags: ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=2, args_size=1 - 0: new #2 // class java/lang/String - 3: dup - 4: ldc #3 // String abc - 6: invokespecial #4 // Method java/lang/String."":(Ljava/lang/String;)V - 9: astore_1 -// ... +  public static void main(java.lang.String[]); +    descriptor: ([Ljava/lang/String;)V +    flags: ACC_PUBLIC, ACC_STATIC +    Code: +      stack=3, locals=2, args_size=1 +         0: new           #2                  // class java/lang/String +         3: dup +         4: ldc           #3                  // String abc +         6: invokespecial #4                  // Method java/lang/String."":(Ljava/lang/String;)V +         9: astore_1 +// ... ``` -在 Constant Pool 中,#19 存储这字符串字面量 "abc",#3 是 String Pool 的字符串对象,它指向 #19 这个字符串字面量。在 main 方法中,0: 行使用 new #2 在堆中创建一个字符串对象,并且使用 ldc #3 将 String Pool 中的字符串对象作为 String 构造函数的参数。 +在 Constant Pool 中,#19 存储这字符串字面量 "abc",#3 是 String Pool 的字符串对象,它指向 #19 这个字符串字面量。在 main 方法中,0: 行使用 new #2 在堆中创建一个字符串对象,并且使用 ldc #3 将 String Pool 中的字符串对象作为 String 构造函数的参数。 -以下是 String 构造函数的源码,可以看到,在将一个字符串对象作为另一个字符串对象的构造函数参数时,并不会完全复制 value 数组内容,而是都会指向同一个 value 数组。 +以下是 String 构造函数的源码,可以看到,在将一个字符串对象作为另一个字符串对象的构造函数参数时,并不会完全复制 value 数组内容,而是都会指向同一个 value 数组。 ```java -public String(String original) { - this.value = original.value; - this.hash = original.hash; +public String(String original) { +    this.value = original.value; +    this.hash = original.hash; } ``` -# 三、运算 +# 三、运算 -## 参数传递 +## 参数传递 -Java 的参数是以值传递的形式传入方法中,而不是引用传递。 +Java 的参数是以值传递的形式传入方法中,而不是引用传递。 -以下代码中 Dog dog 的 dog 是一个指针,存储的是对象的地址。在将一个参数传入一个方法时,本质上是将对象的地址以值的方式传递到形参中。因此在方法中使指针引用其它对象,那么这两个指针此时指向的是完全不同的对象,在一方改变其所指向对象的内容时对另一方没有影响。 +以下代码中 Dog dog 的 dog 是一个指针,存储的是对象的地址。在将一个参数传入一个方法时,本质上是将对象的地址以值的方式传递到形参中。因此在方法中使指针引用其它对象,那么这两个指针此时指向的是完全不同的对象,在一方改变其所指向对象的内容时对另一方没有影响。 ```java -public class Dog { +public class Dog { - String name; +    String name; - Dog(String name) { - this.name = name; - } +    Dog(String name) { +        this.name = name; +    } - String getName() { - return this.name; - } +    String getName() { +        return this.name; +    } - void setName(String name) { - this.name = name; - } +    void setName(String name) { +        this.name = name; +    } - String getObjectAddress() { - return super.toString(); - } +    String getObjectAddress() { +        return super.toString(); +    } } ``` ```java -public class PassByValueExample { - public static void main(String[] args) { - Dog dog = new Dog("A"); - System.out.println(dog.getObjectAddress()); // Dog@4554617c - func(dog); - System.out.println(dog.getObjectAddress()); // Dog@4554617c - System.out.println(dog.getName()); // A - } +public class PassByValueExample { +    public static void main(String[] args) { +        Dog dog = new Dog("A"); +        System.out.println(dog.getObjectAddress()); // Dog@4554617c +        func(dog); +        System.out.println(dog.getObjectAddress()); // Dog@4554617c +        System.out.println(dog.getName());          // A +    } - private static void func(Dog dog) { - System.out.println(dog.getObjectAddress()); // Dog@4554617c - dog = new Dog("B"); - System.out.println(dog.getObjectAddress()); // Dog@74a14482 - System.out.println(dog.getName()); // B - } +    private static void func(Dog dog) { +        System.out.println(dog.getObjectAddress()); // Dog@4554617c +        dog = new Dog("B"); +        System.out.println(dog.getObjectAddress()); // Dog@74a14482 +        System.out.println(dog.getName());          // B +    } } ``` 如果在方法中改变对象的字段值会改变原对象该字段值,因为改变的是同一个地址指向的内容。 ```java -class PassByValueExample { - public static void main(String[] args) { - Dog dog = new Dog("A"); - func(dog); - System.out.println(dog.getName()); // B - } +class PassByValueExample { +    public static void main(String[] args) { +        Dog dog = new Dog("A"); +        func(dog); +        System.out.println(dog.getName());          // B +    } - private static void func(Dog dog) { - dog.setName("B"); - } +    private static void func(Dog dog) { +        dog.setName("B"); +    } } ``` -[StackOverflow: Is Java “pass-by-reference” or “pass-by-value”?](https://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value) +[StackOverflow: Is Java “pass-by-reference” or “pass-by-value”?](https://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value) -## float 与 double +## float 与 double -Java 不能隐式执行向下转型,因为这会使得精度降低。 +Java 不能隐式执行向下转型,因为这会使得精度降低。 -1.1 字面量属于 double 类型,不能直接将 1.1 直接赋值给 float 变量,因为这是向下转型。 +1.1 字面量属于 double 类型,不能直接将 1.1 直接赋值给 float 变量,因为这是向下转型。 ```java -// float f = 1.1; +// float f = 1.1; ``` -1.1f 字面量才是 float 类型。 +1.1f 字面量才是 float 类型。 ```java -float f = 1.1f; +float f = 1.1f; ``` -## 隐式类型转换 +## 隐式类型转换 -因为字面量 1 是 int 类型,它比 short 类型精度要高,因此不能隐式地将 int 类型下转型为 short 类型。 +因为字面量 1 是 int 类型,它比 short 类型精度要高,因此不能隐式地将 int 类型下转型为 short 类型。 ```java -short s1 = 1; -// s1 = s1 + 1; +short s1 = 1; +// s1 = s1 + 1; ``` -但是使用 += 或者 ++ 运算符可以执行隐式类型转换。 +但是使用 += 或者 ++ 运算符可以执行隐式类型转换。 ```java -s1 += 1; -// s1++; +s1 += 1; +// s1++; ``` -上面的语句相当于将 s1 + 1 的计算结果进行了向下转型: +上面的语句相当于将 s1 + 1 的计算结果进行了向下转型: ```java -s1 = (short) (s1 + 1); +s1 = (short) (s1 + 1); ``` -[StackOverflow : Why don't Java's +=, -=, *=, /= compound assignment operators require casting?](https://stackoverflow.com/questions/8710619/why-dont-javas-compound-assignment-operators-require-casting) +[StackOverflow : Why don't Java's +=, -=, *=, /= compound assignment operators require casting?](https://stackoverflow.com/questions/8710619/why-dont-javas-compound-assignment-operators-require-casting) -## switch +## switch -从 Java 7 开始,可以在 switch 条件判断语句中使用 String 对象。 +从 Java 7 开始,可以在 switch 条件判断语句中使用 String 对象。 ```java -String s = "a"; -switch (s) { - case "a": - System.out.println("aaa"); - break; - case "b": - System.out.println("bbb"); - break; +String s = "a"; +switch (s) { +    case "a": +        System.out.println("aaa"); +        break; +    case "b": +        System.out.println("bbb"); +        break; } ``` -switch 不支持 long,是因为 switch 的设计初衷是对那些只有少数的几个值进行等值判断,如果值过于复杂,那么还是用 if 比较合适。 +switch 不支持 long,是因为 switch 的设计初衷是对那些只有少数的几个值进行等值判断,如果值过于复杂,那么还是用 if 比较合适。 ```java -// long x = 111; -// switch (x) { // Incompatible types. Found: 'long', required: 'char, byte, short, int, Character, Byte, Short, Integer, String, or an enum' -// case 111: -// System.out.println(111); -// break; -// case 222: -// System.out.println(222); -// break; -// } +// long x = 111; +// switch (x) { // Incompatible types. Found: 'long', required: 'char, byte, short, int, Character, Byte, Short, Integer, String, or an enum' +//     case 111: +//         System.out.println(111); +//         break; +//     case 222: +//         System.out.println(222); +//         break; +// } ``` -[StackOverflow : Why can't your switch statement data type be long, Java?](https://stackoverflow.com/questions/2676210/why-cant-your-switch-statement-data-type-be-long-java) +[StackOverflow : Why can't your switch statement data type be long, Java?](https://stackoverflow.com/questions/2676210/why-cant-your-switch-statement-data-type-be-long-java) -# 四、继承 +# 四、继承 -## 访问权限 +## 访问权限 -Java 中有三个访问权限修饰符:private、protected 以及 public,如果不加访问修饰符,表示包级可见。 +Java 中有三个访问权限修饰符:private、protected 以及 public,如果不加访问修饰符,表示包级可见。 可以对类或类中的成员(字段以及方法)加上访问修饰符。 -- 类可见表示其它类可以用这个类创建实例对象。 -- 成员可见表示其它类可以用这个类的实例对象访问到该成员; +- 类可见表示其它类可以用这个类创建实例对象。 +- 成员可见表示其它类可以用这个类的实例对象访问到该成员; -protected 用于修饰成员,表示在继承体系中成员对于子类可见,但是这个访问修饰符对于类没有意义。 +protected 用于修饰成员,表示在继承体系中成员对于子类可见,但是这个访问修饰符对于类没有意义。 -设计良好的模块会隐藏所有的实现细节,把它的 API 与它的实现清晰地隔离开来。模块之间只通过它们的 API 进行通信,一个模块不需要知道其他模块的内部工作情况,这个概念被称为信息隐藏或封装。因此访问权限应当尽可能地使每个类或者成员不被外界访问。 +设计良好的模块会隐藏所有的实现细节,把它的 API 与它的实现清晰地隔离开来。模块之间只通过它们的 API 进行通信,一个模块不需要知道其他模块的内部工作情况,这个概念被称为信息隐藏或封装。因此访问权限应当尽可能地使每个类或者成员不被外界访问。 如果子类的方法重写了父类的方法,那么子类中该方法的访问级别不允许低于父类的访问级别。这是为了确保可以使用父类实例的地方都可以使用子类实例,也就是确保满足里氏替换原则。 -字段决不能是公有的,因为这么做的话就失去了对这个字段修改行为的控制,客户端可以对其随意修改。例如下面的例子中,AccessExample 拥有 id 公有字段,如果在某个时刻,我们想要使用 int 存储 id 字段,那么就需要修改所有的客户端代码。 +字段决不能是公有的,因为这么做的话就失去了对这个字段修改行为的控制,客户端可以对其随意修改。例如下面的例子中,AccessExample 拥有 id 公有字段,如果在某个时刻,我们想要使用 int 存储 id 字段,那么就需要修改所有的客户端代码。 ```java -public class AccessExample { - public String id; +public class AccessExample { +    public String id; } ``` -可以使用公有的 getter 和 setter 方法来替换公有字段,这样的话就可以控制对字段的修改行为。 +可以使用公有的 getter 和 setter 方法来替换公有字段,这样的话就可以控制对字段的修改行为。 ```java -public class AccessExample { +public class AccessExample { - private int id; +    private int id; - public String getId() { - return id + ""; - } +    public String getId() { +        return id + ""; +    } - public void setId(String id) { - this.id = Integer.valueOf(id); - } +    public void setId(String id) { +        this.id = Integer.valueOf(id); +    } } ``` 但是也有例外,如果是包级私有的类或者私有的嵌套类,那么直接暴露成员不会有特别大的影响。 ```java -public class AccessWithInnerClassExample { +public class AccessWithInnerClassExample { - private class InnerClass { - int x; - } +    private class InnerClass { +        int x; +    } - private InnerClass innerClass; +    private InnerClass innerClass; - public AccessWithInnerClassExample() { - innerClass = new InnerClass(); - } +    public AccessWithInnerClassExample() { +        innerClass = new InnerClass(); +    } - public int getValue() { - return innerClass.x; // 直接访问 - } +    public int getValue() { +        return innerClass.x;  // 直接访问 +    } } ``` -## 抽象类与接口 +## 抽象类与接口 -**1. 抽象类** +**1. 抽象类** -抽象类和抽象方法都使用 abstract 关键字进行声明。抽象类一般会包含抽象方法,抽象方法一定位于抽象类中。 +抽象类和抽象方法都使用 abstract 关键字进行声明。抽象类一般会包含抽象方法,抽象方法一定位于抽象类中。 抽象类和普通类最大的区别是,抽象类不能被实例化,需要继承抽象类才能实例化其子类。 ```java -public abstract class AbstractClassExample { +public abstract class AbstractClassExample { - protected int x; - private int y; +    protected int x; +    private int y; - public abstract void func1(); +    public abstract void func1(); - public void func2() { - System.out.println("func2"); - } +    public void func2() { +        System.out.println("func2"); +    } } ``` ```java -public class AbstractExtendClassExample extends AbstractClassExample { - @Override - public void func1() { - System.out.println("func1"); - } +public class AbstractExtendClassExample extends AbstractClassExample { +    @Override +    public void func1() { +        System.out.println("func1"); +    } } ``` ```java -// AbstractClassExample ac1 = new AbstractClassExample(); // 'AbstractClassExample' is abstract; cannot be instantiated -AbstractClassExample ac2 = new AbstractExtendClassExample(); +// AbstractClassExample ac1 = new AbstractClassExample(); // 'AbstractClassExample' is abstract; cannot be instantiated +AbstractClassExample ac2 = new AbstractExtendClassExample(); ac2.func1(); ``` -**2. 接口** +**2. 接口** -接口是抽象类的延伸,在 Java 8 之前,它可以看成是一个完全抽象的类,也就是说它不能有任何的方法实现。 +接口是抽象类的延伸,在 Java 8 之前,它可以看成是一个完全抽象的类,也就是说它不能有任何的方法实现。 -从 Java 8 开始,接口也可以拥有默认的方法实现,这是因为不支持默认方法的接口的维护成本太高了。在 Java 8 之前,如果一个接口想要添加新的方法,那么要修改所有实现了该接口的类。 +从 Java 8 开始,接口也可以拥有默认的方法实现,这是因为不支持默认方法的接口的维护成本太高了。在 Java 8 之前,如果一个接口想要添加新的方法,那么要修改所有实现了该接口的类。 -接口的成员(字段 + 方法)默认都是 public 的,并且不允许定义为 private 或者 protected。 +接口的成员(字段 + 方法)默认都是 public 的,并且不允许定义为 private 或者 protected。 -接口的字段默认都是 static 和 final 的。 +接口的字段默认都是 static 和 final 的。 ```java -public interface InterfaceExample { +public interface InterfaceExample { - void func1(); +    void func1(); - default void func2(){ - System.out.println("func2"); - } +    default void func2(){ +        System.out.println("func2"); +    } - int x = 123; - // int y; // Variable 'y' might not have been initialized - public int z = 0; // Modifier 'public' is redundant for interface fields - // private int k = 0; // Modifier 'private' not allowed here - // protected int l = 0; // Modifier 'protected' not allowed here - // private void fun3(); // Modifier 'private' not allowed here +    int x = 123; +    // int y;               // Variable 'y' might not have been initialized +    public int z = 0;       // Modifier 'public' is redundant for interface fields +    // private int k = 0;   // Modifier 'private' not allowed here +    // protected int l = 0; // Modifier 'protected' not allowed here +    // private void fun3(); // Modifier 'private' not allowed here } ``` ```java -public class InterfaceImplementExample implements InterfaceExample { - @Override - public void func1() { - System.out.println("func1"); - } +public class InterfaceImplementExample implements InterfaceExample { +    @Override +    public void func1() { +        System.out.println("func1"); +    } } ``` ```java -// InterfaceExample ie1 = new InterfaceExample(); // 'InterfaceExample' is abstract; cannot be instantiated -InterfaceExample ie2 = new InterfaceImplementExample(); +// InterfaceExample ie1 = new InterfaceExample(); // 'InterfaceExample' is abstract; cannot be instantiated +InterfaceExample ie2 = new InterfaceImplementExample(); ie2.func1(); System.out.println(InterfaceExample.x); ``` -**3. 比较** +**3. 比较** -- 从设计层面上看,抽象类提供了一种 IS-A 关系,那么就必须满足里式替换原则,即子类对象必须能够替换掉所有父类对象。而接口更像是一种 LIKE-A 关系,它只是提供一种方法实现契约,并不要求接口和实现接口的类具有 IS-A 关系。 -- 从使用上来看,一个类可以实现多个接口,但是不能继承多个抽象类。 -- 接口的字段只能是 static 和 final 类型的,而抽象类的字段没有这种限制。 -- 接口的成员只能是 public 的,而抽象类的成员可以有多种访问权限。 +- 从设计层面上看,抽象类提供了一种 IS-A 关系,那么就必须满足里式替换原则,即子类对象必须能够替换掉所有父类对象。而接口更像是一种 LIKE-A 关系,它只是提供一种方法实现契约,并不要求接口和实现接口的类具有 IS-A 关系。 +- 从使用上来看,一个类可以实现多个接口,但是不能继承多个抽象类。 +- 接口的字段只能是 static 和 final 类型的,而抽象类的字段没有这种限制。 +- 接口的成员只能是 public 的,而抽象类的成员可以有多种访问权限。 -**4. 使用选择** +**4. 使用选择** 使用接口: -- 需要让不相关的类都实现一个方法,例如不相关的类都可以实现 Compareable 接口中的 compareTo() 方法; -- 需要使用多重继承。 +- 需要让不相关的类都实现一个方法,例如不相关的类都可以实现 Compareable 接口中的 compareTo() 方法; +- 需要使用多重继承。 使用抽象类: -- 需要在几个相关的类中共享代码。 -- 需要能控制继承来的成员的访问权限,而不是都为 public。 -- 需要继承非静态和非常量字段。 +- 需要在几个相关的类中共享代码。 +- 需要能控制继承来的成员的访问权限,而不是都为 public。 +- 需要继承非静态和非常量字段。 -在很多情况下,接口优先于抽象类。因为接口没有抽象类严格的类层次结构要求,可以灵活地为一个类添加行为。并且从 Java 8 开始,接口也可以有默认的方法实现,使得修改接口的成本也变的很低。 +在很多情况下,接口优先于抽象类。因为接口没有抽象类严格的类层次结构要求,可以灵活地为一个类添加行为。并且从 Java 8 开始,接口也可以有默认的方法实现,使得修改接口的成本也变的很低。 -- [深入理解 abstract class 和 interface](https://www.ibm.com/developerworks/cn/java/l-javainterface-abstract/) -- [When to Use Abstract Class and Interface](https://dzone.com/articles/when-to-use-abstract-class-and-intreface) +- [深入理解 abstract class 和 interface](https://www.ibm.com/developerworks/cn/java/l-javainterface-abstract/) +- [When to Use Abstract Class and Interface](https://dzone.com/articles/when-to-use-abstract-class-and-intreface) -## super +## super -- 访问父类的构造函数:可以使用 super() 函数访问父类的构造函数,从而委托父类完成一些初始化的工作。 -- 访问父类的成员:如果子类重写了父类的某个方法,可以通过使用 super 关键字来引用父类的方法实现。 +- 访问父类的构造函数:可以使用 super() 函数访问父类的构造函数,从而委托父类完成一些初始化的工作。 +- 访问父类的成员:如果子类重写了父类的某个方法,可以通过使用 super 关键字来引用父类的方法实现。 ```java -public class SuperExample { +public class SuperExample { - protected int x; - protected int y; +    protected int x; +    protected int y; - public SuperExample(int x, int y) { - this.x = x; - this.y = y; - } +    public SuperExample(int x, int y) { +        this.x = x; +        this.y = y; +    } - public void func() { - System.out.println("SuperExample.func()"); - } +    public void func() { +        System.out.println("SuperExample.func()"); +    } } ``` ```java -public class SuperExtendExample extends SuperExample { +public class SuperExtendExample extends SuperExample { - private int z; +    private int z; - public SuperExtendExample(int x, int y, int z) { - super(x, y); - this.z = z; - } +    public SuperExtendExample(int x, int y, int z) { +        super(x, y); +        this.z = z; +    } - @Override - public void func() { - super.func(); - System.out.println("SuperExtendExample.func()"); - } +    @Override +    public void func() { +        super.func(); +        System.out.println("SuperExtendExample.func()"); +    } } ``` ```java -SuperExample e = new SuperExtendExample(1, 2, 3); +SuperExample e = new SuperExtendExample(1, 2, 3); e.func(); ``` @@ -655,191 +613,191 @@ SuperExample.func() SuperExtendExample.func() ``` -[Using the Keyword super](https://docs.oracle.com/javase/tutorial/java/IandI/super.html) +[Using the Keyword super](https://docs.oracle.com/javase/tutorial/java/IandI/super.html) -## 重写与重载 +## 重写与重载 -**1. 重写(Override)** +**1. 重写(Override)** 存在于继承体系中,指子类实现了一个与父类在方法声明上完全相同的一个方法。 为了满足里式替换原则,重写有有以下两个限制: -- 子类方法的访问权限必须大于等于父类方法; -- 子类方法的返回类型必须是父类方法返回类型或为其子类型。 +- 子类方法的访问权限必须大于等于父类方法; +- 子类方法的返回类型必须是父类方法返回类型或为其子类型。 -使用 @Override 注解,可以让编译器帮忙检查是否满足上面的两个限制条件。 +使用 @Override 注解,可以让编译器帮忙检查是否满足上面的两个限制条件。 -**2. 重载(Overload)** +**2. 重载(Overload)** 存在于同一个类中,指一个方法与已经存在的方法名称上相同,但是参数类型、个数、顺序至少有一个不同。 应该注意的是,返回值不同,其它都相同不算是重载。 -# 五、Object 通用方法 +# 五、Object 通用方法 -## 概览 +## 概览 ```java -public native int hashCode() +public native int hashCode() -public boolean equals(Object obj) +public boolean equals(Object obj) -protected native Object clone() throws CloneNotSupportedException +protected native Object clone() throws CloneNotSupportedException -public String toString() +public String toString() -public final native Class getClass() +public final native Class getClass() -protected void finalize() throws Throwable {} +protected void finalize() throws Throwable {} -public final native void notify() +public final native void notify() -public final native void notifyAll() +public final native void notifyAll() -public final native void wait(long timeout) throws InterruptedException +public final native void wait(long timeout) throws InterruptedException -public final void wait(long timeout, int nanos) throws InterruptedException +public final void wait(long timeout, int nanos) throws InterruptedException -public final void wait() throws InterruptedException +public final void wait() throws InterruptedException ``` -## equals() +## equals() -**1. 等价关系** +**1. 等价关系** -Ⅰ 自反性 +Ⅰ 自反性 ```java -x.equals(x); // true +x.equals(x); // true ``` -Ⅱ 对称性 +Ⅱ 对称性 ```java -x.equals(y) == y.equals(x); // true +x.equals(y) == y.equals(x); // true ``` -Ⅲ 传递性 +Ⅲ 传递性 ```java -if (x.equals(y) && y.equals(z)) - x.equals(z); // true; +if (x.equals(y) && y.equals(z)) +    x.equals(z); // true; ``` -Ⅳ 一致性 +Ⅳ 一致性 -多次调用 equals() 方法结果不变 +多次调用 equals() 方法结果不变 ```java -x.equals(y) == x.equals(y); // true +x.equals(y) == x.equals(y); // true ``` -Ⅴ 与 null 的比较 +Ⅴ 与 null 的比较 -对任何不是 null 的对象 x 调用 x.equals(null) 结果都为 false +对任何不是 null 的对象 x 调用 x.equals(null) 结果都为 false ```java -x.equals(null); // false; +x.equals(null); // false; ``` -**2. 等价与相等** +**2. 等价与相等** -- 对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。 -- 对于引用类型,== 判断两个变量是否引用同一个对象,而 equals() 判断引用的对象是否等价。 +- 对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。 +- 对于引用类型,== 判断两个变量是否引用同一个对象,而 equals() 判断引用的对象是否等价。 ```java -Integer x = new Integer(1); -Integer y = new Integer(1); -System.out.println(x.equals(y)); // true -System.out.println(x == y); // false +Integer x = new Integer(1); +Integer y = new Integer(1); +System.out.println(x.equals(y)); // true +System.out.println(x == y);      // false ``` -**3. 实现** +**3. 实现** -- 检查是否为同一个对象的引用,如果是直接返回 true; -- 检查是否是同一个类型,如果不是,直接返回 false; -- 将 Object 对象进行转型; -- 判断每个关键域是否相等。 +- 检查是否为同一个对象的引用,如果是直接返回 true; +- 检查是否是同一个类型,如果不是,直接返回 false; +- 将 Object 对象进行转型; +- 判断每个关键域是否相等。 ```java -public class EqualExample { +public class EqualExample { - private int x; - private int y; - private int z; +    private int x; +    private int y; +    private int z; - public EqualExample(int x, int y, int z) { - this.x = x; - this.y = y; - this.z = z; - } +    public EqualExample(int x, int y, int z) { +        this.x = x; +        this.y = y; +        this.z = z; +    } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; +    @Override +    public boolean equals(Object o) { +        if (this == o) return true; +        if (o == null || getClass() != o.getClass()) return false; - EqualExample that = (EqualExample) o; +        EqualExample that = (EqualExample) o; - if (x != that.x) return false; - if (y != that.y) return false; - return z == that.z; - } +        if (x != that.x) return false; +        if (y != that.y) return false; +        return z == that.z; +    } } ``` -## hashCode() +## hashCode() -hashCode() 返回散列值,而 equals() 是用来判断两个对象是否等价。等价的两个对象散列值一定相同,但是散列值相同的两个对象不一定等价。 +hashCode() 返回散列值,而 equals() 是用来判断两个对象是否等价。等价的两个对象散列值一定相同,但是散列值相同的两个对象不一定等价。 -在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证等价的两个对象散列值也相等。 +在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证等价的两个对象散列值也相等。 -下面的代码中,新建了两个等价的对象,并将它们添加到 HashSet 中。我们希望将这两个对象当成一样的,只在集合中添加一个对象,但是因为 EqualExample 没有实现 hasCode() 方法,因此这两个对象的散列值是不同的,最终导致集合添加了两个等价的对象。 +下面的代码中,新建了两个等价的对象,并将它们添加到 HashSet 中。我们希望将这两个对象当成一样的,只在集合中添加一个对象,但是因为 EqualExample 没有实现 hasCode() 方法,因此这两个对象的散列值是不同的,最终导致集合添加了两个等价的对象。 ```java -EqualExample e1 = new EqualExample(1, 1, 1); -EqualExample e2 = new EqualExample(1, 1, 1); -System.out.println(e1.equals(e2)); // true -HashSet set = new HashSet<>(); +EqualExample e1 = new EqualExample(1, 1, 1); +EqualExample e2 = new EqualExample(1, 1, 1); +System.out.println(e1.equals(e2)); // true +HashSet set = new HashSet<>(); set.add(e1); set.add(e2); -System.out.println(set.size()); // 2 +System.out.println(set.size());   // 2 ``` -理想的散列函数应当具有均匀性,即不相等的对象应当均匀分布到所有可能的散列值上。这就要求了散列函数要把所有域的值都考虑进来。可以将每个域都当成 R 进制的某一位,然后组成一个 R 进制的整数。R 一般取 31,因为它是一个奇素数,如果是偶数的话,当出现乘法溢出,信息就会丢失,因为与 2 相乘相当于向左移一位。 +理想的散列函数应当具有均匀性,即不相等的对象应当均匀分布到所有可能的散列值上。这就要求了散列函数要把所有域的值都考虑进来。可以将每个域都当成 R 进制的某一位,然后组成一个 R 进制的整数。R 一般取 31,因为它是一个奇素数,如果是偶数的话,当出现乘法溢出,信息就会丢失,因为与 2 相乘相当于向左移一位。 -一个数与 31 相乘可以转换成移位和减法:`31*x == (x<<5)-x`,编译器会自动进行这个优化。 +一个数与 31 相乘可以转换成移位和减法:`31*x == (x<<5)-x`,编译器会自动进行这个优化。 ```java @Override -public int hashCode() { - int result = 17; - result = 31 * result + x; - result = 31 * result + y; - result = 31 * result + z; - return result; +public int hashCode() { +    int result = 17; +    result = 31 * result + x; +    result = 31 * result + y; +    result = 31 * result + z; +    return result; } ``` -## toString() +## toString() -默认返回 ToStringExample@4554617c 这种形式,其中 @ 后面的数值为散列码的无符号十六进制表示。 +默认返回 ToStringExample@4554617c 这种形式,其中 @ 后面的数值为散列码的无符号十六进制表示。 ```java -public class ToStringExample { +public class ToStringExample { - private int number; +    private int number; - public ToStringExample(int number) { - this.number = number; - } +    public ToStringExample(int number) { +        this.number = number; +    } } ``` ```java -ToStringExample example = new ToStringExample(123); +ToStringExample example = new ToStringExample(123); System.out.println(example.toString()); ``` @@ -847,290 +805,290 @@ System.out.println(example.toString()); ToStringExample@4554617c ``` -## clone() +## clone() -**1. cloneable** +**1. cloneable** -clone() 是 Object 的 protected 方法,它不是 public,一个类不显式去重写 clone(),其它类就不能直接去调用该类实例的 clone() 方法。 +clone() 是 Object 的 protected 方法,它不是 public,一个类不显式去重写 clone(),其它类就不能直接去调用该类实例的 clone() 方法。 ```java -public class CloneExample { - private int a; - private int b; +public class CloneExample { +    private int a; +    private int b; } ``` ```java -CloneExample e1 = new CloneExample(); -// CloneExample e2 = e1.clone(); // 'clone()' has protected access in 'java.lang.Object' +CloneExample e1 = new CloneExample(); +// CloneExample e2 = e1.clone(); // 'clone()' has protected access in 'java.lang.Object' ``` -重写 clone() 得到以下实现: +重写 clone() 得到以下实现: ```java -public class CloneExample { - private int a; - private int b; +public class CloneExample { +    private int a; +    private int b; - @Override - public CloneExample clone() throws CloneNotSupportedException { - return (CloneExample)super.clone(); - } +    @Override +    public CloneExample clone() throws CloneNotSupportedException { +        return (CloneExample)super.clone(); +    } } ``` ```java -CloneExample e1 = new CloneExample(); -try { - CloneExample e2 = e1.clone(); -} catch (CloneNotSupportedException e) { - e.printStackTrace(); +CloneExample e1 = new CloneExample(); +try { +    CloneExample e2 = e1.clone(); +} catch (CloneNotSupportedException e) { +    e.printStackTrace(); } ``` ```html -java.lang.CloneNotSupportedException: CloneExample +java.lang.CloneNotSupportedException: CloneExample ``` -以上抛出了 CloneNotSupportedException,这是因为 CloneExample 没有实现 Cloneable 接口。 +以上抛出了 CloneNotSupportedException,这是因为 CloneExample 没有实现 Cloneable 接口。 -应该注意的是,clone() 方法并不是 Cloneable 接口的方法,而是 Object 的一个 protected 方法。Cloneable 接口只是规定,如果一个类没有实现 Cloneable 接口又调用了 clone() 方法,就会抛出 CloneNotSupportedException。 +应该注意的是,clone() 方法并不是 Cloneable 接口的方法,而是 Object 的一个 protected 方法。Cloneable 接口只是规定,如果一个类没有实现 Cloneable 接口又调用了 clone() 方法,就会抛出 CloneNotSupportedException。 ```java -public class CloneExample implements Cloneable { - private int a; - private int b; +public class CloneExample implements Cloneable { +    private int a; +    private int b; - @Override - public Object clone() throws CloneNotSupportedException { - return super.clone(); - } +    @Override +    public Object clone() throws CloneNotSupportedException { +        return super.clone(); +    } } ``` -**2. 浅拷贝** +**2. 浅拷贝** 拷贝对象和原始对象的引用类型引用同一个对象。 ```java -public class ShallowCloneExample implements Cloneable { +public class ShallowCloneExample implements Cloneable { - private int[] arr; +    private int[] arr; - public ShallowCloneExample() { - arr = new int[10]; - for (int i = 0; i < arr.length; i++) { - arr[i] = i; - } - } +    public ShallowCloneExample() { +        arr = new int[10]; +        for (int i = 0; i < arr.length; i++) { +            arr[i] = i; +        } +    } - public void set(int index, int value) { - arr[index] = value; - } +    public void set(int index, int value) { +        arr[index] = value; +    } - public int get(int index) { - return arr[index]; - } +    public int get(int index) { +        return arr[index]; +    } - @Override - protected ShallowCloneExample clone() throws CloneNotSupportedException { - return (ShallowCloneExample) super.clone(); - } +    @Override +    protected ShallowCloneExample clone() throws CloneNotSupportedException { +        return (ShallowCloneExample) super.clone(); +    } } ``` ```java -ShallowCloneExample e1 = new ShallowCloneExample(); -ShallowCloneExample e2 = null; -try { - e2 = e1.clone(); -} catch (CloneNotSupportedException e) { - e.printStackTrace(); +ShallowCloneExample e1 = new ShallowCloneExample(); +ShallowCloneExample e2 = null; +try { +    e2 = e1.clone(); +} catch (CloneNotSupportedException e) { +    e.printStackTrace(); } -e1.set(2, 222); -System.out.println(e2.get(2)); // 222 +e1.set(2, 222); +System.out.println(e2.get(2)); // 222 ``` -**3. 深拷贝** +**3. 深拷贝** 拷贝对象和原始对象的引用类型引用不同对象。 ```java -public class DeepCloneExample implements Cloneable { +public class DeepCloneExample implements Cloneable { - private int[] arr; +    private int[] arr; - public DeepCloneExample() { - arr = new int[10]; - for (int i = 0; i < arr.length; i++) { - arr[i] = i; - } - } +    public DeepCloneExample() { +        arr = new int[10]; +        for (int i = 0; i < arr.length; i++) { +            arr[i] = i; +        } +    } - public void set(int index, int value) { - arr[index] = value; - } +    public void set(int index, int value) { +        arr[index] = value; +    } - public int get(int index) { - return arr[index]; - } +    public int get(int index) { +        return arr[index]; +    } - @Override - protected DeepCloneExample clone() throws CloneNotSupportedException { - DeepCloneExample result = (DeepCloneExample) super.clone(); - result.arr = new int[arr.length]; - for (int i = 0; i < arr.length; i++) { - result.arr[i] = arr[i]; - } - return result; - } +    @Override +    protected DeepCloneExample clone() throws CloneNotSupportedException { +        DeepCloneExample result = (DeepCloneExample) super.clone(); +        result.arr = new int[arr.length]; +        for (int i = 0; i < arr.length; i++) { +            result.arr[i] = arr[i]; +        } +        return result; +    } } ``` ```java -DeepCloneExample e1 = new DeepCloneExample(); -DeepCloneExample e2 = null; -try { - e2 = e1.clone(); -} catch (CloneNotSupportedException e) { - e.printStackTrace(); +DeepCloneExample e1 = new DeepCloneExample(); +DeepCloneExample e2 = null; +try { +    e2 = e1.clone(); +} catch (CloneNotSupportedException e) { +    e.printStackTrace(); } -e1.set(2, 222); -System.out.println(e2.get(2)); // 2 +e1.set(2, 222); +System.out.println(e2.get(2)); // 2 ``` -**4. clone() 的替代方案** +**4. clone() 的替代方案** -使用 clone() 方法来拷贝一个对象即复杂又有风险,它会抛出异常,并且还需要类型转换。Effective Java 书上讲到,最好不要去使用 clone(),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象。 +使用 clone() 方法来拷贝一个对象即复杂又有风险,它会抛出异常,并且还需要类型转换。Effective Java 书上讲到,最好不要去使用 clone(),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象。 ```java -public class CloneConstructorExample { +public class CloneConstructorExample { - private int[] arr; +    private int[] arr; - public CloneConstructorExample() { - arr = new int[10]; - for (int i = 0; i < arr.length; i++) { - arr[i] = i; - } - } +    public CloneConstructorExample() { +        arr = new int[10]; +        for (int i = 0; i < arr.length; i++) { +            arr[i] = i; +        } +    } - public CloneConstructorExample(CloneConstructorExample original) { - arr = new int[original.arr.length]; - for (int i = 0; i < original.arr.length; i++) { - arr[i] = original.arr[i]; - } - } +    public CloneConstructorExample(CloneConstructorExample original) { +        arr = new int[original.arr.length]; +        for (int i = 0; i < original.arr.length; i++) { +            arr[i] = original.arr[i]; +        } +    } - public void set(int index, int value) { - arr[index] = value; - } +    public void set(int index, int value) { +        arr[index] = value; +    } - public int get(int index) { - return arr[index]; - } +    public int get(int index) { +        return arr[index]; +    } } ``` ```java -CloneConstructorExample e1 = new CloneConstructorExample(); -CloneConstructorExample e2 = new CloneConstructorExample(e1); -e1.set(2, 222); -System.out.println(e2.get(2)); // 2 +CloneConstructorExample e1 = new CloneConstructorExample(); +CloneConstructorExample e2 = new CloneConstructorExample(e1); +e1.set(2, 222); +System.out.println(e2.get(2)); // 2 ``` -# 六、关键字 +# 六、关键字 -## final +## final -**1. 数据** +**1. 数据** 声明数据为常量,可以是编译时常量,也可以是在运行时被初始化后不能被改变的常量。 -- 对于基本类型,final 使数值不变; -- 对于引用类型,final 使引用不变,也就不能引用其它对象,但是被引用的对象本身是可以修改的。 +- 对于基本类型,final 使数值不变; +- 对于引用类型,final 使引用不变,也就不能引用其它对象,但是被引用的对象本身是可以修改的。 ```java -final int x = 1; -// x = 2; // cannot assign value to final variable 'x' -final A y = new A(); -y.a = 1; +final int x = 1; +// x = 2;  // cannot assign value to final variable 'x' +final A y = new A(); +y.a = 1; ``` -**2. 方法** +**2. 方法** 声明方法不能被子类重写。 -private 方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是重写基类方法,而是在子类中定义了一个新的方法。 +private 方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是重写基类方法,而是在子类中定义了一个新的方法。 -**3. 类** +**3. 类** 声明类不允许被继承。 -## static +## static -**1. 静态变量** +**1. 静态变量** -- 静态变量:又称为类变量,也就是说这个变量属于类的,类所有的实例都共享静态变量,可以直接通过类名来访问它。静态变量在内存中只存在一份。 -- 实例变量:每创建一个实例就会产生一个实例变量,它与该实例同生共死。 +- 静态变量:又称为类变量,也就是说这个变量属于类的,类所有的实例都共享静态变量,可以直接通过类名来访问它。静态变量在内存中只存在一份。 +- 实例变量:每创建一个实例就会产生一个实例变量,它与该实例同生共死。 ```java -public class A { +public class A { - private int x; // 实例变量 - private static int y; // 静态变量 +    private int x;         // 实例变量 +    private static int y;  // 静态变量 - public static void main(String[] args) { - // int x = A.x; // Non-static field 'x' cannot be referenced from a static context - A a = new A(); - int x = a.x; - int y = A.y; - } +    public static void main(String[] args) { +        // int x = A.x;  // Non-static field 'x' cannot be referenced from a static context +        A a = new A(); +        int x = a.x; +        int y = A.y; +    } } ``` -**2. 静态方法** +**2. 静态方法** 静态方法在类加载的时候就存在了,它不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法。 ```java -public abstract class A { - public static void func1(){ - } - // public abstract static void func2(); // Illegal combination of modifiers: 'abstract' and 'static' +public abstract class A { +    public static void func1(){ +    } +    // public abstract static void func2();  // Illegal combination of modifiers: 'abstract' and 'static' } ``` -只能访问所属类的静态字段和静态方法,方法中不能有 this 和 super 关键字。 +只能访问所属类的静态字段和静态方法,方法中不能有 this 和 super 关键字。 ```java -public class A { +public class A { - private static int x; - private int y; +    private static int x; +    private int y; - public static void func1(){ - int a = x; - // int b = y; // Non-static field 'y' cannot be referenced from a static context - // int b = this.y; // 'A.this' cannot be referenced from a static context - } +    public static void func1(){ +        int a = x; +        // int b = y;  // Non-static field 'y' cannot be referenced from a static context +        // int b = this.y;     // 'A.this' cannot be referenced from a static context +    } } ``` -**3. 静态语句块** +**3. 静态语句块** 静态语句块在类初始化时运行一次。 ```java -public class A { - static { - System.out.println("123"); - } +public class A { +    static { +        System.out.println("123"); +    } - public static void main(String[] args) { - A a1 = new A(); - A a2 = new A(); - } +    public static void main(String[] args) { +        A a1 = new A(); +        A a2 = new A(); +    } } ``` @@ -1138,195 +1096,198 @@ public class A { 123 ``` -**4. 静态内部类** +**4. 静态内部类** 非静态内部类依赖于外部类的实例,而静态内部类不需要。 ```java -public class OuterClass { +public class OuterClass { - class InnerClass { - } +    class InnerClass { +    } - static class StaticInnerClass { - } +    static class StaticInnerClass { +    } - public static void main(String[] args) { - // InnerClass innerClass = new InnerClass(); // 'OuterClass.this' cannot be referenced from a static context - OuterClass outerClass = new OuterClass(); - InnerClass innerClass = outerClass.new InnerClass(); - StaticInnerClass staticInnerClass = new StaticInnerClass(); - } +    public static void main(String[] args) { +        // InnerClass innerClass = new InnerClass(); // 'OuterClass.this' cannot be referenced from a static context +        OuterClass outerClass = new OuterClass(); +        InnerClass innerClass = outerClass.new InnerClass(); +        StaticInnerClass staticInnerClass = new StaticInnerClass(); +    } } ``` 静态内部类不能访问外部类的非静态的变量和方法。 -**5. 静态导包** +**5. 静态导包** -在使用静态变量和方法时不用再指明 ClassName,从而简化代码,但可读性大大降低。 +在使用静态变量和方法时不用再指明 ClassName,从而简化代码,但可读性大大降低。 ```java -import static com.xxx.ClassName.* +import static com.xxx.ClassName.* ``` -**6. 初始化顺序** +**6. 初始化顺序** 静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。 ```java -public static String staticField = "静态变量"; +public static String staticField = "静态变量"; ``` ```java -static { - System.out.println("静态语句块"); +static { +    System.out.println("静态语句块"); } ``` ```java -public String field = "实例变量"; +public String field = "实例变量"; ``` ```java { - System.out.println("普通语句块"); +    System.out.println("普通语句块"); } ``` 最后才是构造函数的初始化。 ```java -public InitialOrderTest() { - System.out.println("构造函数"); +public InitialOrderTest() { +    System.out.println("构造函数"); } ``` 存在继承的情况下,初始化顺序为: -- 父类(静态变量、静态语句块) -- 子类(静态变量、静态语句块) -- 父类(实例变量、普通语句块) -- 父类(构造函数) -- 子类(实例变量、普通语句块) -- 子类(构造函数) +- 父类(静态变量、静态语句块) +- 子类(静态变量、静态语句块) +- 父类(实例变量、普通语句块) +- 父类(构造函数) +- 子类(实例变量、普通语句块) +- 子类(构造函数) -# 七、反射 +# 七、反射 -每个类都有一个 **Class** 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。 +每个类都有一个 **Class** 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。 -类加载相当于 Class 对象的加载,类在第一次使用时才动态加载到 JVM 中。也可以使用 `Class.forName("com.mysql.jdbc.Driver")` 这种方式来控制类的加载,该方法会返回一个 Class 对象。 +类加载相当于 Class 对象的加载,类在第一次使用时才动态加载到 JVM 中。也可以使用 `Class.forName("com.mysql.jdbc.Driver")` 这种方式来控制类的加载,该方法会返回一个 Class 对象。 -反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译时期该类的 .class 不存在也可以加载进来。 +反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译时期该类的 .class 不存在也可以加载进来。 -Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类: +Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类: -- **Field** :可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段; -- **Method** :可以使用 invoke() 方法调用与 Method 对象关联的方法; -- **Constructor** :可以用 Constructor 创建新的对象。 +- **Field**:可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段; +- **Method**:可以使用 invoke() 方法调用与 Method 对象关联的方法; +- **Constructor**:可以用 Constructor 创建新的对象。 -**反射的优点:** +**反射的优点:** -* **可扩展性** :应用程序可以利用全限定名创建可扩展对象的实例,来使用来自外部的用户自定义类。 -* **类浏览器和可视化开发环境** :一个类浏览器需要可以枚举类的成员。可视化开发环境(如 IDE)可以从利用反射中可用的类型信息中受益,以帮助程序员编写正确的代码。 -* **调试器和测试工具** : 调试器需要能够检查一个类里的私有成员。测试工具可以利用反射来自动地调用类里定义的可被发现的 API 定义,以确保一组测试中有较高的代码覆盖率。 +*   **可扩展性** :应用程序可以利用全限定名创建可扩展对象的实例,来使用来自外部的用户自定义类。 +*   **类浏览器和可视化开发环境** :一个类浏览器需要可以枚举类的成员。可视化开发环境(如 IDE)可以从利用反射中可用的类型信息中受益,以帮助程序员编写正确的代码。 +*   **调试器和测试工具** : 调试器需要能够检查一个类里的私有成员。测试工具可以利用反射来自动地调用类里定义的可被发现的 API 定义,以确保一组测试中有较高的代码覆盖率。 -**反射的缺点:** +**反射的缺点:** 尽管反射非常强大,但也不能滥用。如果一个功能可以不用反射完成,那么最好就不用。在我们使用反射技术时,下面几条内容应该牢记于心。 -* **性能开销** :反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。 +*   **性能开销** :反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。 -* **安全限制** :使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如 Applet,那么这就是个问题了。 +*   **安全限制** :使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如 Applet,那么这就是个问题了。 -* **内部暴露** :由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。 +*   **内部暴露** :由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。 -- [Trail: The Reflection API](https://docs.oracle.com/javase/tutorial/reflect/index.html) -- [深入解析 Java 反射(1)- 基础](http://www.sczyh30.com/posts/Java/java-reflection-1/) +- [Trail: The Reflection API](https://docs.oracle.com/javase/tutorial/reflect/index.html) +- [深入解析 Java 反射(1)- 基础](http://www.sczyh30.com/posts/Java/java-reflection-1/) -# 八、异常 +# 八、异常 -Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: **Error** 和 **Exception**。其中 Error 用来表示 JVM 无法处理的错误,Exception 分为两种: +Throwable 可以用来表示任何可以作为异常抛出的类,分为两种:**Error** 和 **Exception**。其中 Error 用来表示 JVM 无法处理的错误,Exception 分为两种: -- **受检异常** :需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复; -- **非受检异常** :是程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序崩溃并且无法恢复。 +- **受检异常**:需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复; +- **非受检异常**:是程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序崩溃并且无法恢复。 -

+ -- [Java 入门之异常处理](https://www.tianmaying.com/tutorial/Java-Exception) -- [Java 异常的面试问题及答案 -Part 1](http://www.importnew.com/7383.html) +- [Java 入门之异常处理](https://www.tianmaying.com/tutorial/Java-Exception) +- [Java 异常的面试问题及答案 -Part 1](http://www.importnew.com/7383.html) -# 九、泛型 +# 九、泛型 ```java -public class Box { - // T stands for "Type" - private T t; - public void set(T t) { this.t = t; } - public T get() { return t; } +public class Box { +    // T stands for "Type" +    private T t; +    public void set(T t) { this.t = t; } +    public T get() { return t; } } ``` -- [Java 泛型详解](http://www.importnew.com/24029.html) -- [10 道 Java 泛型面试题](https://cloud.tencent.com/developer/article/1033693) +- [Java 泛型详解](http://www.importnew.com/24029.html) +- [10 道 Java 泛型面试题](https://cloud.tencent.com/developer/article/1033693) -# 十、注解 +# 十、注解 -Java 注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。 +Java 注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。 -[注解 Annotation 实现原理与自定义注解例子](https://www.cnblogs.com/acm-bingzi/p/javaAnnotation.html) +[注解 Annotation 实现原理与自定义注解例子](https://www.cnblogs.com/acm-bingzi/p/javaAnnotation.html) -# 十一、特性 +# 十一、特性 -## Java 各版本的新特性 +## Java 各版本的新特性 -**New highlights in Java SE 8** +**New highlights in Java SE 8** -1. Lambda Expressions -2. Pipelines and Streams -3. Date and Time API -4. Default Methods -5. Type Annotations -6. Nashhorn JavaScript Engine -7. Concurrent Accumulators -8. Parallel operations -9. PermGen Error Removed +1. Lambda Expressions +2. Pipelines and Streams +3. Date and Time API +4. Default Methods +5. Type Annotations +6. Nashhorn JavaScript Engine +7. Concurrent Accumulators +8. Parallel operations +9. PermGen Error Removed -**New highlights in Java SE 7** +**New highlights in Java SE 7** -1. Strings in Switch Statement -2. Type Inference for Generic Instance Creation -3. Multiple Exception Handling -4. Support for Dynamic Languages -5. Try with Resources -6. Java nio Package -7. Binary Literals, Underscore in literals -8. Diamond Syntax +1. Strings in Switch Statement +2. Type Inference for Generic Instance Creation +3. Multiple Exception Handling +4. Support for Dynamic Languages +5. Try with Resources +6. Java nio Package +7. Binary Literals, Underscore in literals +8. Diamond Syntax -- [Difference between Java 1.8 and Java 1.7?](http://www.selfgrowth.com/articles/difference-between-java-18-and-java-17) -- [Java 8 特性](http://www.importnew.com/19345.html) +- [Difference between Java 1.8 and Java 1.7?](http://www.selfgrowth.com/articles/difference-between-java-18-and-java-17) +- [Java 8 特性](http://www.importnew.com/19345.html) -## Java 与 C++ 的区别 +## Java 与 C++ 的区别 -- Java 是纯粹的面向对象语言,所有的对象都继承自 java.lang.Object,C++ 为了兼容 C 即支持面向对象也支持面向过程。 -- Java 通过虚拟机从而实现跨平台特性,但是 C++ 依赖于特定的平台。 -- Java 没有指针,它的引用可以理解为安全指针,而 C++ 具有和 C 一样的指针。 -- Java 支持自动垃圾回收,而 C++ 需要手动回收。 -- Java 不支持多重继承,只能通过实现多个接口来达到相同目的,而 C++ 支持多重继承。 -- Java 不支持操作符重载,虽然可以对两个 String 对象执行加法运算,但是这是语言内置支持的操作,不属于操作符重载,而 C++ 可以。 -- Java 的 goto 是保留字,但是不可用,C++ 可以使用 goto。 -- Java 不支持条件编译,C++ 通过 #ifdef #ifndef 等预处理命令从而实现条件编译。 +- Java 是纯粹的面向对象语言,所有的对象都继承自 java.lang.Object,C++ 为了兼容 C 即支持面向对象也支持面向过程。 +- Java 通过虚拟机从而实现跨平台特性,但是 C++ 依赖于特定的平台。 +- Java 没有指针,它的引用可以理解为安全指针,而 C++ 具有和 C 一样的指针。 +- Java 支持自动垃圾回收,而 C++ 需要手动回收。 +- Java 不支持多重继承,只能通过实现多个接口来达到相同目的,而 C++ 支持多重继承。 +- Java 不支持操作符重载,虽然可以对两个 String 对象执行加法运算,但是这是语言内置支持的操作,不属于操作符重载,而 C++ 可以。 +- Java 的 goto 是保留字,但是不可用,C++ 可以使用 goto。 +- Java 不支持条件编译,C++ 通过 #ifdef #ifndef 等预处理命令从而实现条件编译。 -[What are the main differences between Java and C++?](http://cs-fundamentals.com/tech-interview/java/differences-between-java-and-cpp.php) +[What are the main differences between Java and C++?](http://cs-fundamentals.com/tech-interview/java/differences-between-java-and-cpp.php) -## JRE or JDK +## JRE or JDK -- JRE is the JVM program, Java application need to run on JRE. -- JDK is a superset of JRE, JRE + tools for developing java programs. e.g, it provides the compiler "javac" +- JRE is the JVM program, Java application need to run on JRE. +- JDK is a superset of JRE, JRE + tools for developing java programs. e.g, it provides the compiler "javac" -# 参考资料 +# 参考资料 -- Eckel B. Java 编程思想[M]. 机械工业出版社, 2002. -- Bloch J. Effective java[M]. Addison-Wesley Professional, 2017. +- Eckel B. Java 编程思想[M]. 机械工业出版社, 2002. +- Bloch J. Effective java[M]. Addison-Wesley Professional, 2017. +---bottom---CyC--- +![](index_files/f76067a5-7d5f-4135-9549-8199c77d8f1c.jpg) +![](index_files/PPjwP.png) diff --git a/docs/notes/Java 容器.md b/docs/notes/Java 容器.md index 04902d87..b64d7987 100644 --- a/docs/notes/Java 容器.md +++ b/docs/notes/Java 容器.md @@ -1,326 +1,304 @@ -[🎉 面试进阶指南已上线](https://xiaozhuanlan.com/CyC2018) - -* [一、概览](#一概览) - * [Collection](#collection) - * [Map](#map) -* [二、容器中的设计模式](#二容器中的设计模式) - * [迭代器模式](#迭代器模式) - * [适配器模式](#适配器模式) -* [三、源码分析](#三源码分析) - * [ArrayList](#arraylist) - * [Vector](#vector) - * [CopyOnWriteArrayList](#copyonwritearraylist) - * [LinkedList](#linkedlist) - * [HashMap](#hashmap) - * [ConcurrentHashMap](#concurrenthashmap) - * [LinkedHashMap](#linkedhashmap) - * [WeakHashMap](#weakhashmap) -* [附录](#附录) -* [参考资料](#参考资料) - +# 一、概览 + +容器主要包括 Collection 和 Map 两种,Collection 存储着对象的集合,而 Map 存储着键值对(两个对象)的映射表。 + +## Collection + +![](index_files/VP6n3i8W48Ptde8NQ9_0eSR5eOD6uqx.png) + +### 1. Set + +- TreeSet:基于红黑树实现,支持有序性操作,例如根据一个范围查找元素的操作。但是查找效率不如 HashSet,HashSet 查找的时间复杂度为 O(1),TreeSet 则为 O(logN)。 + +- HashSet:基于哈希表实现,支持快速查找,但不支持有序性操作。并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的。 + +- LinkedHashSet:具有 HashSet 的查找效率,且内部使用双向链表维护元素的插入顺序。 + +### 2. List + +- ArrayList:基于动态数组实现,支持随机访问。 + +- Vector:和 ArrayList 类似,但它是线程安全的。 + +- LinkedList:基于双向链表实现,只能顺序访问,但是可以快速地在链表中间插入和删除元素。不仅如此,LinkedList 还可以用作栈、队列和双向队列。 + +### 3. Queue + +- LinkedList:可以用它来实现双向队列。 + +- PriorityQueue:基于堆结构实现,可以用它来实现优先队列。 + +## Map + +![](index_files/SoWkIImgAStDuUBAp2j9BKfBJ4vLy4q.png) + +- TreeMap:基于红黑树实现。 + +- HashMap:基于哈希表实现。 + +- HashTable:和 HashMap 类似,但它是线程安全的,这意味着同一时刻多个线程可以同时写入 HashTable 并且不会导致数据不一致。它是遗留类,不应该去使用它。现在可以使用 ConcurrentHashMap 来支持线程安全,并且 ConcurrentHashMap 的效率会更高,因为 ConcurrentHashMap 引入了分段锁。 + +- LinkedHashMap:使用双向链表来维护元素的顺序,顺序为插入顺序或者最近最少使用(LRU)顺序。 -# 一、概览 +# 二、容器中的设计模式 -容器主要包括 Collection 和 Map 两种,Collection 存储着对象的集合,而 Map 存储着键值对(两个对象)的映射表。 +## 迭代器模式 -## Collection +![](index_files/SoWkIImgAStDuUBAp2j9BKfBJ4vLy0G.png) -

+Collection 继承了 Iterable 接口,其中的 iterator() 方法能够产生一个 Iterator 对象,通过这个对象就可以迭代遍历 Collection 中的元素。 -### 1. Set - -- TreeSet:基于红黑树实现,支持有序性操作,例如根据一个范围查找元素的操作。但是查找效率不如 HashSet,HashSet 查找的时间复杂度为 O(1),TreeSet 则为 O(logN)。 - -- HashSet:基于哈希表实现,支持快速查找,但不支持有序性操作。并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的。 - -- LinkedHashSet:具有 HashSet 的查找效率,且内部使用双向链表维护元素的插入顺序。 - -### 2. List - -- ArrayList:基于动态数组实现,支持随机访问。 - -- Vector:和 ArrayList 类似,但它是线程安全的。 - -- LinkedList:基于双向链表实现,只能顺序访问,但是可以快速地在链表中间插入和删除元素。不仅如此,LinkedList 还可以用作栈、队列和双向队列。 - -### 3. Queue - -- LinkedList:可以用它来实现双向队列。 - -- PriorityQueue:基于堆结构实现,可以用它来实现优先队列。 - -## Map - -

- -- TreeMap:基于红黑树实现。 - -- HashMap:基于哈希表实现。 - -- HashTable:和 HashMap 类似,但它是线程安全的,这意味着同一时刻多个线程可以同时写入 HashTable 并且不会导致数据不一致。它是遗留类,不应该去使用它。现在可以使用 ConcurrentHashMap 来支持线程安全,并且 ConcurrentHashMap 的效率会更高,因为 ConcurrentHashMap 引入了分段锁。 - -- LinkedHashMap:使用双向链表来维护元素的顺序,顺序为插入顺序或者最近最少使用(LRU)顺序。 - - -# 二、容器中的设计模式 - -## 迭代器模式 - -

- -Collection 继承了 Iterable 接口,其中的 iterator() 方法能够产生一个 Iterator 对象,通过这个对象就可以迭代遍历 Collection 中的元素。 - -从 JDK 1.5 之后可以使用 foreach 方法来遍历实现了 Iterable 接口的聚合对象。 +从 JDK 1.5 之后可以使用 foreach 方法来遍历实现了 Iterable 接口的聚合对象。 ```java -List list = new ArrayList<>(); +List list = new ArrayList<>(); list.add("a"); list.add("b"); -for (String item : list) { - System.out.println(item); +for (String item : list) { +    System.out.println(item); } ``` -## 适配器模式 +## 适配器模式 -java.util.Arrays#asList() 可以把数组类型转换为 List 类型。 +java.util.Arrays#asList() 可以把数组类型转换为 List 类型。 ```java @SafeVarargs -public static List asList(T... a) +public static  List asList(T... a) ``` -应该注意的是 asList() 的参数为泛型的变长参数,不能使用基本类型数组作为参数,只能使用相应的包装类型数组。 +应该注意的是 asList() 的参数为泛型的变长参数,不能使用基本类型数组作为参数,只能使用相应的包装类型数组。 ```java -Integer[] arr = {1, 2, 3}; -List list = Arrays.asList(arr); +Integer[] arr = {1, 2, 3}; +List list = Arrays.asList(arr); ``` -也可以使用以下方式调用 asList(): +也可以使用以下方式调用 asList(): ```java -List list = Arrays.asList(1, 2, 3); +List list = Arrays.asList(1, 2, 3); ``` -# 三、源码分析 +# 三、源码分析 -如果没有特别说明,以下源码分析基于 JDK 1.8。 +如果没有特别说明,以下源码分析基于 JDK 1.8。 -在 IDEA 中 double shift 调出 Search EveryWhere,查找源码文件,找到之后就可以阅读源码。 +在 IDEA 中 double shift 调出 Search EveryWhere,查找源码文件,找到之后就可以阅读源码。 -## ArrayList +## ArrayList -### 1. 概览 +### 1. 概览 -实现了 RandomAccess 接口,因此支持随机访问。这是理所当然的,因为 ArrayList 是基于数组实现的。 +实现了 RandomAccess 接口,因此支持随机访问。这是理所当然的,因为 ArrayList 是基于数组实现的。 ```java -public class ArrayList extends AbstractList - implements List, RandomAccess, Cloneable, java.io.Serializable +public class ArrayList extends AbstractList +        implements List, RandomAccess, Cloneable, java.io.Serializable ``` -数组的默认大小为 10。 +数组的默认大小为 10。 ```java -private static final int DEFAULT_CAPACITY = 10; +private static final int DEFAULT_CAPACITY = 10; ``` -### 2. 扩容 +### 2. 扩容 -添加元素时使用 ensureCapacityInternal() 方法来保证容量足够,如果不够时,需要使用 grow() 方法进行扩容,新容量的大小为 `oldCapacity + (oldCapacity >> 1)`,也就是旧容量的 1.5 倍。 +添加元素时使用 ensureCapacityInternal() 方法来保证容量足够,如果不够时,需要使用 grow() 方法进行扩容,新容量的大小为 `oldCapacity + (oldCapacity >> 1)`,也就是旧容量的 1.5 倍。 -扩容操作需要调用 `Arrays.copyOf()` 把原数组整个复制到新数组中,这个操作代价很高,因此最好在创建 ArrayList 对象时就指定大概的容量大小,减少扩容操作的次数。 +扩容操作需要调用 `Arrays.copyOf()` 把原数组整个复制到新数组中,这个操作代价很高,因此最好在创建 ArrayList 对象时就指定大概的容量大小,减少扩容操作的次数。 ```java -public boolean add(E e) { - ensureCapacityInternal(size + 1); // Increments modCount!! - elementData[size++] = e; - return true; +public boolean add(E e) { +    ensureCapacityInternal(size + 1);  // Increments modCount!! +    elementData[size++] = e; +    return true; } -private void ensureCapacityInternal(int minCapacity) { - if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { - minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); - } - ensureExplicitCapacity(minCapacity); +private void ensureCapacityInternal(int minCapacity) { +    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { +        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); +    } +    ensureExplicitCapacity(minCapacity); } -private void ensureExplicitCapacity(int minCapacity) { - modCount++; - // overflow-conscious code - if (minCapacity - elementData.length > 0) - grow(minCapacity); +private void ensureExplicitCapacity(int minCapacity) { +    modCount++; +    // overflow-conscious code +    if (minCapacity - elementData.length > 0) +        grow(minCapacity); } -private void grow(int minCapacity) { - // overflow-conscious code - int oldCapacity = elementData.length; - int newCapacity = oldCapacity + (oldCapacity >> 1); - if (newCapacity - minCapacity < 0) - newCapacity = minCapacity; - 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); +private void grow(int minCapacity) { +    // overflow-conscious code +    int oldCapacity = elementData.length; +    int newCapacity = oldCapacity + (oldCapacity >> 1); +    if (newCapacity - minCapacity < 0) +        newCapacity = minCapacity; +    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); } ``` -### 3. 删除元素 +### 3. 删除元素 -需要调用 System.arraycopy() 将 index+1 后面的元素都复制到 index 位置上,该操作的时间复杂度为 O(N),可以看出 ArrayList 删除元素的代价是非常高的。 +需要调用 System.arraycopy() 将 index+1 后面的元素都复制到 index 位置上,该操作的时间复杂度为 O(N),可以看出 ArrayList 删除元素的代价是非常高的。 ```java -public E remove(int index) { - rangeCheck(index); - modCount++; - E oldValue = elementData(index); - int numMoved = size - index - 1; - if (numMoved > 0) - System.arraycopy(elementData, index+1, elementData, index, numMoved); - elementData[--size] = null; // clear to let GC do its work - return oldValue; +public E remove(int index) { +    rangeCheck(index); +    modCount++; +    E oldValue = elementData(index); +    int numMoved = size - index - 1; +    if (numMoved > 0) +        System.arraycopy(elementData, index+1, elementData, index, numMoved); +    elementData[--size] = null; // clear to let GC do its work +    return oldValue; } ``` -### 4. Fail-Fast +### 4. Fail-Fast -modCount 用来记录 ArrayList 结构发生变化的次数。结构发生变化是指添加或者删除至少一个元素的所有操作,或者是调整内部数组的大小,仅仅只是设置元素的值不算结构发生变化。 +modCount 用来记录 ArrayList 结构发生变化的次数。结构发生变化是指添加或者删除至少一个元素的所有操作,或者是调整内部数组的大小,仅仅只是设置元素的值不算结构发生变化。 -在进行序列化或者迭代等操作时,需要比较操作前后 modCount 是否改变,如果改变了需要抛出 ConcurrentModificationException。 +在进行序列化或者迭代等操作时,需要比较操作前后 modCount 是否改变,如果改变了需要抛出 ConcurrentModificationException。 ```java -private void writeObject(java.io.ObjectOutputStream s) - throws java.io.IOException{ - // Write out element count, and any hidden stuff - int expectedModCount = modCount; - s.defaultWriteObject(); +private void writeObject(java.io.ObjectOutputStream s) +    throws java.io.IOException{ +    // Write out element count, and any hidden stuff +    int expectedModCount = modCount; +    s.defaultWriteObject(); - // Write out size as capacity for behavioural compatibility with clone() - s.writeInt(size); +    // Write out size as capacity for behavioural compatibility with clone() +    s.writeInt(size); - // Write out all elements in the proper order. - for (int i=0; i