diff --git a/docs/about-the-author/bzhan-10wan.md b/docs/about-the-author/bzhan-10wan.md index 078a589d7..94919c22f 100644 --- a/docs/about-the-author/bzhan-10wan.md +++ b/docs/about-the-author/bzhan-10wan.md @@ -11,13 +11,13 @@ tag: 恭喜这个 B。。。。。。站上的 UP,上一期视频播放量突破了 10 万!这也是二哥人生当中的第一次,凭借单条视频突破 10 万播放,必须得纪念下。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/bzhan-10wan-4f27a848-7dba-4cd3-a705-a6ef02162338.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/bzhan-10wan-4f27a848-7dba-4cd3-a705-a6ef02162338.png) 从众多的宫斗剧中我得出了一条宝贵的人生经验:“母凭子贵”。这条经验同样适用于二哥本人,可能会因为这一期视频,吹这辈子最多的牛逼:这不,荣获哔哩哔哩第 3 周【校园优秀奖&校园新星奖】。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/bzhan-10wan-340f1fe4-49f1-48c6-a972-c5da7de8ddd0.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/bzhan-10wan-340f1fe4-49f1-48c6-a972-c5da7de8ddd0.png) 我已经按捺不住激动的心情,在两万人的朋友圈大肆炫耀了。十万播放,对于百大 UP 来说,可能就是分分钟的事,可对于我这个(未来的) B站百大来说,苦苦等了 149 天!!!!!!! @@ -37,13 +37,13 @@ tag: 三不三连没关系,有关系的是不三连可能会对不起二哥的肝,所以还是三连吧,哈哈哈哈,瞧瞧我们这该死的生物钟,起这么早。。。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/bzhan-10wan-5a1d423e-8827-4a66-9197-4641ef0ecbaf.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/bzhan-10wan-5a1d423e-8827-4a66-9197-4641ef0ecbaf.png) 接下来,上干货,我把这期 10万+ 播放的视频台本重新整理了一下,本来不想发的,很多小伙伴私信说二哥偏爱 B 站,同步都懒得同步了吗? 这不,赶紧发到公众号上来,希望学生党们现在立刻马上收藏起来,这个寒假你会过得非常充实;至于工作党嘛,像二哥这样的,既要工作,又要读书写作照顾家庭的,忙都忙死了,就算了吧! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/bzhan-10wan-99073995-10b0-42ee-81e5-cc4abc26aa71.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/bzhan-10wan-99073995-10b0-42ee-81e5-cc4abc26aa71.png) 啊,不,还是要稍微卷一卷吧,免得被那群还有半年就毕业的家伙们拍死在沙滩上。。。。 @@ -61,7 +61,7 @@ tag: 所以我的建议是,**趁寒假打打王者上上分吧**! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/bzhan-10wan-63412a28-7315-4ac4-a04b-a049b338c0d8.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/bzhan-10wan-63412a28-7315-4ac4-a04b-a049b338c0d8.gif) 啊,不!**趁寒假刷一波清华在 GitHub 上 20k+ star 的开源课程吧**! @@ -71,7 +71,7 @@ tag: >地址:https://github.com/PKUanonym/REKCARC-TSC-UHT -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/bzhan-10wan-c309fdb8-084c-44b1-bd8e-86a4b39cbb7b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/bzhan-10wan-c309fdb8-084c-44b1-bd8e-86a4b39cbb7b.png) 我来带小伙伴们过一下清华的课程安排哈,主要是针对计算机专业的。 @@ -106,7 +106,7 @@ tag: 学完这些,大家至少能学会下面这幅思维导图中列出来的内容。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/bzhan-10wan-03e4f5b4-c756-401c-aada-695b9cfaf00d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/bzhan-10wan-03e4f5b4-c756-401c-aada-695b9cfaf00d.png) 更多 C 语言的学习内容,可以戳下面这个链接,之前在公众号上发过了,这里就不再复制粘贴了: @@ -191,7 +191,7 @@ class 二哥 { 第一本,《趣学数据结构》 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/bzhan-10wan-bd02be2f-ae71-413f-b0c0-0050fee0e2b5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/bzhan-10wan-bd02be2f-ae71-413f-b0c0-0050fee0e2b5.png) 说到这,多说一嘴。2018 年的时候,人民邮电出版社的张老师邀请我出一本 Java 方面的书,我当时想命名为《趣学 Java》。张老师说,刚好之前和陈小玉老师合作出了一本《趣学算法》的书,要不发一本你看看吧。 @@ -199,11 +199,11 @@ class 二哥 { 第二本,《数据结构(C++语言版)》 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/bzhan-10wan-de28bb8a-ddb6-4b73-b132-ebf5e507fdbe.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/bzhan-10wan-de28bb8a-ddb6-4b73-b132-ebf5e507fdbe.png) 对,清华大学邓俊辉教授编著的,豆瓣评分也蛮高的。这本书还配套了视频课程,是免费的,可以在学堂在线上看,我之前也有推荐过。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/bzhan-10wan-15de0615-3d9c-4bdc-bedb-4165ac6f4802.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/bzhan-10wan-15de0615-3d9c-4bdc-bedb-4165ac6f4802.png) 课程质量木得说,算是国家级精品课了。大家有时间的话,一定要刷一遍。 @@ -218,13 +218,13 @@ class 二哥 { 第一本,《数据结构与算法分析(Java 语言描述)》 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/bzhan-10wan-1672e79d-a576-4a24-bc60-ced47b692a0f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/bzhan-10wan-1672e79d-a576-4a24-bc60-ced47b692a0f.png) 虽然翻译得不怎么样,但内容很全面,适合拿来作为一本数据结构的入门书。 第二本,《算法(第 4 版)》 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/bzhan-10wan-9e260fc8-69fa-4dfa-82c6-9d6b1d7027e5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/bzhan-10wan-9e260fc8-69fa-4dfa-82c6-9d6b1d7027e5.png) 虽然名为算法,但大家都知道,算法是基于数据结构的,数组、队列、栈、堆、二叉树、哈希表等等,这些数据结构都讲到了。 diff --git a/docs/about-the-author/csdn-1000wan.md b/docs/about-the-author/csdn-1000wan.md index 4f7d09109..01a3db105 100644 --- a/docs/about-the-author/csdn-1000wan.md +++ b/docs/about-the-author/csdn-1000wan.md @@ -14,15 +14,15 @@ tag: 我努力的回想着,回想自己在 2021 年做出了哪些耀眼的成绩,正襟危坐,回想良久,也只想到这最后一件:**CSDN 的博文访问量也突破了一千万**。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/csdn-1000wan-2627c2a4-46c1-49a9-b86f-607c65ba6398.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/csdn-1000wan-2627c2a4-46c1-49a9-b86f-607c65ba6398.png) 但这算不算得上是成绩,很难说,因为喜欢这个平台的人有很多,不喜欢这个平台的也有很多。也许,GitHub 上有 110k+ star 的 JavaGuide 的话最具有说服力了,这个平台不规范转载的很多,垃圾资源下载的很多,但也有几个优秀的作者撑起了 CSDN 的半边天,二哥就是其中一个。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/csdn-1000wan-a7c74449-7183-453f-8286-92d0bfe0a56d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/csdn-1000wan-a7c74449-7183-453f-8286-92d0bfe0a56d.png) 老读者都知道,我是从2014 年,开始坚持写技术博客的。一开始,还没敢在 CSDN 上写,只敢在 JavaEye 上写(估计很多新读者都不太知道这个平台)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/csdn-1000wan-43677bfa-bdd6-4c3b-aa67-3a3277dc3575.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/csdn-1000wan-43677bfa-bdd6-4c3b-aa67-3a3277dc3575.png) 那时候的 JavaEye 真的是非常非常非常的纯粹(比博客园更纯粹),没有任何商业广告,还时不时送送书,头部作者有 fastjson 的作者温少,《亿级流量网站架构核心技术》作者开涛,想必做技术的大家应该都知道他们俩。 @@ -30,13 +30,13 @@ tag: 就这样写着写着,我成了 CSDN 的博客专家,出版了一本技术图书,成为了两届博客之星。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/csdn-1000wan-5137a245-035e-4e36-ba04-2b06fae275e1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/csdn-1000wan-5137a245-035e-4e36-ba04-2b06fae275e1.png) 就这样写着写着,我遇到了越来越多的读者,给他们提供帮助的同时,也成为了他们前进的动力。 据我自己的不完全统计,2021 年,我在朋友圈和公众号送出去了超过 200 本技术图书,每次我都会留个小心机,问中奖的读者是怎么认识二哥的,有没有什么建议,留言中竟然很多都来自 CSDN,这让我又惊又喜。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/csdn-1000wan-11df1e5d-2505-416c-949a-607b4ebbc61f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/csdn-1000wan-11df1e5d-2505-416c-949a-607b4ebbc61f.png) 经常有读者夸赞二哥好有写作的天赋啊,其实哪里是有天赋,纯粹是因为写得多,所以才写得好。我现在的文笔,讲真,还不如上高中那会,那会才是真的笔下生花,诗都能写得出来,情书就更不用说了。 @@ -50,47 +50,47 @@ tag: 2 月 15 日,我和奶奶的合影。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/csdn-1000wan-14d7737b-7324-4717-bd16-a3da7f2b1223.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/csdn-1000wan-14d7737b-7324-4717-bd16-a3da7f2b1223.png) 3 月 26 日,读者考上研究生了,特意发来祝贺。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/csdn-1000wan-63626ad5-8580-4565-a0da-af3a0bf43875.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/csdn-1000wan-63626ad5-8580-4565-a0da-af3a0bf43875.png) 4 月 3 日,和教练小姐姐在健身房合影。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/csdn-1000wan-eb256e24-de8a-49d0-a6ef-eb6d0b8d28e7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/csdn-1000wan-eb256e24-de8a-49d0-a6ef-eb6d0b8d28e7.png) 5 月 25 日,二哥的读者群体扩大了台湾省。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/csdn-1000wan-b2a8a63c-8c8f-4d39-9a9f-acf54c908058.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/csdn-1000wan-b2a8a63c-8c8f-4d39-9a9f-acf54c908058.png) 6 月 13 日,和家人畅游青岛。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/csdn-1000wan-b430951d-544c-4743-b700-3c71a854267a.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/csdn-1000wan-b430951d-544c-4743-b700-3c71a854267a.png) 7 月 20 日,被某某女粉追着要联系方式。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/csdn-1000wan-d7e127c5-5560-45c0-87c8-4cea924047cd.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/csdn-1000wan-d7e127c5-5560-45c0-87c8-4cea924047cd.png) 8 月 21 日,在十八线县城的老家砸核桃吃。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/csdn-1000wan-944b3f88-86ab-4e86-aeb6-4ad1f4f1ece3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/csdn-1000wan-944b3f88-86ab-4e86-aeb6-4ad1f4f1ece3.png) 9 月 23 日,收到掘金和 CSDN 寄来的月饼。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/csdn-1000wan-0056fcd2-afa4-475e-ad69-95b84b90b8c7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/csdn-1000wan-0056fcd2-afa4-475e-ad69-95b84b90b8c7.png) 10 月 11 日,收到《Java 程序员进阶之路》专栏在 GitHub 上开源以来的两笔大额打赏。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/csdn-1000wan-7e6d9918-6d94-45ef-9afa-42882f79944d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/csdn-1000wan-7e6d9918-6d94-45ef-9afa-42882f79944d.png) 11 月 6 日,和四位河科大的学弟撸完串后在校园里的合影。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/csdn-1000wan-0c1b3624-f4e5-4e0c-b119-0ef4d440a60d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/csdn-1000wan-0c1b3624-f4e5-4e0c-b119-0ef4d440a60d.png) 12 月 27 日,CSDN 生成的年度报告。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/csdn-1000wan-a070af45-bfeb-4b1a-9b29-745279a4a0fc.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/csdn-1000wan-a070af45-bfeb-4b1a-9b29-745279a4a0fc.png) 不知道大家的 2021 过得怎么样? @@ -107,7 +107,7 @@ tag: 一年时间过得可真快,有很多想做好的事情,到最后都差了点意思。就说一件吧,B 站的视频播放量没有达到预期。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/csdn-1000wan-83e7cfbc-496d-45e8-bfd6-cd0d1dfc60d3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/csdn-1000wan-83e7cfbc-496d-45e8-bfd6-cd0d1dfc60d3.png) 8 月份还能坚持一周输出一个,从一开始面对镜头时的恐惧,到慢慢接纳自己。但好景不长,9 月份的时候,视频播放量呈现下降趋势,我就开始胡思乱想了。 @@ -161,7 +161,7 @@ B 站我一定做到一万粉——这个 flag 不能到。 这不,新的惊喜就是《Java 程序员进阶之路》专栏第一次上了 GitHub 的 trending 榜单! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/csdn-1000wan-8868c50f-d622-4f1f-a92b-13cf95edd786.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/csdn-1000wan-8868c50f-d622-4f1f-a92b-13cf95edd786.png) 正应了那句话,功夫不负有心人。 @@ -173,7 +173,7 @@ B 站我一定做到一万粉——这个 flag 不能到。 立个 flag 吧,**2022 年,冲 5000 star**! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/csdn-1000wan-5ebc2c65-f342-45f9-aaf6-7f663b5406b8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/csdn-1000wan-5ebc2c65-f342-45f9-aaf6-7f663b5406b8.png) 这个 flag 绝不能倒! diff --git a/docs/about-the-author/readme.md b/docs/about-the-author/readme.md index bf13b2822..a63b5f245 100644 --- a/docs/about-the-author/readme.md +++ b/docs/about-the-author/readme.md @@ -15,11 +15,11 @@ category: 联系作者 可以微信搜索 **沉默王二** 关键字或者扫码直接关注,关注后回复 **00** 还可以拉取我为你精心准备的学习资料。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/about-the-author/readme-34972eb2-f214-48db-a43e-c44918dfa23e.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/about-the-author/readme-34972eb2-f214-48db-a43e-c44918dfa23e.png) 学习资料有 BAT 大佬的刷题笔记,有《Java 程序员进阶之路》的 PDF 版电子书等等。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/about-the-author/readme-a3b81b80-03ec-470c-a9aa-ae8868e239cd.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/about-the-author/readme-a3b81b80-03ec-470c-a9aa-ae8868e239cd.png) @@ -29,7 +29,7 @@ category: 联系作者 >访问地址:https://blog.csdn.net/qing_gee -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/about-the-author/readme-14fd83ec-db6e-4a6f-a8e9-8ce1ce0097c3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/about-the-author/readme-14fd83ec-db6e-4a6f-a8e9-8ce1ce0097c3.png) ### 03、知乎 @@ -37,7 +37,7 @@ LV9 选手,阅读总数超 1590 万,今年卷一卷的话,破 2000 万阅 >访问地址:https://www.zhihu.com/people/cmower -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/about-the-author/readme-0fa19b6e-d06c-436b-bd11-1de8265c56bb.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/about-the-author/readme-0fa19b6e-d06c-436b-bd11-1de8265c56bb.png) ### 04、B 站 @@ -45,7 +45,7 @@ B 站还比较菜,目前只有一个 10 万+播放量的视频,等我的开 >访问地址:https://space.bilibili.com/513340480 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/about-the-author/readme-5db6c62f-6194-4022-aee5-daf4d1a19f0c.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/about-the-author/readme-5db6c62f-6194-4022-aee5-daf4d1a19f0c.png) ### 05、GitHub @@ -53,14 +53,14 @@ B 站还比较菜,目前只有一个 10 万+播放量的视频,等我的开 >访问地址:https://github.com/itwanger/toBeBetterJavaer -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/about-the-author/readme-aa477206-41a9-4c55-a649-3d87ba1cb26b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/about-the-author/readme-aa477206-41a9-4c55-a649-3d87ba1cb26b.png) ### 05、知识星球 目前还处在试运营阶段,正在筹备星球用户专属的 5 份小册,质量高的一笔。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/about-the-author/readme-c3dd1280-098e-460c-9a41-7d566976392b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/about-the-author/readme-c3dd1280-098e-460c-9a41-7d566976392b.png) 内容涵盖实战项目开发笔记、面试指南、Java学习、LeetCode Java 版刷题笔记等优质内容,价值远超门票! @@ -72,11 +72,11 @@ B 站还比较菜,目前只有一个 10 万+播放量的视频,等我的开 这是《Java 面试指南》专栏目前已经更新的内容,讲真,就这一个专栏就值回票价(新人优惠价只有 69 元)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/about-the-author/readme-066ef990-a603-4ace-9a19-728eeb319924.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/about-the-author/readme-066ef990-a603-4ace-9a19-728eeb319924.png) 还有星球内部我也在坚持每天更新优质的内容。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/about-the-author/readme-e108c929-ebc5-4d75-8d40-825f6d027117.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/about-the-author/readme-e108c929-ebc5-4d75-8d40-825f6d027117.png) 喜欢的小伙伴可以直接扫码加入。 diff --git a/docs/about-the-author/zhihu-1000wan.md b/docs/about-the-author/zhihu-1000wan.md index 83a738337..d82b69624 100644 --- a/docs/about-the-author/zhihu-1000wan.md +++ b/docs/about-the-author/zhihu-1000wan.md @@ -13,7 +13,7 @@ tag: 前几天,偷偷摸摸过了自己的第 N 个 18 岁,本来不想过生日的,就想当做是平常的一天。结果我妹非要提醒我,大家伙瞧瞧,这像妹妹该说的话吗? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/zhihu-1000wan-5addb157-141f-400b-a51f-77557c8fdb8d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/zhihu-1000wan-5addb157-141f-400b-a51f-77557c8fdb8d.png) 呜呜呜~ @@ -23,7 +23,7 @@ tag: **经营了近一年的知乎,阅读总数突破了一千万,这也是我人生当中的第一个**。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/zhihu-1000wan-0324afde-4009-4e80-b878-2311ff88e5ca.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/zhihu-1000wan-0324afde-4009-4e80-b878-2311ff88e5ca.png) 其实早在 11 月就破了千万,当时就想记录一下,但细想一下,好像这点成绩也算不上什么。毕竟逼乎上人均 985、年薪百万、刚下飞机的大佬多的是。 @@ -33,13 +33,13 @@ tag: 这不,前几天一个帖子莫名其妙被知乎删除了,我是无感知的。一个小伙伴为了看这个帖子,还特意发起了一次 9.8 元的付费咨询。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/zhihu-1000wan-2fdd5b2b-67c5-40cf-b0e4-0a92a37e659a.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/zhihu-1000wan-2fdd5b2b-67c5-40cf-b0e4-0a92a37e659a.png) 这足以说明这个帖子的内容是足够硬核的。 写知乎这近一年时间里,有一个帖子无声无息地爆了:**60 万+的阅读,7000+赞同,2.3 万+次的收藏**。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/zhihu-1000wan-8b4637f2-08c9-479b-855f-3fd332d44651.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/zhihu-1000wan-8b4637f2-08c9-479b-855f-3fd332d44651.png) 不对啊,收藏竟然是点赞的 3 倍还多。。嗯,此时此刻天空飘出来了四个字:白票真香。 @@ -101,7 +101,7 @@ tag: 我在知乎上还有不少硬核输出,尤其是这些千赞以上的帖子,真心推荐给大家看看,看完后绝壁是有收获的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/zhihu-1000wan-4612a83f-6207-496c-b32b-c6f1ab031c4f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/zhihu-1000wan-4612a83f-6207-496c-b32b-c6f1ab031c4f.png) 虽然有些埋没的帖子我觉得价值也很高。不过,埋没就埋没吧。 diff --git a/docs/array/print.md b/docs/array/print.md index 44dc1748e..619b157e3 100644 --- a/docs/array/print.md +++ b/docs/array/print.md @@ -102,11 +102,11 @@ for (String s : cmowers) { `Arrays.toString()` 可以将任意类型的数组转成字符串,包括基本类型数组和引用类型数组。该方法有多种重载形式。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/array/print-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/array/print-01.png) 使用 `Arrays.toString()` 方法来打印数组再优雅不过了,就像,就像,就像蒙娜丽莎的微笑。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/array/print-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/array/print-02.png) (三妹看到这么一副图的时候忍不住地笑了) @@ -144,7 +144,7 @@ System.out.println(Arrays.deepToString(deepArray)); “说到打印,三妹,哥给你提醒一点。阿里巴巴的 Java 开发手册上有这样一条规约,你看。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/array/print-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/array/print-03.png) “什么是 POJO 呢,就是 Plain Ordinary Java Object 的缩写,一般在 Web 应用程序中建立一个数据库的映射对象时,我们称它为 POJO,这类对象不继承或不实现任何其它 Java 框架的类或接口。” diff --git a/docs/baguwen/java-basic-34.md b/docs/baguwen/java-basic-34.md index 61a7c1487..ebbb7476a 100644 --- a/docs/baguwen/java-basic-34.md +++ b/docs/baguwen/java-basic-34.md @@ -11,13 +11,13 @@ tag: java 是一门**开源的跨平台的面向对象的**计算机语言. -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/baguwen/basic-34-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/baguwen/basic-34-01.png) 跨平台是因为 java 的 class 文件是运行在虚拟机上的,其实跨平台的,而**虚拟机是不同平台有不同版本**,所以说 java 是跨平台的. 面向对象有几个特点: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/baguwen/basic-34-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/baguwen/basic-34-02.png) - 1.**封装** - 两层含义:一层含义是把对象的属性和行为看成一个密不可分的整体,将这两者'封装'在一个不可分割的**独立单元**(即对象)中 @@ -55,7 +55,7 @@ java 是一门**开源的跨平台的面向对象的**计算机语言. ## 2.java 有哪些数据类型? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/baguwen/basic-34-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/baguwen/basic-34-03.png) java 主要有两种数据类型 @@ -131,7 +131,7 @@ try{ ## 7.arrayList 和 linkedList 的区别? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/baguwen/basic-34-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/baguwen/basic-34-04.png) - 1.ArrayList 是实现了基于**数组**的,存储空间是连续的。LinkedList 基于**链表**的,存储空间是不连续的。(LinkedList 是双向链表) @@ -160,7 +160,7 @@ try{ ## 10.那么 hashMap 线程不安全怎么解决? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/baguwen/basic-34-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/baguwen/basic-34-05.png) - 一.给 hashMap **直接加锁**,来保证线程安全 - 二.使用 **hashTable**,比方法一效率高,其实就是在其方法上加了 synchronized 锁 @@ -178,7 +178,7 @@ try{ ## 12.介绍一下 hashset 吧 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/baguwen/basic-34-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/baguwen/basic-34-06.png) 上图是 set 家族整体的结构, @@ -204,7 +204,7 @@ HashSet 是**基于 HashMap 实现**的,底层**采用 HashMap 来保存元素 ## 16.volatile 有什么作用? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/baguwen/basic-34-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/baguwen/basic-34-07.png) - **1.保证内存可见性** - 可见性是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果,另一个线程马上就能看到。 @@ -240,7 +240,7 @@ Integer a = 1,Integer b = 1,a==b 结果为**true** ## 19.JMM 是什么? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/baguwen/basic-34-08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/baguwen/basic-34-08.png) JMM 就是 **Java内存模型**(java memory model)。因为在不同的硬件生产商和不同的操作系统下,内存的访问有一定的差异,所以会造成相同的代码运行在不同的系统上会出现各种问题。所以java内存模型(JMM)**屏蔽掉各种硬件和操作系统的内存访问差异,以实现让java程序在各种平台下都能达到一致的并发效果**。 @@ -317,7 +317,7 @@ public class Singleton { ## 22.volatile 有什么作用 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/baguwen/basic-34-09.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/baguwen/basic-34-09.png) - 1.**保证内存可见性** - 当一个被volatile关键字修饰的变量被一个线程修改的时候,其他线程可以立刻得到修改之后的结果。当一个线程向被volatile关键字修饰的变量**写入数据**的时候,虚拟机会**强制它被值刷新到主内存中**。当一个线程**读取**被volatile关键字修饰的值的时候,虚拟机会**强制要求它从主内存中读取**。 @@ -348,7 +348,7 @@ try { 在 Java1.6 之前的版本中,synchronized 属于重量级锁,效率低下,**锁是** cpu 一个**总量级的资源**,每次获取锁都要和 cpu 申请,非常消耗性能。 在 **jdk1.6 之后** Java 官方对从 JVM 层面对 synchronized 较大优化,所以现在的 synchronized 锁效率也优化得很不错了,Jdk1.6 之后,为了减少获得锁和释放锁所带来的性能消耗,引入了偏向锁和轻量级锁,**增加了锁升级的过程**,由无锁->偏向锁->自旋锁->重量级锁 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/baguwen/basic-34-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/baguwen/basic-34-10.png) 增加锁升级的过程主要是**减少用户态到核心态的切换,提高锁的效率,从 jvm 层面优化锁** @@ -362,7 +362,7 @@ cas 叫做 CompareAndSwap,**比较并交换**,很多地方使用到了它, 当一个线程需要修改一个共享变量的值,完成这个操作需要先取出共享变量的值,赋给 A,基于 A 进行计算,得到新值 B,在用预期原值 A 和内存中的共享变量值进行比较,**如果相同就认为其他线程没有进行修改**,而将新值写入内存 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/baguwen/basic-34-11.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/baguwen/basic-34-11.png) **CAS的缺点** @@ -383,7 +383,7 @@ ReentrantLock有两种模式,一种是公平锁,一种是非公平锁。 **公平锁** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/baguwen/basic-34-12.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/baguwen/basic-34-12.png) - 第一步:**获取状态的 state 的值** - 如果 state=0 即代表锁没有被其它线程占用,执行第二步。 @@ -397,7 +397,7 @@ ReentrantLock有两种模式,一种是公平锁,一种是非公平锁。 **非公平锁** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/baguwen/basic-34-13.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/baguwen/basic-34-13.png) - 获取状态的 state 的值 - 如果 state=0 即代表锁没有被其它线程占用,则设置当前锁的持有者为当前线程,该操作用 CAS 完成。 @@ -500,7 +500,7 @@ public class Demo { ## 29.线程池的执行流程? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/baguwen/basic-34-14.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/baguwen/basic-34-14.png) - 判断线程池中的线程数**是否大于设置的核心线程数** - 如果**小于**,就**创建**一个核心线程来执行任务 @@ -548,13 +548,13 @@ Object obj = new Object(); ## 33.聊聊 ThreadLocal 吧 - ThreadLocal其实就是**线程本地变量**,他会在每个线程都创建一个副本,那么在线程之间访问内部副本变量就行了,做到了线程之间互相隔离。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/baguwen/basic-34-15.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/baguwen/basic-34-15.png) - ThreadLocal 有一个**静态内部类 ThreadLocalMap**,ThreadLocalMap 又包含了一个 Entry 数组,**Entry 本身是一个弱引用**,他的 key 是指向 ThreadLocal 的弱引用,**弱引用的目的是为了防止内存泄露**,如果是强引用那么除非线程结束,否则无法终止,可能会有内存泄漏的风险。 - 但是这样还是会存在内存泄露的问题,假如 key 和 ThreadLocal 对象被回收之后,entry 中就存在 key 为 null ,但是 value 有值的 entry 对象,但是永远没办法被访问到,同样除非线程结束运行。**解决方法就是调用 remove 方法删除 entry 对象**。 ## 34.一个对象的内存布局是怎么样的? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/baguwen/basic-34-16.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/baguwen/basic-34-16.png) - **1.对象头**: 对象头又分为 **MarkWord** 和 **Class Pointer** 两部分。 diff --git a/docs/basic-extra-meal/Overriding.md b/docs/basic-extra-meal/Overriding.md index cc7ddb6c8..ceae9ca00 100644 --- a/docs/basic-extra-meal/Overriding.md +++ b/docs/basic-extra-meal/Overriding.md @@ -13,7 +13,7 @@ tag: 重写带来了一种非常重要的能力,可以让子类重新实现从超类那继承过来的方法。在下面这幅图中,Animal 是父类,Dog 是子类,Dog 重新实现了 `move()` 方法用来和父类进行区分,毕竟狗狗跑起来还是比较有特色的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/basic-extra-meal/Overriding-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/basic-extra-meal/Overriding-1.png) 重写的方法和被重写的方法,不仅方法名相同,参数也相同,只不过,方法体有所不同。 @@ -77,7 +77,7 @@ public class Animal { 由于父类 Animal 中的 `move()` 是 final 的,所以子类在尝试重写该方法的时候就出现编译错误了! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/basic-extra-meal/Overriding-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/basic-extra-meal/Overriding-2.png) 同样的,如果一个方法是 static 的,也不允许重写,因为静态方法可用于父类以及子类的所有实例。 @@ -89,7 +89,7 @@ public class Animal { 重写的目的在于根据对象的类型不同而表现出多态,而静态方法不需要创建对象就可以使用。没有了对象,重写所需要的“对象的类型”也就没有存在的意义了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/basic-extra-meal/Overriding-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/basic-extra-meal/Overriding-3.png) ### 04、重写方法的要求 @@ -141,7 +141,7 @@ public class Dog extends Animal { 于是就编译出错了(返回类型不兼容)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/basic-extra-meal/Overriding-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/basic-extra-meal/Overriding-4.png) **规则五:重写的方法不能使用限制等级更严格的权限修饰符**。 @@ -169,7 +169,7 @@ public class Dog extends Animal { 如果子类中的方法用了更严格的权限修饰符,编译器就报错了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/basic-extra-meal/Overriding-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/basic-extra-meal/Overriding-5.png) **规则六:重写后的方法不能抛出比父类中更高级别的异常**。 diff --git a/docs/basic-extra-meal/annotation.md b/docs/basic-extra-meal/annotation.md index 4283a391a..dda634a05 100644 --- a/docs/basic-extra-meal/annotation.md +++ b/docs/basic-extra-meal/annotation.md @@ -13,7 +13,7 @@ tag: 三妹毫不犹豫地摇摇头,摆摆手,不好意思地承认自己的确没有自定义过。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/annotation/annotation-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/annotation/annotation-01.png) “好吧,哥来告诉你吧。” diff --git a/docs/basic-extra-meal/box.md b/docs/basic-extra-meal/box.md index 7e94c78bc..3577b999a 100644 --- a/docs/basic-extra-meal/box.md +++ b/docs/basic-extra-meal/box.md @@ -73,7 +73,7 @@ List list = new ArrayList<>(); “作为局部变量时,基本类型在栈中直接存储的具体数值,而包装类型则存储的是堆中的引用。”我一边说着,一边打开 `draw.io` 画起了图。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/box-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/box-01.png) 很显然,相比较于基本类型而言,包装类型需要占用更多的内存空间,不仅要存储对象,还要存储引用。假如没有基本类型的话,对于数值这类经常使用到的数据来说,每次都要通过 new 一个包装类型就显得非常笨重。 diff --git a/docs/basic-extra-meal/class-object.md b/docs/basic-extra-meal/class-object.md index 42871c623..b5595dcb8 100644 --- a/docs/basic-extra-meal/class-object.md +++ b/docs/basic-extra-meal/class-object.md @@ -14,7 +14,7 @@ Java 对象模型中: 那到底是先有Class还是先有Object? JVM 是怎么处理这个“鸡·蛋”问题呢? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/basic-extra-meal/class-object-2f47490c-70b8-41b8-9551-42c2f98eea91.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/basic-extra-meal/class-object-2f47490c-70b8-41b8-9551-42c2f98eea91.png) 针对这个问题,我在知乎上看到了 R 大的一个回答,正好解答了我心中的疑惑,就分享出来给各位小伙伴一个参考和启发~ diff --git a/docs/basic-extra-meal/deep-copy.md b/docs/basic-extra-meal/deep-copy.md index ff59f1383..6f8dd2161 100644 --- a/docs/basic-extra-meal/deep-copy.md +++ b/docs/basic-extra-meal/deep-copy.md @@ -111,7 +111,7 @@ writer2:Writer@b97c004{age=18, name='三妹'} 可以看得出,浅拷贝后,writer1 和 writer2 引用了不同的对象,但值是相同的,说明拷贝成功。之后,修改了 writer2 的 name 字段,直接上图就明白了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/deep-copy-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/deep-copy-01.png) 之前的例子中,Writer 类只有两个字段,没有引用类型字段。那么,我们再来看另外一个例子,为 Writer 类增加一个自定义的引用类型字段 Book,先来看 Book 的定义。 @@ -210,7 +210,7 @@ writer2:Writer@36d4b5c age=18, name='二哥', book=Book@32e6e9c3 bookName='永 与之前例子不同的是,writer2.book 变更后,writer1.book 也发生了改变。这是因为字符串 String 是不可变对象,一个新的值必须在字符串常量池中开辟一段新的内存空间,而自定义对象的内存地址并没有发生改变,只是对应的字段值发生了改变,见下图。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/deep-copy-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/deep-copy-02.png) “哇,哥,果真一图胜千言,我明白了。”三妹似乎对我画的图很感兴趣呢,“那你继续说深拷贝吧!” @@ -311,7 +311,7 @@ writer2:Writer@6d00a15d age=18, name='二哥', book=Book@51efea79 bookName=' 不只是 writer1 和 writer2 是不同的对象,它们中的 book 也是不同的对象。所以,改变了 writer2 中的 book 并不会影响到 writer1。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/deep-copy-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/deep-copy-03.png) 不过,通过 `clone()` 方法实现的深拷贝比较笨重,因为要将所有的引用类型都重写 `clone()` 方法,当嵌套的对象比较多的时候,就废了! diff --git a/docs/basic-extra-meal/enum.md b/docs/basic-extra-meal/enum.md index 7c2d31771..e2d331e2f 100644 --- a/docs/basic-extra-meal/enum.md +++ b/docs/basic-extra-meal/enum.md @@ -116,7 +116,7 @@ if(player.getType().equals(Player.PlayerType.BASKETBALL)){}; 另外, “==”运算符会在编译时进行检查,如果两侧的类型不匹配,会提示错误,而 `equals()` 方法则不会。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/enum/enum-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/enum/enum-01.png) “枚举还可用于 switch 语句,和基本数据类型的用法一致。”我说。 @@ -166,7 +166,7 @@ public enum PlayerType { “因为 EnumSet 是一个抽象类,所以创建 EnumSet 时不能使用 new 关键字。不过,EnumSet 提供了很多有用的静态工厂方法。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/enum/enum-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/enum/enum-02.png) “来看下面这个例子,我们使用 `noneOf()` 静态工厂方法创建了一个空的 PlayerType 类型的 EnumSet;使用 `allOf()` 静态工厂方法创建了一个包含所有 PlayerType 类型的 EnumSet。” @@ -197,7 +197,7 @@ public class EnumSetTest { 有了 EnumSet 后,就可以使用 Set 的一些方法了,见下图。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/enum/enum-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/enum/enum-03.png) “除了 EnumSet,还有 EnumMap,是一个专门针对枚举类型的 Map 接口的实现类,它可以将枚举常量作为键来使用。EnumMap 的效率比 HashMap 还要高,可以直接通过数组下标(枚举的 ordinal 值)访问到元素。” @@ -209,7 +209,7 @@ EnumMap enumMap = new EnumMap<>(PlayerType.class); 有了 EnumMap 对象后就可以使用 Map 的一些方法了,见下图。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/enum/enum-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/enum/enum-04.png) 和 HashMap(后面会讲)的使用方法大致相同,来看下面的例子。 diff --git a/docs/basic-extra-meal/equals-hashcode.md b/docs/basic-extra-meal/equals-hashcode.md index 3fc1a5ce1..b5f83906d 100644 --- a/docs/basic-extra-meal/equals-hashcode.md +++ b/docs/basic-extra-meal/equals-hashcode.md @@ -172,7 +172,7 @@ null “原因就在于重写 equals 方法的时候没有重写 hashCode 方法。”我回答道,“equals 方法虽然认定名字和年纪相同就是同一个学生,但它们本质上是两个对象,hashCode 并不相同。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/equals-hashcode-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/equals-hashcode-01.png) “那怎么重写 hashCode 方法呢?”三妹问。 @@ -223,7 +223,7 @@ result = (31*1 + Integer(18).hashCode()) * 31 + String("张三").hashCode(); 因为此时 s1 和 s2 对象的哈希值都为 776408。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/equals-hashcode-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/equals-hashcode-02.png) “每当重写 equals 方法时,hashCode 方法也需要重写,原因就是为了保证:如果两个对象调用 equals 方法返回的结果为 true,那么两个对象调用 hashCode 方法返回的结果也必然相同。”我点题了。 diff --git a/docs/basic-extra-meal/fanshe.md b/docs/basic-extra-meal/fanshe.md index 6587dc727..3bf6f7ce7 100644 --- a/docs/basic-extra-meal/fanshe.md +++ b/docs/basic-extra-meal/fanshe.md @@ -170,18 +170,18 @@ public Object invoke(Object obj, Object... args) `invoke()` 方法实际上是委派给 MethodAccessor 接口来完成的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/fanshe/fanshe-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/fanshe/fanshe-01.png) MethodAccessor 接口有三个实现类,其中的 MethodAccessorImpl 是一个抽象类,另外两个具体的实现类继承了这个抽象类。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/fanshe/fanshe-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/fanshe/fanshe-02.png) - NativeMethodAccessorImpl:通过本地方法来实现反射调用; - DelegatingMethodAccessorImpl:通过委派模式来实现反射调用; 通过 debug 的方式进入 `invoke()` 方法后,可以看到第一次反射调用会生成一个委派实现 DelegatingMethodAccessorImpl,它在生成的时候会传递一个本地实现 NativeMethodAccessorImpl。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/fanshe/fanshe-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/fanshe/fanshe-03.png) 也就是说,`invoke()` 方法在执行的时候,会先调用 DelegatingMethodAccessorImpl,然后调用 NativeMethodAccessorImpl,最后再调用实际的方法。 @@ -204,7 +204,7 @@ for (int i = 0;i < 20; i++) { 在 `invoke()` 方法处加断点进入 debug 模式,当 i = 15 的时候,也就是第 16 次执行的时候,会进入到 if 条件分支中,改变 DelegatingMethodAccessorImpl 的委派模式 delegate 为 `(MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod()`,而之前的委派模式 delegate 为 NativeMethodAccessorImpl。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/fanshe/fanshe-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/fanshe/fanshe-04.png) “这下明白了吧?三妹。”我说,“接下来,我们再来熟悉一下反射当中常用的 API。” diff --git a/docs/basic-extra-meal/generic.md b/docs/basic-extra-meal/generic.md index bb801aa44..87b1c6cd6 100644 --- a/docs/basic-extra-meal/generic.md +++ b/docs/basic-extra-meal/generic.md @@ -117,7 +117,7 @@ class Arraylist { 不过,说实话,泛型方法的定义看起来略显晦涩。来一副图吧(注意:方法返回类型和方法参数类型至少需要一个)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/generic/generic-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/generic/generic-01.png) 现在,我们来调用一下泛型方法。 diff --git a/docs/basic-extra-meal/hashcode.md b/docs/basic-extra-meal/hashcode.md index f3939b8de..5306532a8 100644 --- a/docs/basic-extra-meal/hashcode.md +++ b/docs/basic-extra-meal/hashcode.md @@ -24,7 +24,7 @@ public native int hashCode(); 具体的实现可以参考 `jdk/src/hotspot/share/runtime/synchronizer.cpp`(源码可以到 GitHub 上 OpenJDK 的仓库中下载)。`get_next_hash()` 方法会根据 hashCode 的取值来决定采用哪一种哈希值的生成策略。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/basic-extra-meal/hashcode-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/basic-extra-meal/hashcode-1.png) 并且 `hashCode()` 方法被 `@HotSpotIntrinsicCandidate` 注解修饰,说明它在 HotSpot 虚拟机中有一套高效的实现,基于 CPU 指令。 @@ -166,7 +166,7 @@ public static int hashCode(Object a[]) { 代码似乎很简单,归纳出的数学公式如下所示(n 为字符串长度)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/basic-extra-meal/hashcode-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/basic-extra-meal/hashcode-2.png) 注意:31 是个奇质数,不大不小,一般质数都非常适合哈希计算,偶数相当于移位运算,容易溢出,造成数据信息丢失。 diff --git a/docs/basic-extra-meal/instanceof-jvm.md b/docs/basic-extra-meal/instanceof-jvm.md index fab9623f6..00666272e 100644 --- a/docs/basic-extra-meal/instanceof-jvm.md +++ b/docs/basic-extra-meal/instanceof-jvm.md @@ -13,7 +13,7 @@ tag: >作者:RednaxelaFX,整理:沉默王二,链接:https://www.zhihu.com/question/21574535/answer/18998914 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/basic-extra-meal/instanceof-jvm-b676fee6-bfd4-4ae9-9c7b-e488e345f775.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/basic-extra-meal/instanceof-jvm-b676fee6-bfd4-4ae9-9c7b-e488e345f775.gif) -------- diff --git a/docs/basic-extra-meal/java-naming.md b/docs/basic-extra-meal/java-naming.md index 9c10b9985..9972a2821 100644 --- a/docs/basic-extra-meal/java-naming.md +++ b/docs/basic-extra-meal/java-naming.md @@ -44,7 +44,7 @@ tag: - 尽量不要省略成单词的首字母,但以下情形例外:DO/BO/DTO/VO/AO/ PO / UID 等 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-grammar/fifteen-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-grammar/fifteen-01.png) 另外,如果是抽象类的话,使用 Abstract 或 Base 开头;如果是异常类的话,使用 Exception 结尾;如果是测试类的话,使用 Test 结尾。 diff --git a/docs/basic-extra-meal/java-unicode.md b/docs/basic-extra-meal/java-unicode.md index 79529d39a..33afae04d 100644 --- a/docs/basic-extra-meal/java-unicode.md +++ b/docs/basic-extra-meal/java-unicode.md @@ -20,7 +20,7 @@ ASCII 码由电报码发展而来,第一版标准发布于 1963 年,最后 ASCII 码的局限在于只能显示 26 个基本拉丁字母、阿拉伯数字和英式标点符号,因此只能用于显示现代美国英语,对于其他一些语言则无能无力,比如在法语中,字母上方有注音符号,它就无法用 ASCII 码表示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/ten-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/ten-01.png) PS:拉丁字母(也称为罗马字母)是多数欧洲语言采用的字母系统,是世界上最通行的字母文字系统,是罗马文明的成果之一。 @@ -28,15 +28,15 @@ PS:拉丁字母(也称为罗马字母)是多数欧洲语言采用的字母 在我们的印象中,可能说拉丁字母多少有些陌生,说英语字母可能就有直观的印象了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/ten-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/ten-02.png) PPS:阿拉伯数字,我们都很熟悉了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/ten-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/ten-03.png) 但是,阿拉伯数字并非起源于阿拉伯,而是起源于古印度。学过历史的我们应该有一些印象,阿拉伯分布于西亚和北非,以阿拉伯语为主要语言,以伊斯兰教为主要信仰。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/ten-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/ten-04.png) 处在这样的地理位置,做起东亚和欧洲的一些生意就很有优势,于是阿拉伯数字就由阿拉伯人传到了欧洲,因此得名。 @@ -46,7 +46,7 @@ PPPS:英式标点符号,也叫英文标点符号,和中文标点符号很 在很多人的印象中,古文是没有标点符号的,但管锡华博士研究指出,中国早在先秦时代就有标点符号了,后来融合了一些英文标点符号后,逐渐形成了现在的中文标点符号。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/ten-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/ten-05.png) **2)Unicode** @@ -61,7 +61,7 @@ PS:常用字大概 2500 个,次常用字 1000 个。 要知道,世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/ten-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/ten-06.png) PPS:这“锟斤拷”价格挺公道的啊!!!(逃 @@ -71,7 +71,7 @@ PPS:这“锟斤拷”价格挺公道的啊!!!(逃 Unicode 至今仍在不断增修,每个新版本都会加入更多新的字符。目前最新的版本为 2020 年 3 月公布的 13.0,收录了 13 万个字符。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/ten-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/ten-07.png) Unicode 是一个很大的集合,现在的规模可以容纳 100 多万个符号。每个符号的编码都不一样,比如,`U+0639`表示阿拉伯字母 `Ain`,`U+0041` 表示英语的大写字母 `A`,`U+4E25` 表示汉字`严`。 @@ -164,7 +164,7 @@ UTF-16 使用 2 个或者 4 个字节来存储字符。 这个 `𐐷` 字符很特殊,Unicode 编码是 `U+10437`,它就无法使用一个 char 来表示,当你尝试用 char 来表示时,它会被 IDEA 转成 UTF-16 十六进制字符代码 `\uD801\uDC37`(与此同时,编译器会提醒你最好把它声明成 String 类型)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/ten-08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/ten-08.png) 也就是说,在 Java 中,char 会占用两个字节,超出 char 的承受范围('\u0000'(0)和 '\uffff'(65,535))的字符,都将无法表示。 diff --git a/docs/basic-extra-meal/jdk9-char-byte-string.md b/docs/basic-extra-meal/jdk9-char-byte-string.md index 50242961f..6b9923b80 100644 --- a/docs/basic-extra-meal/jdk9-char-byte-string.md +++ b/docs/basic-extra-meal/jdk9-char-byte-string.md @@ -17,7 +17,7 @@ tag: 以我正在运行着的编程喵喵项目实例(基于 Java 8)来说,结果是这样的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/basic-extra-meal/jdk9-char-byte-string-d826ce88-bbbe-47a3-a1a9-4dd86dd3632f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/basic-extra-meal/jdk9-char-byte-string-d826ce88-bbbe-47a3-a1a9-4dd86dd3632f.png) 其中 String 对象有 17638 个,占用了 423312 个字节的内存,排在第三位。 diff --git a/docs/basic-extra-meal/override-overload.md b/docs/basic-extra-meal/override-overload.md index bbff22d1a..6e6a256bd 100644 --- a/docs/basic-extra-meal/override-overload.md +++ b/docs/basic-extra-meal/override-overload.md @@ -27,7 +27,7 @@ tag: 话音刚落,我就在 IDEA 中噼里啪啦地敲了起来。两段代码,分别是方法重写和方法重载。然后,把这两段代码截图到 draw.io(一个很漂亮的在线画图网站)上,加了一些文字说明。最后,打开 Photoscape X,把两张图片合并到了一起。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/21-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/21-01.png) ### 02、方法重载 @@ -101,7 +101,7 @@ class Adder { “编译时报错优于运行时报错,所以当两个方法的名字相同,参数个数和类型也相同的时候,虽然返回值类型不同,但依然会提示方法已经被定义的错误。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/21-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/21-02.png) “你想啊,三妹。我们在调用一个方法的时候,可以指定返回值类型,也可以不指定。当不指定的时候,直接指定 `add(1, 2)` 的时候,编译器就不知道该调用返回 int 的 `add()` 方法还是返回 double 的 `add()` 方法,产生了歧义。” @@ -139,7 +139,7 @@ String[] args “由于可以通过改变参数类型的方式实现方法重载,那么当传递的参数没有找到匹配的方法时,就会发生隐式的类型转换。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/21-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/21-03.png) “如上图所示,byte 可以向上转换为 short、int、long、float 和 double,short 可以向上转换为 int、long、float 和 double,char 可以向上转换为 int、long、float 和 double,依次类推。” @@ -214,7 +214,7 @@ public class OverloadingTypePromotion2 { “二哥,我又想到一个问题。当有两个方法 `sum(long a, int b)` 和 `sum(int a, long b)`,参数个数相同,参数类型相同,只不过位置不同的时候,会发生什么呢?” “当通过 `obj.sum(20, 20)` 来调用 sum 方法的时候,编译器会提示错误。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/21-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/21-04.png) “不明确,编译器会很为难,究竟是把第一个 20 从 int 转成 long 呢,还是把第二个 20 从 int 转成 long,智障了!所以,不能写这样让编译器左右为难的代码。” diff --git a/docs/basic-extra-meal/pass-by-value.md b/docs/basic-extra-meal/pass-by-value.md index 69f549af0..fc9d5fea4 100644 --- a/docs/basic-extra-meal/pass-by-value.md +++ b/docs/basic-extra-meal/pass-by-value.md @@ -40,7 +40,7 @@ age 是基本类型,值就保存在变量中,而 name 是引用类型,变 “画幅图。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/pass-by-value-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/pass-by-value-01.png) 当用 = 赋值运算符改变 age 和 name 的值时。 @@ -53,7 +53,7 @@ name = "三妹"; 对于引用类型 name,赋值运算符会改变对象引用中保存的地址,原来的地址被覆盖,但原来的对象不会被覆盖。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/pass-by-value-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/pass-by-value-02.png) “三妹,注意听,接下来,我们来说说基本数据类型的参数传递。” @@ -122,11 +122,11 @@ class ReferenceTypeDemo { 在调用 `modify()` 方法的时候,形参 name1 复制了 name 的地址,指向的是堆中“二哥”的位置。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/pass-by-value-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/pass-by-value-03.png) 当 `modify()` 方法调用结束后,改变了形参 name1 的地址,但 `main()` 方法中 name 并没有发生改变。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-points/pass-by-value-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-points/pass-by-value-04.png) 总结: diff --git a/docs/basic-extra-meal/true-generic.md b/docs/basic-extra-meal/true-generic.md index 52a000691..f96d33723 100644 --- a/docs/basic-extra-meal/true-generic.md +++ b/docs/basic-extra-meal/true-generic.md @@ -114,7 +114,7 @@ String s = a.get(); 对吧?这就是我们想要的“真正意义上的泛型”,A 不仅仅可以是引用类型 String,还可以是基本数据类型。要知道,Java 的泛型不允许是基本数据类型,只能是包装器类型。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/generic/true-generic-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/generic/true-generic-01.png) 除此之外,Pizza 的泛型还可以直接使用 `new` 关键字进行声明,并且 Pizza 编译器会从构造方法的参数上推断出具体的对象类型,究竟是 String 还是 int。要知道,Java 的泛型因为类型擦除的原因,程序员是无法知道一个 ArrayList 究竟是 `ArrayList` 还是 `ArrayList` 的。 @@ -185,7 +185,7 @@ Java 一直以来都强调兼容性,我认为这也是 Java 之所以能被广 但 Java 并不支持高版本 JDK 编译生成的字节码文件在低版本的 JRE(Java 运行时环境)上跑。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/generic/true-generic-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/generic/true-generic-02.png) 针对泛型,兼容性具体表现在什么地方呢?来看下面这段代码。 @@ -214,7 +214,7 @@ Java 神奇就神奇在这,表面上万物皆对象,但为了性能上的考 一个好消息是 Valhalla 项目正在努力解决这些因为泛型擦除带来的历史遗留问题。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/generic/true-generic-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/generic/true-generic-03.png) Project Valhalla:正在进行当中的 OpenJDK 项目,计划给未来的 Java 添加改进的泛型支持。 diff --git a/docs/basic-extra-meal/varables.md b/docs/basic-extra-meal/varables.md index e7abc6ac3..bb57ec188 100644 --- a/docs/basic-extra-meal/varables.md +++ b/docs/basic-extra-meal/varables.md @@ -30,11 +30,11 @@ public static void print(String... strs) { 说到可变参数,我想起来阿里巴巴开发手册上有这样一条规约。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/basic-extra-meal/varables-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/basic-extra-meal/varables-01.png) 意思就是尽量不要使用可变参数,如果要用的话,可变参数必须要在参数列表的最后一位。既然坑位有限,只能在最后,那么可变参数就只能有一个(悠着点,悠着点)。如果可变参数不在最后一位,IDE 就会提示对应的错误,如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/basic-extra-meal/varables-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/basic-extra-meal/varables-02.png) @@ -117,7 +117,7 @@ public static void print(Integer... ints) { 这时候,编译器完全不知道该调用哪个 `print()` 方法,`print(String... strs)` 还是 `print(Integer... ints)`,傻傻分不清。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/basic-extra-meal/varables-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/basic-extra-meal/varables-03.png) 假如真的需要重载带有可变参数的方法,就必须在调用方法的时候给出明确的指示,不要让编译器去猜。 diff --git a/docs/basic-grammar/basic-data-type.md b/docs/basic-grammar/basic-data-type.md index 4fdb85d0a..f8412db98 100644 --- a/docs/basic-grammar/basic-data-type.md +++ b/docs/basic-grammar/basic-data-type.md @@ -23,13 +23,13 @@ Java 中的数据类型可分为 2 种: 来个思维导图,感受下。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-grammar/nine-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-grammar/nine-01.png) 通过[上一节](https://mp.weixin.qq.com/s/IgBpLGn0L1HZymgI4hWGVA)的学习,我们知道变量可以分为局部变量、成员变量、静态变量。 当变量是局部变量的时候,必须得先初始化,否则编译器不允许你使用它。拿 int 来举例吧,看下图。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-grammar/nine-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-grammar/nine-02.png) 当变量是成员变量或者静态变量时,可以不进行初始化,它们会有一个默认值,仍然以 int 为例,来看代码: @@ -87,7 +87,7 @@ public class LocalVar { (终于知道 1024 和程序员的关系了吧?狗头保命) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-grammar/nine-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-grammar/nine-03.png) 接下来,我们再来详细地了解一下 8 种基本数据类型。 @@ -169,11 +169,11 @@ double d1 = 12.3 Tips:单精度是这样的格式,1 位符号,8 位指数,23 位小数,有效位数为 7 位。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-grammar/nine-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-grammar/nine-04.png) 双精度是这样的格式,1 位符号,11 位指数,52 为小数,有效位数为 16 位。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-grammar/nine-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-grammar/nine-05.png) 取值范围取决于指数位,计算精度取决于小数位(尾数)。小数位越多,则能表示的数越大,那么计算精度则越高。 @@ -248,7 +248,7 @@ arrays 是一个 int 类型的数组,对吧?打印结果如下所示: `[I` 表示数组是 int 类型的,@ 后面是十六进制的 hashCode——这样的打印结果太“人性化”了,一般人表示看不懂!为什么会这样显示呢?查看一下 `java.lang.Object` 类的 `toString()` 方法就明白了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-grammar/nine-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-grammar/nine-06.png) 数组虽然没有显式定义成一个类,但它的确是一个对象,继承了祖先类 Object 的所有方法。那为什么数组不单独定义一个类来表示呢?就像字符串 String 类那样呢? @@ -294,7 +294,7 @@ public class ArrayList extends AbstractList 对于接口类型的引用变量来说,你没法直接 new 一个: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-grammar/nine-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-grammar/nine-07.png) 只能 new 一个实现它的类的对象——那自然接口也是引用数据类型了。 diff --git a/docs/basic-grammar/flow-control.md b/docs/basic-grammar/flow-control.md index 56e9518d8..c36c6c118 100644 --- a/docs/basic-grammar/flow-control.md +++ b/docs/basic-grammar/flow-control.md @@ -13,7 +13,7 @@ tag: ### 01、if-else 相关 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/control/thirteen-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/control/thirteen-01.png) **1)if 语句** @@ -28,7 +28,7 @@ if(布尔表达式){ 画个流程图表示一下: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/control/thirteen-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/control/thirteen-02.png) 来写个示例: @@ -64,7 +64,7 @@ if(布尔表达式){ 画个流程图表示一下: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/control/thirteen-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/control/thirteen-03.png) 来写个示例: @@ -148,7 +148,7 @@ else{ 画个流程图表示一下: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/control/thirteen-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/control/thirteen-04.png) 来写个示例: @@ -191,7 +191,7 @@ if(外侧条件){ 画个流程图表示一下: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/control/thirteen-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/control/thirteen-05.png) 来写个示例: @@ -251,7 +251,7 @@ default: // 该关键字是可选项 画个流程图: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/control/thirteen-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/control/thirteen-06.png) @@ -359,7 +359,7 @@ public class SwitchEnumDemo { ### 03、for 循环 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/control/thirteen-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/control/thirteen-07.png) **1)普通 for 循环** @@ -389,7 +389,7 @@ for(初识变量;条件;自增/自减){ 画个流程图: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/control/thirteen-08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/control/thirteen-08.png) @@ -532,7 +532,7 @@ while(条件){ 画个流程图: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/control/thirteen-09.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/control/thirteen-09.png) @@ -614,7 +614,7 @@ do{ 画个流程图: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/control/thirteen-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/control/thirteen-10.png) @@ -681,7 +681,7 @@ public class InfinitiveDoWhileExample { 把 do-while 的条件设置为 true,并且循环体中没有 break 关键字的话,程序一旦运行起来,就根本停不下来了,除非强制停止。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/control/thirteen-11.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/control/thirteen-11.png) ### 06、break @@ -692,7 +692,7 @@ break 关键字通常用于中断循环或 switch 语句,它在指定条件下 来画个流程图感受一下: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/control/thirteen-12.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/control/thirteen-12.png) 用在 for 循环中的示例: diff --git a/docs/basic-grammar/javadoc.md b/docs/basic-grammar/javadoc.md index a5dd2986f..53157fbc2 100644 --- a/docs/basic-grammar/javadoc.md +++ b/docs/basic-grammar/javadoc.md @@ -11,7 +11,7 @@ tag: “注释的种类确实不多,但还是挺有意思的,且听哥来给你说道说道。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/fourteen-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/fourteen-01.png) @@ -28,7 +28,7 @@ public void method() { **但如果写在行尾的话,其实是不符合阿里巴巴的开发规约的**。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/fourteen-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/fourteen-02.png) 正确的单行注释如上图中所说,在被注释语句上方另起一行,使用 `//` 注释。 @@ -93,24 +93,24 @@ PS:在 Intellij IDEA 中,按下 `/**` 后敲下回车键就可以自动添 **第一步**,在该类文件上右键,找到「Open in Terminal」 可以打开命令行窗口。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/fourteen-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/fourteen-03.png) **第二步**,执行 javadoc 命令 `javadoc Demo.java -encoding utf-8`。`-encoding utf-8` 可以保证中文不发生乱码。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/fourteen-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/fourteen-04.png) **第三步,**执行 `ls -l` 命令就可以看到生成代码文档时产生的文件,主要是一些可以组成网页的 html、js 和 css 文件。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/fourteen-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/fourteen-05.png) **第四步**,执行 `open index.html` 命令可以通过默认的浏览器打开文档注释。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/fourteen-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/fourteen-06.png) 点击「Demo」,可以查看到该类更具体的注释文档。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/fourteen-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/fourteen-07.png) ### 04、文档注释的注意事项 @@ -120,7 +120,7 @@ default 和 private 修饰的字段和方法的注释将会被忽略掉。因为 如果类不是 public 的话,javadoc 会执行失败。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/fourteen-08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/fourteen-08.png) 2)文档注释中可以嵌入一些 HTML 标记,比如说段落标记 `

`,超链接标记 `` 等等,但不要使用标题标记,比如说 `

`,因为 javadoc 会插入自己的标题,容易发生冲突。 @@ -132,7 +132,7 @@ default 和 private 修饰的字段和方法的注释将会被忽略掉。因为 比如说,在使用 String 类的时候,鼠标悬停在 String 上时可以得到以下提示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/fourteen-09.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/fourteen-09.png) 2)所有的抽象方法(包括接口中的方法)必须要用Javadoc注释、除了返回值、参数、 异常说明外,还必须指出该方法做什么事情,实现什么功能。 @@ -140,7 +140,7 @@ default 和 private 修饰的字段和方法的注释将会被忽略掉。因为 Intellij IDEA 中可以在「File and Code Templates」中设置。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/fourteen-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/fourteen-10.png) 语法如下所示: diff --git a/docs/basic-grammar/operator.md b/docs/basic-grammar/operator.md index f62b57e83..adefa2d6f 100644 --- a/docs/basic-grammar/operator.md +++ b/docs/basic-grammar/operator.md @@ -11,7 +11,7 @@ tag: “是的,三妹。运算符在 Java 中占据着重要的位置,对程序的执行有着很大的帮助。除了常见的加减乘除,还有许多其他类型的运算符,来看下面这张思维导图。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-grammar/eleven-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-grammar/eleven-01.png) ### 01、算数运算符 @@ -118,7 +118,7 @@ System.out.println(y + " " + x);// 10 11 关系运算符用来比较两个操作数,返回结果为 true 或者 false。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-grammar/eleven-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-grammar/eleven-02.png) 来看示例: @@ -159,7 +159,7 @@ public class BitOperator { PS:现代的二进制记数系统由戈特弗里德·威廉·莱布尼茨于 1679 年设计。莱布尼茨是德意志哲学家、数学家,历史上少见的通才。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-grammar/eleven-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-grammar/eleven-03.png) 来看示例: @@ -298,7 +298,7 @@ public class AssignmentOperator { 不过在进行数值的赋值时,需要小点心,比如说下面这种情况: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/core-grammar/eleven-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/core-grammar/eleven-04.png) 编译器之所以提示错误,是因为 = 右侧的算术表达式默认为 int 类型,左侧是 short 类型的时候需要进行强转。 diff --git a/docs/cityselect/beijing.md b/docs/cityselect/beijing.md index 0b167aec3..df644b8e2 100644 --- a/docs/cityselect/beijing.md +++ b/docs/cityselect/beijing.md @@ -13,7 +13,7 @@ tag: 因为北京适合程序员工作的企业实在太多了,所以怎样给大家介绍北京适合程序员的工作机会一直让我十分头疼。如果写的太简单,我跟大家说北京有百度、腾讯、阿里、美团等等,大家一定觉得我是在废话,但把所有企业一次性全介绍了工作量太大,并且文章也看不出重点了。所以我最终决定,北京拆成三篇来讲,一篇介绍北京的国企央企研究所、一篇介绍北京的大型互联网公司和大型外企、一篇介绍北京的中小型互联网公司。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-68628d58-37ff-44c8-a3f6-6b9f4978f976.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-68628d58-37ff-44c8-a3f6-6b9f4978f976.jpg) 我发现在互联网公司工作的程序员都对国企央企以及研究所十分好奇,但是因为这些单位的信息比较封闭,所以大家对于这些企业的工作方式以及薪酬情况都都不清楚。为了能给北京系列开个好头,第一篇文章我就以我熟悉的北京的国企央企研究所来给大家介绍了。想提前声明一点,虽然我有很多同学和朋友在北京国企央企以及研究所,但是同一个公司,不同的部门或者不同的组差别都太大了。尤其是研究所,每个系列都有数量庞大的下属研究所,并且下属的研究所独立性都特别高,所以风格差别很大。我只能把我见到的情况给大家做一个介绍,具体去到哪个研究所哪个团队还得你们自己去详细打听。只能说我的介绍相对于更接近一些真实。如果大家发现哪里有误,欢迎大家指出。 @@ -29,7 +29,7 @@ tag: #### 工商银行软件开发中心 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-b8653934-cc7f-4703-81f2-f654f7d4440a.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-b8653934-cc7f-4703-81f2-f654f7d4440a.png) 和同行相比,工商银行的软件开发中心算技术最好的了。工行喜欢招应届生,社招进来的比较少。刚进来的应届生职级是助理经理,税前年薪差不多是20万左右(到手15-16万吧,加上年终奖和节假日补贴)。工资发放方式稍微有点奇怪,就是每月发八九千,然后留一部分年底一并发放。每月会有饭补,过节也会有节假日补贴(放三天的节日假期给 1000,国庆给 2000,过年 4000)。这个职级的工资对于在北京工作来说还是有点难顶的,毕竟光跟人合租每月就要画掉两千多了。如果你是本科毕业进入工行,从助理经理升到经理一需要两年时间,硕士只需要一年,这个职级基本上全都能按时达到,不过工资变化不太大。从经理一升到经理二大约要一到两年的时间,这就看你能力了,经理二以后工资就相对多一些了,经理二绩效不太好的话每年到手能拿 22 万左右,如果绩效好,可能能拿到 29 万左右。经理二升经理三就完全看你自己能力了,到经理三以后每年到手都能 35 万左右,收入就很可观了。另外在工行发专利什么的也有些奖金,一个专利貌似是奖励 5000. @@ -37,25 +37,25 @@ tag: #### 农业银行软件开发中心 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-fb046afd-1116-4619-a5ae-d9756863ca19.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-fb046afd-1116-4619-a5ae-d9756863ca19.png) 感觉农业银行的软件开发中心技术比工行差一点,不过工资比工行多一些,刚毕业的应届生税前能到税前25万-32万(这是hr说的啊,我了解到的西安每月税后到手是 1万2,加上年终奖税前能算 20 万的年包,北京比西安稍微多一点点,但是多不了太多)。农行软件开发中心有个大问题就是工资分配很平均,但是工作量分布不均,有的组十分养生,有的组忙的受不了(越接近客户用的产品线越累)。我的一个学长天天早早就下班了,另外一个同学周末跟我吃个饭都急匆匆的,为了投产要加班到凌晨。目前农行内部的怨气比较大,这点大家在脉脉上也能看出来。农行想要升职还要去考软考。 #### 中国银行软件开发中心 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-e3490a86-2b92-4a47-8c68-8e2281c2cb32.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-e3490a86-2b92-4a47-8c68-8e2281c2cb32.png) 中国银行软件开发中心算是给钱最少的了,年包15万左右。这钱在北京确实太少了。不过工作是目前几个银行软开里最轻松的,家里不怎么缺钱,只想找个轻松稳定的可以选。 #### 邮政储蓄银行软件研发中心 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-4e15e06c-f808-4d79-954f-3fbca94ce6e6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-4e15e06c-f808-4d79-954f-3fbca94ce6e6.png) 第一年包含试用期,年薪税前总包 26w, 第二年年薪税前总包 31w。加不加班看你运气了,运气好被分到好的组不怎么加班,运气不好就很惨了。在北京工作的话不会被外调,但是在西安等地工作的许多人会被外调。 ### 券商 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-58cd1949-b992-4550-b2e1-b24c519c0a78.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-58cd1949-b992-4550-b2e1-b24c519c0a78.png) 曾经农行软开的一位领导来我们学校校招宣讲时就说因为银行的工资有限制,涨薪比较慢。所以很多银行软开的职工干几年后就会跑到券商去挣钱。那时我才知道券商居然还招不少程序员。后来我发现中信建投证券是真不错,北京这边校招生进来月薪 18k ,年终奖 6 个月,虽然薪资还是不及互联网公司,但是涨薪速度相比于其它国企央企还是快很多的。另外只有股票开市的日子用上班,加班很少。不过对于学历的要求比较高,必须是985硕士,并且还卡本科。 @@ -63,7 +63,7 @@ tag: ### 运营商 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-1b67f498-dc2e-401f-b942-59455b941bd9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-1b67f498-dc2e-401f-b942-59455b941bd9.png) 这里说的运营商说的是移动联通还有电信的研究院。移动的工资发放风格和工行有异曲同工之妙,每个月发的工资很少,到年终会一起再发一些。应届生年薪大概 20w 左右,平时工资发一半,年终把剩下的一半发给你。联通在北京的研究院有两个,一个叫联通研究院,一个叫联通软件研究院。我听说的是联通研究院比较好,联通软件研究院比较坑。 电信研究院的据说很轻松,但是工资低,没朋友在这里,我不太确定,只是听说。 @@ -79,7 +79,7 @@ tag: ### 航天科技&航空工业 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-fcff3f6d-6357-48fe-a807-74c225ce0c95.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-fcff3f6d-6357-48fe-a807-74c225ce0c95.png) 我有很多的校友都在航空航天系列的研究所。研究所的具体薪资都比较保密,大部分研究所在给你 offer 的时候不会像互联网公司那样把薪资给你说的清清楚楚并且写在合同上,而是大概和你说个数。而且你入职后两到三个月的薪资往往都很少,等三四个月以后薪资稳定了你才能确定你开多少钱。 @@ -89,13 +89,13 @@ tag: ### 工信部下属研究院 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-0f3b7783-4567-4f07-974d-a31b8d9ec939.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-0f3b7783-4567-4f07-974d-a31b8d9ec939.png) 工信部下属的研究院整体上都不错,就比如信通院(中国信息通信研究院),如果你对这个单位陌生的话你可以看看你的行程码下面服务提供商排在第一个的是谁。信通院是制定标准的单位,也是监理单位。在信通院的话代码就很少写了,更多就是写文档了,不过信通院这种地方的视野肯定高一些。薪资的话信通院每个职工薪资差别很大,刚进去工资很低,可能就四五千块钱,但是薪资每个月都会变化,然后稳定在一个数。薪资低的每月到手有一万出头,但我知道每月到手两三万甚至更多的也是有的(不是领导啊,只是职工)。 ### 中科院系列 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-96c5f7a6-b13b-4153-9c7f-7c4617dbddca.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-96c5f7a6-b13b-4153-9c7f-7c4617dbddca.png) 中科院系列下计算机相关最强的三个所就是自动化所(搞 nlp 的应该都听过宗成庆老师)、软件所、计算所。另外还有信工所、电子所也还凑合。整体待遇上,中科院系列的待遇不如航空航天系列的。你可以把中科院系列的研究所完全的想象成一个大学(嗯,不用想象,确实是个大学,中国科学院大学嘛)。每个团队就是你们研究生的实验室。基本上就是一个大老板,带几个小老板,再带几个职工,另外再配几个中科院的学生。课题组想要挣钱也是需要大老板出去拉活,然后小老板带着学生去干活。工作方式和技术水平和你们研究生的实验室一模一样的,就是多了一些硕士博士职工罢了。 @@ -103,13 +103,13 @@ tag: ### 中电科系列 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-a4804493-fdb0-48ea-b9f5-82ad9a3d3d08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-a4804493-fdb0-48ea-b9f5-82ad9a3d3d08.png) 中电科系列的待遇比航空航天系列稍差,比中科院系列稍强一些。我感觉中电科系列的加班强度比较高,认识的几个在中电科系列的经常 996 甚至 007,还经常出差。待遇的话基本工资也是每月1万出头,然后出差也是有出差补贴,每天300 或者400,具体要看团队。感觉工资主要是靠出差补贴堆起来的。大部分下属院所会提供职工宿舍,中电科系列下属研究所风格差别很大,好起来是真好,坑起来是真坑啊。 ### 中国兵器系列 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-db583262-a5d4-4137-88e1-90f0127ba755.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-db583262-a5d4-4137-88e1-90f0127ba755.png) 中国兵器系列计算机相关专业招的不是太多,其实每年一共招的人也不多。我最初对中国兵器系列的研究所有了解是一个所来我们学校宣讲。感觉中国兵器在西安的几个所能算的上是西安研究所的天花板了吧,待遇还不错(和航天504所比我有点不太确定)但是在北京我就不太清楚了。 @@ -121,7 +121,7 @@ tag: ## 生活方面 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-799a0d1a-7782-4e3c-8a36-4c495cd94ae5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-799a0d1a-7782-4e3c-8a36-4c495cd94ae5.png) 北京具体的房价、美食教育等情况我放到下一篇讲北京互联网公司的文章中好好去讲下。这篇文章只讲一个事,那就是**北京户口**。北京户口的主要用处就是子女教育问题,其它的一些好处就是有北京户口可以申请共有产权房还有摇车牌等。应届毕业生是最好搞定户口的时候了,如果你能进这篇文章中讲的这些单位,大概率你是有户口的。不过拿户口的同时都要签一个五年的服务期合同,违约金每个单位不一样, 20万-50万不等,逐年递减。对于应届生来说户口貌似吸引力没有特别的大,毕竟现在自己还顾不过来,也不知道自己子女在哪呢。但是对于已经有子女的人来说,我一提给北京户口的事他们眼睛就亮了。 @@ -139,7 +139,7 @@ tag: #### 百度 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-7d1367ae-04c1-4162-aed8-2a99203a623d.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-7d1367ae-04c1-4162-aed8-2a99203a623d.jpg) 坐标:西北旺 @@ -151,7 +151,7 @@ tag: #### 腾讯 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-7d6cd775-8895-447a-a348-27443594bdcd.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-7d6cd775-8895-447a-a348-27443594bdcd.jpg) 坐标:西北旺 @@ -166,7 +166,7 @@ tag: #### 阿里巴巴 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-996bbbca-dd9c-4108-b796-46f29dd840e9.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-996bbbca-dd9c-4108-b796-46f29dd840e9.jpg) 坐标:望京 @@ -176,7 +176,7 @@ tag: #### 字节跳动 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-fd757b6c-154c-4aa6-95a8-467d6a9a83b4.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-fd757b6c-154c-4aa6-95a8-467d6a9a83b4.jpg) 坐标:中航广场 @@ -186,7 +186,7 @@ tag: #### 美团 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-809a1c71-7a8f-43c8-ad4b-befab2fe5cf2.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-809a1c71-7a8f-43c8-ad4b-befab2fe5cf2.jpg) 坐标:望京 @@ -196,7 +196,7 @@ tag: #### 京东 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-e08e1eef-6561-4057-a12e-3a1183fb9ae1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-e08e1eef-6561-4057-a12e-3a1183fb9ae1.png) 坐标:亦庄 @@ -206,7 +206,7 @@ tag: #### 网易 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-fc7b1b08-0363-4af6-adc2-e6185c55bb9c.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-fc7b1b08-0363-4af6-adc2-e6185c55bb9c.png) 坐标:西北旺 @@ -222,7 +222,7 @@ tag: #### Hulu -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-483d5154-671c-4ef4-844c-3240f34b6008.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-483d5154-671c-4ef4-844c-3240f34b6008.png) 坐标:望京 @@ -232,7 +232,7 @@ tag: #### 微软 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-e0dbb967-2bb3-453f-8384-02c1c0aa61a0.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-e0dbb967-2bb3-453f-8384-02c1c0aa61a0.png) 坐标:离五道口也不远。 @@ -240,7 +240,7 @@ tag: #### Shoppe(虾皮) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-ea99812c-fdb6-44a6-bc55-79c15ad37a3a.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-ea99812c-fdb6-44a6-bc55-79c15ad37a3a.jpg) 坐标:五道口附近,和搜狗挨着。那片有好多互联网公司。 @@ -250,7 +250,7 @@ tag: #### Amazon -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-9a9d93ca-89fa-4b67-a491-d085eb2601d3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-9a9d93ca-89fa-4b67-a491-d085eb2601d3.png) 坐标:朝阳区远洋国际中心 @@ -260,7 +260,7 @@ tag: #### Apple -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-ba5e8a95-1cf8-42f9-aacc-3573cd93f19f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-ba5e8a95-1cf8-42f9-aacc-3573cd93f19f.png) 坐标:朝阳区建国门外大街国贸大厦 @@ -268,7 +268,7 @@ tag: #### Airbnb -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-b9d6ef38-e7c1-4e00-8409-2f63489a5d6c.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-b9d6ef38-e7c1-4e00-8409-2f63489a5d6c.png) 坐标:朝阳区环球金融中心 @@ -290,11 +290,11 @@ tag: 下面就说买房的事了,算了,我对北京买房的事也没什么见解。贴个图你们自己看吧,数据不一定准,就感受下房价有多高吧....密云延庆什么的就太远了,当然有很多人在北京上班在天津买房或者廊坊买房的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-e5bc2cf6-647d-43f9-9352-934440d71b16.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-e5bc2cf6-647d-43f9-9352-934440d71b16.png) ### 美食 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-148db131-6c78-4cbf-86a0-465d88fdd217.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-148db131-6c78-4cbf-86a0-465d88fdd217.png) 有一句话叫“京城无美食”。虽然这句话有点极端但是也有点道理的。美食的聚集地大部分是沿山沿海沿河沿江的地方,北京一直不是一个美食原料富足的地方。北京常说的北京名菜特点不像鲁菜川菜江苏菜那样特点鲜明。我能想到的北京特色的饭也就是烤鸭、涮羊肉、爆肚、炒肝、京酱肉丝、炸酱面,除了这些还真不太好想了。在北京有个好处是你能在这里吃到全国各地的美食,因为北京全国各地甚至世界各地的人都有,所以各省各国风味的餐厅都会有,但是感觉风味上还是根据北京这边的口味做了一些改变。 @@ -302,13 +302,13 @@ tag: ### 教育&医疗 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-dc5e04dd-8a49-45b2-b96d-e1a17a2405f2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-dc5e04dd-8a49-45b2-b96d-e1a17a2405f2.png) 北京的教育和医疗肯定全国顶级了。再往详细的介绍我觉得我写不出什么有深度的东西了,对于教育和医疗来说,我觉得我这个刚毕业不到一年的人肯定不如在北京待了多年的并且有孩子的了解的多。我只是知道虽然北京的高考会比其它省份容易很多,但北京一贯实行的素质教育对于孩子家长来说会很累,今天陪孩子学个交谊舞,明天学个马术,后天学校开个家长联谊会。对于 996 或者 995 的程序员家长来说真的有点难顶,不像我们上学时很少叫家长了。 ### 娱乐 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/beijing-58c387f7-575f-4c2f-9702-e62d7b7575dc.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/beijing-58c387f7-575f-4c2f-9702-e62d7b7575dc.png) 北京值得去的玩的地方很多的,圆明园、颐和园、故宫、香山、各种胡同、各种博物馆、各种茶馆。听个相声、听个演唱会,看个体育比赛都是不错的休闲方式。去各种商场转一转买点东西也是休闲。当然因为北京这么大所以周末出来玩一趟会特别花时间。北京的国家级景区票价是比较便宜的,我记得颐和园门票也就四五十,不像其它地区的景区动辄好几百的门票。 diff --git a/docs/cityselect/guangzhou.md b/docs/cityselect/guangzhou.md index 036073c2e..453ad09e9 100644 --- a/docs/cityselect/guangzhou.md +++ b/docs/cityselect/guangzhou.md @@ -15,7 +15,7 @@ tag: 广州的繁华以及它的魅力不用我多说,无论从人文还是从经济来说都是不虚其它城市的。不过我在面试时发现,广州除了几个头部大厂是统一薪资标准外,相较于其它一线城市,广州的互联网行业给我的感觉是整体薪资水平偏低。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/guangzhou-0f0eb358-f7ae-46b9-b070-8b93e401735e.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/guangzhou-0f0eb358-f7ae-46b9-b070-8b93e401735e.png) 大家注意呀,其实学计算机相关专业的想在广州挣钱,不止可以通过互联网公司,也可以通过当公务员,教师等方式去挣钱呀。应届生考进体制内当公务员,年薪也20多万呢,并且各方面福利待遇绝对到位,相当不错了。下面我们还是分互联网公司以及国企央企研究所介绍吧。 @@ -27,61 +27,61 @@ tag: **网易** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/guangzhou-53893155-f8cb-4bfe-b0b4-768c43c03862.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/guangzhou-53893155-f8cb-4bfe-b0b4-768c43c03862.png) 网易在广州主要是游戏部门。算是网易的核心业务了。注意,网易面试时候是散装的,网易互娱、网易雷火和网易互联网是分开招聘的,我当时投简历都是分开给他们投的,同样的简历我在网站上填了三次,累死。不过好处就是你有三次机会能进网易 ~ 注意,互娱、雷火、互联网的薪资方案也是不一样的。网易的薪资方案很复杂,这里面水太深,我感觉我有点把握不住~ 我知道有人拿到sp大概就是 25k * 16吧,还会有股票。网易食堂的伙食是真的好,大部分人去了工作一段时间都胖了。 **微信** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/guangzhou-f4e50c0c-5214-484a-b5e9-18a20a929745.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/guangzhou-f4e50c0c-5214-484a-b5e9-18a20a929745.png) 广州主要是腾讯的 wxg 事业群,也就是微信。wxg 事业群的效益非常好,能进 wxg 还是很棒的,不过面试难度也挺大的。腾讯去年校招的薪资分 17k,18.5k,20k,21.5k,23k 几个档次,hr说是应届生第一年保证18薪,不过第二年就不保证了哈。注意,腾讯开出的薪资是可以argue的,如果他给你的薪资不满意,你可以用其它offer跟腾讯的 hr argue 更高档次的薪资,有成功的。另外校招生还有签字费以及股票以及租房补贴。腾讯时不时送点小礼物,工作的舒适度还是不错的。 **字节跳动** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/guangzhou-a1c6a805-e7fe-450f-bd18-c24d760b60e7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/guangzhou-a1c6a805-e7fe-450f-bd18-c24d760b60e7.png) 广州的字节整体上不如其它地方的字节加班严重,不过也要看部门,有的部门听说挺累的。字节之前一直是大小周,前不久刚刚宣布要取消大小周,有双休还是挺幸福的,就是要少挣不少钱了,看你是不是奋斗逼了。我看到21届字节校招的薪资主要是每月20k,22k,24k,26k,28k,30k不等。不过我看校招拿到广州这边offer的大部分是22k和24k的月薪, **欢聚时代** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/guangzhou-7d47dd3b-924e-4af3-99e9-85c77dca3d45.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/guangzhou-7d47dd3b-924e-4af3-99e9-85c77dca3d45.png) 大家看到欢聚时代这个名称可能有点陌生,其实 YY 和 虎牙 都是欢聚时代的。欢聚时代的薪资水平我不清楚,我问了朋友也不清楚,我就去查校招薪水上的薪资爆料。有点迷呀,去年校招,开发方向的月薪14k、16k、18k 的都有,但是这算法薪资的爆料咋还直接就 27k 呢?这也差的太多了吧。有靠谱消息的老铁欢迎来补充。 **唯品会** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/guangzhou-bd501c3a-05ac-4a1e-a27c-7645dc6d8ab0.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/guangzhou-bd501c3a-05ac-4a1e-a27c-7645dc6d8ab0.png) 唯品会算是广州本地的一霸了吧,唯品会的福利挺给力的,包三餐,双休,租房补贴。本科生开发的月薪主要集中在14k-16k。 **SHEIN** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/guangzhou-d805d3a7-a5b8-4e5a-a304-e59cac38baad.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/guangzhou-d805d3a7-a5b8-4e5a-a304-e59cac38baad.png) 这家公司我熟,我就给你们展开讲讲。这家公司最近势头很猛,做跨境电商的,主要卖女装。我在去年秋招初期还面试过这家公司,面试难度比较友好。首先是 hr 给我发了个笔试链接,让我几天内找个时间做了。通过笔试后,第一轮面试,面试了大概有 30 多分钟,主要问了项目以及面试八股文,没有让写代码,然后面试就通过了。第二轮面试,面试官随便问了两个八股文问题就开始聊理想了。第三轮面试好像是他们的 cto,问我高考数学考多少分 ~ 问我上学期间大概写了多少行代码~ 然后又聊了几句就跟我结束面试了。就给我 offer了。后续hr跟我谈薪的时候说他们加班少,工资是 16k * 14,我觉得工资有点少,就没接offer。 **三七互娱** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/guangzhou-b7f04bcf-684c-4c89-acb2-5dc8907284a8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/guangzhou-b7f04bcf-684c-4c89-acb2-5dc8907284a8.png) 我了解到三七互娱后端岗位大部分是 PHP,本科生校招进来薪资大概是12k 左右吧,每年发 15个月的工资。在知乎上对三七互娱的讨论比较多,有黑点,每个部门的情况也不太一样,大家可以再去了解下呀。 **小鹏汽车** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/guangzhou-fbcfd7c0-79ad-48c5-ad67-471ff91666a5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/guangzhou-fbcfd7c0-79ad-48c5-ad67-471ff91666a5.png) 小鹏汽车最近风头正盛,应届生校招进来的工资基本在 16k 左右,每年15薪。小鹏汽车目前是大小周呀,工作强度还是比较大的。 **酷狗音乐** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/guangzhou-4e3962cb-109e-4f2d-a372-ba325444d415.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/guangzhou-4e3962cb-109e-4f2d-a372-ba325444d415.png) 酷狗音乐是腾讯的这应该大家都知道,不过内部的薪资职级不是完全对齐的哈。酷狗的不同部门工作强度差别很大,有的闲的要死,有的忙的要死。选择部门时要注意。 **4399** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/guangzhou-6a8653f4-b840-4f46-af16-72fa00b9019b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/guangzhou-6a8653f4-b840-4f46-af16-72fa00b9019b.png) 4399大家应该都不陌生,好多小游戏都是从小开始玩的,工资也在16k左右。 @@ -95,19 +95,19 @@ tag: **移动** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/guangzhou-66cca36c-656b-4e6e-9172-f68d79e78af9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/guangzhou-66cca36c-656b-4e6e-9172-f68d79e78af9.png) 移动每月的工资很低,可能到手就五六千块钱,然后年底会一下再发五六万的年终奖,有时候会更多一些。据说 18 年效益好年底一下发了 8 万,19 年就拉跨了。 **联通** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/guangzhou-cc0cdb0f-034c-4a63-81f6-a17beed70b14.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/guangzhou-cc0cdb0f-034c-4a63-81f6-a17beed70b14.png) 转正后每月到手八九千,绩效好能到手一万左右。 **电信** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/guangzhou-ff7fd7ae-3bf8-4797-bf07-f9490da269b9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/guangzhou-ff7fd7ae-3bf8-4797-bf07-f9490da269b9.png) 我看到 offershow 上有做网络运维的爆料,入职第一年每月到手8.5k,年终发了3.5万。 @@ -127,13 +127,13 @@ tag: ### 房价 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/guangzhou-4b8f3cec-ca0d-4b39-9826-755cd2fe29b6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/guangzhou-4b8f3cec-ca0d-4b39-9826-755cd2fe29b6.png) 大家直接看图,相比于其它一线城市房价算是便宜了。但是按照广州互联网的工资水平想在广州差不多的区域和地段买房,压力是很大的。 ### 教育 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/guangzhou-75763e2a-3b9b-48e4-a5dc-b0308d60e98f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/guangzhou-75763e2a-3b9b-48e4-a5dc-b0308d60e98f.png) 广州的985大学有两所,分别是中山大学和华南理工,211也有两所是暨南大学以及华南师范大学,另外广州大学、广东工业大学等等也都不错。广州的高中也很给力,华南师范大学附属中学、广东实验中学、广东广雅中学、广州市执信中学、广州二中、六中等等学校都很不错。 @@ -141,15 +141,15 @@ tag: 一线城市的医疗资源肯定是没问题的,我粗略数了一下,广州有不下40所三甲医院。我了解到顶级医疗资源主要有中山系、南方系、广中医系等等。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/guangzhou-5b5c6c29-cc79-4c0b-be57-b105babd0141.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/guangzhou-5b5c6c29-cc79-4c0b-be57-b105babd0141.png) ### 风景&美食 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/guangzhou-4dbf092b-e820-4114-8fd8-ab7e0ac59f8d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/guangzhou-4dbf092b-e820-4114-8fd8-ab7e0ac59f8d.png) 广州的美景自然不用多说,另外空气质量也很好,说起空气质量,我这个北方人已经习惯冬天经常见不到太阳了。广州的美食也很多,白切鸡、煲仔饭和肠粉等等,还有很多我就不报菜名了,我在西安上学时食堂有个卖肠粉的窗口,我很爱吃,不过这个肠粉明显是本地化了。话说南北差异还是挺大的,我一个同学去广州上学,去洗澡带着搓澡巾,他的广州舍友都十分的好奇~ 当然广州的同学看我拍雪景也很好奇 ~ 还有一次实验室和厦门大学一起合作做项目,我和一个厦门大学的博士都要了一份豆腐脑,我加韭菜花,他加糖,我两面对面坐着都觉得对方的不能吃。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/guangzhou-1e3954bc-0b41-4302-851c-d07d382040cd.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/guangzhou-1e3954bc-0b41-4302-851c-d07d382040cd.png) >作者:大白,转载链接:[https://mp.weixin.qq.com/s/uZQ8p0ytsQFXzt5ppzx6fA](https://mp.weixin.qq.com/s/uZQ8p0ytsQFXzt5ppzx6fA) diff --git a/docs/cityselect/hangzhou.md b/docs/cityselect/hangzhou.md index 3d5564205..ac505b6f2 100644 --- a/docs/cityselect/hangzhou.md +++ b/docs/cityselect/hangzhou.md @@ -9,11 +9,11 @@ tag: 大家好,我是二哥呀!上期发了南京的互联网公司后,杭州的呼声非常高。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/hangzhou-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/hangzhou-1.png) 有小伙伴在公众号后台私信催,还有小伙伴在二哥的《Java 程序员进阶之路》的开源专栏提交了 issue,那今天必须得来整一波了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/hangzhou-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/hangzhou-2.png) 杭州的互联网公司比较多,我先列举一些大家瞅瞅(部分数据来源于好朋友 Carl 的统计)。 @@ -77,7 +77,7 @@ tag: ### 字节跳动 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/hangzhou-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/hangzhou-3.png) - 「基本情况」 :字节总部在北京,在上海、深圳、杭州、广州、成都等地都有办公室。去年 6 月,抖音电商落户杭州。 - 「业务方向」 :抖音电商、抖音餐饮、字节跳动广告业务、字节跳动本地生活 @@ -88,7 +88,7 @@ tag: ### 阿里系 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/hangzhou-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/hangzhou-4.png) - 「基本情况」 :阿里系占据了杭州互联网的一片天,相关联的企业是在是太多了。 - 「业务方向」 :达摩院、淘宝、菜鸟、钉钉、飞猪、盒马、支付宝、夸克、UC、书旗小说...... @@ -100,7 +100,7 @@ tag: ### 京东 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/hangzhou-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/hangzhou-5.png) - 「基本情况」 :京东在杭州也有招聘,不过,招聘的岗位比较少。 - 「业务方向」 :京东金融、京东云 @@ -111,7 +111,7 @@ tag: ### 网易 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/hangzhou-6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/hangzhou-6.png) - 「基本情况」 :网易在杭州有一个研究院,成立于 2006 年 6 月。 - 「业务方向」 :⽹易杭州研究院,简称“杭研”。杭研是⽹易内部的基础技术研发中⼼和前沿技术研究中⼼,在云计算、⼤数据、安全、⼈⼯智能等⽅⾯进⾏前沿技术研究、关键技术攻关和基础技术平台研发,服务⽹易系游戏、邮箱、⾳乐、电商、新闻、在线教育等产品,触达近 10 亿⽤户。 @@ -122,7 +122,7 @@ tag: ### 华为 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/hangzhou-7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/hangzhou-7.png) - 「基本情况」 :华为在杭州有一个研究所。 - 「业务方向」 :智能摄像机、云服务 @@ -134,7 +134,7 @@ tag: ### 中移杭研 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/hangzhou-8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/hangzhou-8.png) - 「基本情况」 :中国移动的一个全资子公司,2014 年在杭州成立。 - 「业务方向」 :统一认证、融合通信、魔固云 @@ -145,7 +145,7 @@ tag: ### 之江实验室 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/hangzhou-9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/hangzhou-9.png) - 「基本情况」 :之江实验室是浙江省委、省政府贯彻落实科技创新思想,深入实施创新驱动发展战略的重大科技创新平台。 - 「业务方向」 :智能感知、智能计算、智能网络、智能系统 @@ -156,7 +156,7 @@ tag: ### 同花顺 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/hangzhou-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/hangzhou-10.png) - 「基本情况」 :同花顺成立于 2001 年,总部位于杭州未来科技城,是国内第一家互联网金融信息服务业上市公司。同花顺作为一家互联网金融信息提供商,致力于为各类机构提供软件产品和系统维护服务、金融数据服务和智能推广服务,为个人投资者提供金融资讯和投资理财分析工具。 - 「业务方向」 :旗下有多款热门投资理财类 APP。 @@ -167,7 +167,7 @@ tag: ### 51 信用卡 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/hangzhou-11.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/hangzhou-11.png) - 「基本情况」 :51 信用卡(母公司为杭州恩牛网络技术有限公司),公司创立于 2012 年,是一家服务于中国亿万信用卡用户的互联网金融公司。 - 「业务方向」 :旗下有“51 信用卡管家”、“51 人品”、“51 人品贷”等 APP @@ -179,7 +179,7 @@ tag: ### 蘑菇街 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/hangzhou-12.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/hangzhou-12.png) - 「基本情况」 :2011 年,蘑菇街正式上线,2016 年 1 月与美丽说战略融合,公司旗下包括:蘑菇街、美丽说、uni 等产品与服务。 - 「业务方向」 :电商 @@ -191,7 +191,7 @@ tag: ### 有赞 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/hangzhou-13.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/hangzhou-13.png) - 「基本情况」 :有赞,原名口袋通,2012 年 11 月 27 日在杭州贝塔咖啡馆孵化成立,是一家主要从事零售科技 SaaS 服务的企业,帮助商家进行网上开店、社交营销、提高留存复购,拓展全渠道新零售业务。2014 年 11 月 27 日,口袋通正式更名为有赞。2018 年 4 月 18 日,有赞完成在港上市。2019 年 4 月,腾讯领投有赞 10 亿港元融资。 - 「业务方向」 :SaaS 服务(帮助商家网上开店、社交营销......)、PaaS 云服务。 @@ -203,7 +203,7 @@ tag: ### Zoom -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/hangzhou-14.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/hangzhou-14.png) - 「基本情况」 :Zoom 是一位美国华人企业家创办的公司,主营业务就是提供视频会议服务。总部位于硅谷,国内的话,杭州、苏州、合肥均有研发中心。 - 「业务方向」 :视频会议 @@ -215,7 +215,7 @@ tag: ### Cisco(思科) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/hangzhou-15.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/hangzhou-15.png) - 「基本情况」 :思科系统公司(Cisco Systems, Inc.),简称思科公司或思科,1984 年 12 月正式成立,是互联网解决方案的领先提供者,其设备和软件产品主要用于连接计算机网络系统,总部位于美国加利福尼亚州圣何塞。 - 「业务方向」 :路由器、交换机等网络基础设施 @@ -233,12 +233,12 @@ tag: 顺带再聊聊杭州的房价、教育吧。杭州的房价不忍直视啊!!!涨的实在是太厉害了! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/hangzhou-16.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/hangzhou-16.png) 杭州只有一所 985,也就是浙江大学。除了浙江大学之外,浙江省没有 211 院校。不过,杭州电子科技大学虽然不是 211,但是实力很强,外界也很认可。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/hangzhou-17.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/hangzhou-17.png) >作者:大白,转载链接:[https://mp.weixin.qq.com/s/hrL2tqXHT5AjOqrQlRhR-w](https://mp.weixin.qq.com/s/hrL2tqXHT5AjOqrQlRhR-w) diff --git a/docs/cityselect/nanjing.md b/docs/cityselect/nanjing.md index c8be73568..9624c92e4 100644 --- a/docs/cityselect/nanjing.md +++ b/docs/cityselect/nanjing.md @@ -13,7 +13,7 @@ tag: 有句话说的很对,“安徽不能没有南京,就像东北不能没有三亚”。不过调研中也发现,对于程序员来说,想要在南京留下也不是件很容易的事情,因为南京程序员的工作机会只能算一般,薪资水平这两年许多大公司选择在南京设立分部后才带起来一些,但是南京的房价已经很高了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-707d074e-def7-4cd3-af0a-84e46a0929a9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-707d074e-def7-4cd3-af0a-84e46a0929a9.png) 提一下哈,在这个系列里说的校招薪资都是常规招聘计划的薪资,说的常规招聘计划给出的 offer 包含普通 offer,sp (special offer) 和 ssp。不包含那些比如华为天才少年、美团北斗之类的。那种神仙 offer 怎么样我这种凡人还真不了解。这些招聘的薪资也都是针对本科生和研究生的。如果你是博士,薪资方面和公司都好商量。 @@ -23,7 +23,7 @@ tag: ### 阿里巴巴 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-f55e084c-f731-419b-af71-278ac8e7b15c.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-f55e084c-f731-419b-af71-278ac8e7b15c.png) 校招薪资:招的人不多,具体薪资情况不太清楚。据说和总部薪资水平差不多。阿里的薪资水平可以参考下[链接](https://mp.weixin.qq.com/s/3c-a7GpkTzAU2DFWSwN4lw)。 @@ -31,7 +31,7 @@ tag: ### 字节跳动 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-f945934a-3ab8-4272-a8b2-7001ea396377.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-f945934a-3ab8-4272-a8b2-7001ea396377.png) 校招薪资:字节在南京的薪资是字节在北京上海深圳薪资的 0.9。感觉薪资水平挺不错了。今年字节校招薪资也可以点这个[链接](https://mp.weixin.qq.com/s/3c-a7GpkTzAU2DFWSwN4lw)查看。 @@ -39,7 +39,7 @@ tag: ### 荣耀 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-8470d707-53c6-4c07-8160-50759ad9d50b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-8470d707-53c6-4c07-8160-50759ad9d50b.png) 校招薪资:月薪 17k - 27k(对应13-15级),每年 14-16 薪。 @@ -49,7 +49,7 @@ tag: ### 华为 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-f8ba1f60-c3bc-450b-b833-54db9bfdb807.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-f8ba1f60-c3bc-450b-b833-54db9bfdb807.png) 校招薪资:13级月薪 20-21k,14 级 23-25k,15级月薪 26-27k。每年 14-16 薪。16级我就不清楚了。 @@ -57,7 +57,7 @@ tag: ### 小米 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-bfb9cd80-f9b5-4ea7-b4f6-a15e54cf3c4b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-bfb9cd80-f9b5-4ea7-b4f6-a15e54cf3c4b.png) 校招薪资:小米的薪资是挺抠,不过放在南京来看也还行。看校招薪水上南京的薪资爆料今年有 11k,14k,16k,18k,19k,21k,23k 几个档。11k,14k基本都是本科生,硕士做开发岗的大部分是16k,18k,19k这几个档。20k 以上的基本都是做算法的。 @@ -65,7 +65,7 @@ tag: ### Vivo -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-7d676f6f-b081-4eb7-a2ce-3590a04b2fcd.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-7d676f6f-b081-4eb7-a2ce-3590a04b2fcd.png) 校招薪资:南京这边目前看到给应届生的价钱分为月薪17k,20k,24k三个档,,每年15薪。额外每个月有 1500 的租房补贴。住房公积金5%。 @@ -73,7 +73,7 @@ tag: ### OPPO -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-9ad6c7fe-c486-4628-8e07-f21b3e2cac6d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-9ad6c7fe-c486-4628-8e07-f21b3e2cac6d.png) 校招薪资:具体薪资不太清楚,大致应届生月薪在 20k 上下,每年 14 -15 个月薪资。 @@ -81,7 +81,7 @@ tag: ### 中兴 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-094809df-30b0-40c6-a1a5-f1e0a5329b6a.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-094809df-30b0-40c6-a1a5-f1e0a5329b6a.png) 校招薪资:中兴常规计划(指的是普通 offer)的月薪基本都在20k 以内,蓝剑计划年薪会在 40w 以上。 @@ -89,7 +89,7 @@ tag: ### 360 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-0bec3704-902d-4b8b-90b7-4ab5fc28a79d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-0bec3704-902d-4b8b-90b7-4ab5fc28a79d.png) 校招薪资:没调查清楚,年薪差不多 23w 左右。 @@ -97,7 +97,7 @@ tag: ### 深信服 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-5041109f-e9d8-4419-9f41-466f93708c53.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-5041109f-e9d8-4419-9f41-466f93708c53.png) 校招薪资:开发岗有3个档,月薪分布在15.6 - 22.8k 这个区间,算法岗也是三个档,月薪分布在19 - 25.2k 这个区间。每年13 - 15 薪 @@ -105,7 +105,7 @@ tag: ### 趋势科技 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-f60ae6ee-13d7-4efc-bd5a-a0334b76c6e3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-f60ae6ee-13d7-4efc-bd5a-a0334b76c6e3.png) 校招薪资:看到校招薪资爆料有三个档,分别是年薪 20w,23.5w 和 25w。 @@ -113,7 +113,7 @@ tag: ### 亚信安全 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-97e8edd1-2a4d-492c-9039-febbae881c05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-97e8edd1-2a4d-492c-9039-febbae881c05.png) 校招薪资:基本上月薪在15-18.5k 这个区间,每年 13- 15薪。 @@ -121,7 +121,7 @@ tag: ### SHEIN -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-fc7ddb5b-cbc1-482f-9087-ea9e3e136795.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-fc7ddb5b-cbc1-482f-9087-ea9e3e136795.png) 校招薪资:目前看校招薪资爆料月薪基本集中在16-23k,有个说自己 27k 的不知道真的假的(如果是真的,感觉 SHEIN 大部分老员工要RUN了)。每年14薪。 @@ -129,7 +129,7 @@ tag: ### 满帮集团 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-ec98654b-0bcf-4e96-bb52-fe7ebd2a6c55.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-ec98654b-0bcf-4e96-bb52-fe7ebd2a6c55.png) 校招薪资:校招薪水上的爆料开发月薪基本都在 20-30k 这个区间,算法基本都在 27-36k 这个区间。大部分人每年 14 薪。 @@ -139,25 +139,25 @@ tag: ### 中电系列 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-21fd091d-8afd-4cde-8fbd-a78b3c805085.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-21fd091d-8afd-4cde-8fbd-a78b3c805085.png) 南京有中电 28 所、中电 14 所、中电 841 所。有一些读者跟我说想去研究所,想法是去研究所可以工作生活平衡。但是现在有很多的研究所其实很忙的,工作生活平衡的研究所尤其不包括南京这几个。841 所还不是特别清楚。大部分互联网公司和中电 28 所以及 14 所 比起来还真是弟弟,不过根据南京的薪资的水平来说,这两个所得福利待遇算是顶级了。刚毕业的应届生进去工资大概每月到手是一万出头吧,有食堂有宿舍,出差会有出差补贴,差不多一天三四百,这几个所出差会非常的多。 ### 南瑞集团(国网电科院) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-aff2d53a-95b6-4654-baac-ebc2ba39fc25.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-aff2d53a-95b6-4654-baac-ebc2ba39fc25.png) 南瑞集团是国家电网直属的科研企业单位,主要做电力系统自动化相关设备,所以会招很多程序员。年薪 14 万左右。南瑞要给各个省公司做电力软件,所以出差的情况会非常多。面试上南瑞以后你还没有国家电网编制,想要编制还要去参加电网的考试。感觉性价比一般,不如去省公司,听说省公司工资还挺高的。 ### 运营商 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-6f4cd4bc-e1da-496c-bf00-7ef2e6089dee.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-6f4cd4bc-e1da-496c-bf00-7ef2e6089dee.png) 几个运营商里只有联通在南京有研究院专门做软件相关,除此之外想去运营商就只有省公司了。一些运营商的子公司我就不多介绍了,我是感觉国企的子公司性价比一般,既没有国企的稳定也没互联网的高薪。 ### 烽火 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-d16dd7fe-f7a0-4f70-a016-f88350a56fa7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-d16dd7fe-f7a0-4f70-a016-f88350a56fa7.png) 简单说下烽火,下面有烽火星空还有烽火通信。是国企,不过感觉一直处于一个乙方的位置。项目很多是涉密项目,但是我劝大家尽量少碰涉密类型的项目。如果手头没大公司可以去烽火锻炼下,如果有其它大公司的 offer,我感觉还是优先其它大公司了。 @@ -169,18 +169,18 @@ tag: 南京的房价还是比较高的,比较好的区域的房价直逼一线城市了,对于刚毕业的学生来说压力很大了,感觉这个房价应该逼走不少人。 另外南京物价也不低噢。下面这张图是网上找的,不一定准,可以当作参考。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-b5247968-f8fa-445a-b83e-7d88fbb056c7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-b5247968-f8fa-445a-b83e-7d88fbb056c7.png) ### 教育 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-61b6b641-72d1-45d2-be1e-463c7d1d7f9c.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-61b6b641-72d1-45d2-be1e-463c7d1d7f9c.png) 南京高校资源方面很强。南京大学、东南大学、南航、河海大学、南理工等等都是很不错的学校。另外南邮、南京工业大学这些双非学校的计算机也很强。感觉近几年各个大厂陆续在南京设分布也是看中了南京有大量的计算机相关专业的毕业生。 江苏高考题的难是出了名的,当然江苏省的中小学教育资源也很优质,所以在教育这方面选择南京还是不错的。 ### 交通&风景&气候&美食 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/nanjing-9da3efca-ceb7-4685-9932-6613ea2ea1c6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/nanjing-9da3efca-ceb7-4685-9932-6613ea2ea1c6.png) 南京拥堵程度是上过全国前十榜单的,我租房是喜欢宁愿住的小一点差一点也尽可能离上班地方近一点,我比较抗拒通勤。宏观上来说,南京的地理位置特别好,离苏州、杭州、上海都很近,另外离安徽也很近,这也是安徽人喜欢往南京跑的原因。 diff --git a/docs/cityselect/qingdao.md b/docs/cityselect/qingdao.md index 538cf625e..ee1c7a07e 100644 --- a/docs/cityselect/qingdao.md +++ b/docs/cityselect/qingdao.md @@ -13,7 +13,7 @@ tag: 当时我妹还在沙滩上留了一张“沉默王二在青岛”的印迹。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/qingdao-938dc8a2-9b47-4e12-aa45-79c2caf94f04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/qingdao-938dc8a2-9b47-4e12-aa45-79c2caf94f04.png) 按照我妹的说法,“青岛真是一座网红城市,美女是真多!” @@ -25,13 +25,13 @@ tag: ### 海尔 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/qingdao-e656449e-a34f-4915-a1cb-459208d23e34.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/qingdao-e656449e-a34f-4915-a1cb-459208d23e34.png) 海尔在青岛多年了,曾经有段辉煌期的。我舍友带着我在山东哈酒时,一个老大哥在酒桌上就跟我们聊起来,说他当初在海尔买家都是要请他们吃饭才卖给产品的。不过近些年海尔就不太行了,但依然在青岛是个工作上的好选择。海尔 Java 岗位给应届 985 硕士的报价是年薪 14 万。本科生相对少一点,年薪大概在 8万 到 12万 左右。我路过海尔时感觉海尔的园区还修的挺漂亮的。另外说一句,感觉海尔卡奥斯还行,在工业互联网领域做的还可以。 ### 海信 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/qingdao-9645cf8d-d9dc-4de7-a192-bdb9a447ec49.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/qingdao-9645cf8d-d9dc-4de7-a192-bdb9a447ec49.png) 我之前总觉得海信和海尔有某种联系,不过我查了下并没有,路过海信的园区时觉得海信也修的挺漂亮的。一个 211 硕士爆料的薪资是每月 10k,然后一年 13 个月工资。福利待遇方面感觉还可以,前两年有免费宿舍,交通补贴每月 200,住房补贴 800 每月,可以领三年。试用期 6 个月,试用期间 90% 薪资,免费班车,公积金 10%,社保全额。工作早9晚6,中午休息一小时。 @@ -39,43 +39,43 @@ tag: ### 光大银行青岛研发中心 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/qingdao-7351e8f8-90d5-4f3c-998a-703b36ec1fca.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/qingdao-7351e8f8-90d5-4f3c-998a-703b36ec1fca.png) 待遇听说还不错,以前是归分行管,现在归总行管。青岛这边主要开发云缴费业务。据说是有末尾淘汰机制。近几年好多员工都跑到青岛银行了。 ### 青岛银行 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/qingdao-cc5091a0-28ad-4439-8718-a6b79e80ef83.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/qingdao-cc5091a0-28ad-4439-8718-a6b79e80ef83.png) 青岛银行总行的技术岗。薪资不算高,有薪资爆料是年薪12w,不过算是摸鱼的天堂了,活大部分是外包干。 ### 青岛凯亚 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/qingdao-9fcaebb9-fcf0-4b34-8302-c0be931ae1ce.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/qingdao-9fcaebb9-fcf0-4b34-8302-c0be931ae1ce.png) 在青岛软件企业能算中上游了,不过全靠同行衬托。加上各种补贴,一年的工资差不多是是十二三万吧。试用期工资打五六折。技术还可以,不过有人爆料说他加班还挺多的。 ### 青岛鼎信 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/qingdao-f941c172-1ff2-40ce-bea0-8cb52a030bf3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/qingdao-f941c172-1ff2-40ce-bea0-8cb52a030bf3.png) 主要做通信的公司,一个应届硕士的爆料是税前13k,每年13个月的工资,有食堂,每个月600的饭补。 ### Yeelight -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/qingdao-7f88980b-d21e-4b27-9306-f911eb8a02e6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/qingdao-7f88980b-d21e-4b27-9306-f911eb8a02e6.png) Yeelight 算是在青岛为数不多的小而美的公司了,目前团队330人,研发人员超过一半,主要做智能照明,有小米的投资。听说团队氛围还不错,比较重视技术。我在网上没找到校招待遇的介绍,社招的话 BOSS 招聘 3-5年 Java 经验的报价是 12k-24k。 ### 中车四方 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/qingdao-f75fc5b3-2587-42d5-a80f-592a49c515eb.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/qingdao-f75fc5b3-2587-42d5-a80f-592a49c515eb.png) 中车四方是国有控股公司,薪资待遇在青岛算中上。也招程序员。应届毕业生是6个月试用期,转正以后每年工资税前差不多15w左右。不过他这个工资构成和别人不一样呀。每月税前一万左右的工资好像其中六千左右是绩效,然后每年年中会发一个业绩奖金,不同部门的奖金差别很大。 ### 中电41所 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/qingdao-53f614bb-d10d-4ae2-9f83-5911ef8ddead.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/qingdao-53f614bb-d10d-4ae2-9f83-5911ef8ddead.png) 说实话,我一直对中电系列的研究所印象不是很好,不过听说中电28所的薪资后对中电系列的印象有所改观(这里是闲扯一句哈,中电28所在南京,和青岛没啥关系)。注意41所得总部是在安徽,青岛只是一个分部,我了解到是加班会比较严重。 @@ -87,7 +87,7 @@ Yeelight 算是在青岛为数不多的小而美的公司了,目前团队330 青岛房均价两万多了吧。崂山和市南比较贵,其它地相对好一些。下面这张图有各区的房价,不是太准,不过可以参考下呀。感觉程序员在北京干几年,攒个首付,然后回青岛找个相对稳定的公司上班,买房压力还不是很大(除了买崂山和市南啊)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/qingdao-a228e29e-363f-44a3-b8cc-6cdd18d34b9f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/qingdao-a228e29e-363f-44a3-b8cc-6cdd18d34b9f.png) ### 教育 @@ -95,7 +95,7 @@ Yeelight 算是在青岛为数不多的小而美的公司了,目前团队330 大学方面,山东大学在青岛是有校区的,虽然离市区是真的远,但是人家有地铁呀。我记得坐那趟地铁去山东大学青岛校区找我舍友时穿越崂山,窗外的风景是真的漂亮。中国海洋大学也是一所985,校区是真的漂亮。中国石油大学是一所211,也挺不错的。另外像青岛大学、青岛理工大学、山东科技大学这些院校也是不错的。这几年各个高校也在青岛修建了研究院,感觉还是挺有发展前景的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/qingdao-b37788d7-c33d-475a-bc69-05927bf0825d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/qingdao-b37788d7-c33d-475a-bc69-05927bf0825d.png) 高中方面,青岛二中、青岛一中、青岛五十八中、青岛九中、即墨一中等都非常好。不过山东高考的压力是真的大,感觉我大学和读研时身边的山东同学,当年高考都是英雄般的人物。 @@ -103,11 +103,11 @@ Yeelight 算是在青岛为数不多的小而美的公司了,目前团队330 青岛是真的挺美的,感觉在青岛既能感觉到现代化大都市的感觉、又能体验到民国风情还能体验到欧式风情。另外我同学带我在青岛转时,经常走着走着就见到了大海,这让我这个多年身居内陆,没见过海的少年来说还是挺欣喜的。感觉青岛比西安冷很多,今年4月西安已经很热了,花都开了,但是去了青岛以后发现柳树才刚有一点点绿,还挺凉快的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/qingdao-d619e60b-732b-497f-a4a0-ebbad7ce1ce9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/qingdao-d619e60b-732b-497f-a4a0-ebbad7ce1ce9.png) 青岛的海鲜是挺丰富了,让我这个很少吃海鲜的少年一次吃了个够,还买了些海鲜给家里面寄了点。果然内陆的海鲜吃起来和沿海城市的海鲜差好多,另外山东菜是真的能倒酱油。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/qingdao-ce0b22f6-0b52-425c-a5d0-4eefe52706f6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/qingdao-ce0b22f6-0b52-425c-a5d0-4eefe52706f6.png) ### 交通 diff --git a/docs/cityselect/suzhou.md b/docs/cityselect/suzhou.md index a2c888ac6..7bb2b78ef 100644 --- a/docs/cityselect/suzhou.md +++ b/docs/cityselect/suzhou.md @@ -21,7 +21,7 @@ tag: 有句话叫做上有天堂,下有苏杭,不过有一说一,互联网环境方面苏州和杭州感觉还是有点差距。但是个人觉得还是不错的,而且从 18 年左右开始,苏州的互联网环境开始有了明显的改善。生活方面无论是苏州的地理位置,还是人文环境,都是十分适合居住生活的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/suzhou-e780c8c6-6e3f-4ba0-a91b-5cf572ee2d54.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/suzhou-e780c8c6-6e3f-4ba0-a91b-5cf572ee2d54.png) 老规矩,下面我们还是按照程序员的工作机会和生活环境来介绍苏州。 @@ -31,7 +31,7 @@ tag: ### 微软 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/suzhou-e18a11b0-2667-4e27-b93f-a05dad122e76.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/suzhou-e18a11b0-2667-4e27-b93f-a05dad122e76.png) 微软可以说是程序员在苏州最好的工作机会了。微软近些年也一直扩大在苏州的投资力度,目前微软苏州研究院的研发团队已经有 2000 人左右了,并且还在近一步扩建。2022 年微软将启动微软苏州三期新大楼的建设,建成后预计微软在苏州的研发团队会达到5000人。所以,目前想拿微软苏州的 offer 的难度,相比于拿北京上海等其它几个工作地 offer 的难度会低一些,大家抓紧呀。 @@ -43,55 +43,55 @@ tag: ### ZOOM -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/suzhou-ce9a78ef-6a18-4adf-8a4f-de7b7fa34b45.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/suzhou-ce9a78ef-6a18-4adf-8a4f-de7b7fa34b45.png) 在苏州,Zoom是除微软外的另一个好选择。Zoom 曾经被评为全球最佳雇主。根据 zoom 员工的说法,zoom的技术水平大概和国内二三线厂的水平差不多。薪资方面,比国内互联网也要低一些,看到一个校招薪资的爆料,一个做前端的硕士在苏州zoom的薪资是17k * 14。额外有 7 万美元的股票,分 4 年发完。虽然 base 薪资方面相对低一些,但是 zoom 工作是真的爽呀,没有 996,没有 kpi,办公环境好,无限零食和水果供应,这还要啥自行车。 ### 华为 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/suzhou-8bd7e5d1-1984-43f7-84e1-b02141160a37.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/suzhou-8bd7e5d1-1984-43f7-84e1-b02141160a37.png) 华为在苏州的研究院设立时间不长。华为的苏研院的加班在各地所有的研发中心中是排的上号的。华为各地研发中心的忙碌程度差不多是成都 》= 西安 》= 苏州 > 武汉 > 东莞 > 杭州 > 南京 > 深圳 > 北京 > 上海(整体感觉是这样,每个研发中心各部门的加班情况也不同)。不过华为有一个好处就是你挣的钱能和你的付出成正比,所以想快速挣钱的还是建议去。 ### 360 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/suzhou-85547b36-15da-443f-b7f2-878bfd06e997.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/suzhou-85547b36-15da-443f-b7f2-878bfd06e997.png) 360 来苏州时间不长,2018年才逐渐开始在苏州设立开发部门。目前 360 苏州包含了未来安全研究、360政企、安全等部门。目前 360苏州的薪资水平是北京上海薪资水平的 8 折,看到 21 年毕业的一个应届硕士爆料月薪是 16k,工资有点少,不过感觉在苏州也不错呀。 ### Momenta -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/suzhou-c4c83915-04b5-4660-9bad-32989a65116d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/suzhou-c4c83915-04b5-4660-9bad-32989a65116d.png) 一开始我对 Momenta 还真没什么了解,直到我有一天突然在校招薪水上搜了下 Momenta,然后我不由得直呼卧槽,这特么是给应届生的薪资?认真的? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/suzhou-ff400245-2fe0-437e-8dd3-aa9eed650e99.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/suzhou-ff400245-2fe0-437e-8dd3-aa9eed650e99.png) 然后我详细的了解了下这家公司,这是一家做自动驾驶的公司,创立时间也不长,2016年创立的。不过据说这家公司加班特别猛,有的组 10 10 5,有的组 996,有的组比 996 还要累。Momenta 现在能给的起这么高的工资,实力还是有的,不过对于发展的前景来说,领域这么垂直的公司风险性是比较大的。 ### 企查查 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/suzhou-d4bcbf1d-f758-4267-b55f-7553d7673511.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/suzhou-d4bcbf1d-f758-4267-b55f-7553d7673511.png) 跟上面的公司相比,企查查算是一个小而美的公司。企查查是苏州的本地企业,目前来说口碑还是很不错的。目前企查查的业务很赚钱,自己的大楼也差不多修好了。没有强制加班,全额社保和公积金。我没有查到企查查的校招薪资,看 boss 上的社招薪资水平不算太高,只能算还可以了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/suzhou-c7dbed8d-c471-4969-bfc4-badf8e0ac346.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/suzhou-c7dbed8d-c471-4969-bfc4-badf8e0ac346.png) ### 收钱吧 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/suzhou-31051fbc-a573-4328-904b-6915bfba2323.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/suzhou-31051fbc-a573-4328-904b-6915bfba2323.png) 这个公司大家应该都听说过,每次你用支付宝或者微信给商家付钱时都能听到“收钱吧到账xx元”。收钱吧的技术不错,加班也比较少,收钱吧的风评不错。不过就是工资相对给的少点,月薪 17k 左右(在苏州也还行了),每年14 个月的月薪。 ### 中国移动苏州研究院 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/suzhou-1bb72f74-dba6-420f-8c37-0a9e621b3561.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/suzhou-1bb72f74-dba6-420f-8c37-0a9e621b3561.png) 中国移动苏州研究院又叫苏小研,其实犹豫了很久要不要把他放在推荐里。苏小妍在知乎上的争议很大,有说待遇福利很好的,也有狂喷的。苏小研肯定有国企的通病,这个我确认,但具体怎么样就靠大家自己判断了,贴两张知乎上对苏小研的评价,好坏要大家自己去具体确认了。这两张图评价完全是两个极端呀。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/suzhou-b616269f-3717-4ea4-8c29-b4d5bcd448b4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/suzhou-b616269f-3717-4ea4-8c29-b4d5bcd448b4.png) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/suzhou-76122245-a8e1-44a8-a1f4-8ce7ab3c640d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/suzhou-76122245-a8e1-44a8-a1f4-8ce7ab3c640d.png) 上面是我认为苏州的比较不错的程序员就业机会,其它的一些苏州比较成规模的提供程序员就业机会的公司就列在下面,并且快速的简单介绍一下。 @@ -119,7 +119,7 @@ tag: ### 教育 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/suzhou-1b300d4a-1384-4dd2-a9a5-98221f1f6c46.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/suzhou-1b300d4a-1384-4dd2-a9a5-98221f1f6c46.png) 高等教育方面。其实就苏州本身而言,高校并不多,只有苏州大学一所 211(苏大的自然语言处理挺强的)。但是前些年许多地理位置偏北的高校为了打造自己在南方的影响力,提高生源质量,都会选择在南方办学。其中一部分选择了深圳,另一部分选择了苏州。西交大、中科大、中国人民大学、东南大学、西工大等院校都在苏州设有校区。所以虽然苏州本地院校不多,但是苏州计算机软件相关的高校毕业生并不少。不过这种现象随着西工大太仓校区刚修好,异地办学就被叫停了,我瓜实惨~ @@ -129,11 +129,11 @@ tag: 相比于苏州的工资水平,其实苏州的房价也不低了,看网站上的新房均价大约两万五一平。但是和旁边的城市一比,那苏州的房价就比较香了。在苏州咬咬牙还是能考虑买房的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/suzhou-60675498-4bfe-46d2-af70-da2f21add403.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/suzhou-60675498-4bfe-46d2-af70-da2f21add403.png) ### 娱乐&交通&美食 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/suzhou-c35c16ca-0bbf-489a-ba0b-9ecb6fef6905.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/suzhou-c35c16ca-0bbf-489a-ba0b-9ecb6fef6905.png) 娱乐方面苏州好玩的地方很多,传统的苏州园林风格和苏州现代风格都让人十分留恋。周末在苏州园林逛一逛真的很惬意。 diff --git a/docs/cityselect/xian.md b/docs/cityselect/xian.md index 5b0e6df7d..c071a326b 100644 --- a/docs/cityselect/xian.md +++ b/docs/cityselect/xian.md @@ -30,7 +30,7 @@ tag: **华为** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-4e82d879-bbe0-4bc5-a088-fadc84483d65.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-4e82d879-bbe0-4bc5-a088-fadc84483d65.png) 如果想在西安快速挣钱的话,华为几乎是最好的选择了,按照华为的工资水平在西安买房根本没有压力。华为在西安的建制很齐全,消费者、CloudBu、云核心等事业群以及`华为海思`,2012 实验室都有。但是因为去年美国对华为的打压,消费者、华为海思以及 2012 实验室这些之前很香的事业群目前日子都不太好过。 @@ -44,13 +44,13 @@ tag: **阿里巴巴** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-135b1f20-48ad-4295-bc50-40969ef023b0.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-135b1f20-48ad-4295-bc50-40969ef023b0.png) 阿里巴巴还没大范围的在西安招人,情况目前还不太清楚。我问过阿里巴巴的员工,只知道目前在西安设点的部门是阿里云,需要半年在杭州上班,半年在西安上班,工资水平和杭州一致。不过目前招的量比较小,基本都是招高p,主要是做售前的业务架构设计,只有很少的校招名额。 **京东** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-c9b0621d-f56c-42a0-a618-f261466f06a4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-c9b0621d-f56c-42a0-a618-f261466f06a4.png) 京东把京东物流的团队设在了西安航天城,目测团队规模在几百人,工资水平大约是京东在北京工资的 80%。大家可以参考一下,京东去年在北京的部门,校招普通 offer 是年包 28 万,sp 是年包 32.9 万。 @@ -58,7 +58,7 @@ tag: **腾讯云** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-67aa194e-52b0-4acb-b22b-1c1f4c09fac8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-67aa194e-52b0-4acb-b22b-1c1f4c09fac8.png) 腾讯云是腾讯的全资子公司,目前对这里褒贬不一。校招薪资水平本科和硕士生大概是月薪 13-16k,每年 16 薪,大家可以参考一下,腾讯 21 届校招的薪资是 17-21.5k ,每年 18 薪。社招我看一个本科毕业四年经验的老哥拿到的年包是 33 万。腾讯云的职级和腾讯不是对齐的,并且业务比较边缘,这也是网上被喷的一个主要原因。目前西安腾讯云创立时间不久,加班强度是比较大的。 @@ -68,7 +68,7 @@ tag: **广联达** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-9180be40-265e-42e5-9a85-4d61193b8798.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-9180be40-265e-42e5-9a85-4d61193b8798.png) 广联达西安的部门在针对 21 届毕业生的校招中开出的薪资是很有诚意的。给应届硕士的 sp 是 19k x 15,普通 offer 是 17k x 15,本科生每月基本都在 13-17k 之间。广联达工作制度基本是 965 或者 975,每个月还有一天的带薪病假。这还要啥自行车?不过广联达曾经也有过黑料,大家自行上网了解。 @@ -78,7 +78,7 @@ tag: **360** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-327db5f0-4061-48c3-b672-b1670f98325f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-327db5f0-4061-48c3-b672-b1670f98325f.png) 360 在西安只有一个几十人的团队。没听说过这里有校招,社招的话 3 年以上经验的差不多能每月给到 20k 。有说在这里待的舒服的,也有喷的。 @@ -88,7 +88,7 @@ tag: **科大讯飞** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-3592f841-8390-4399-ae8a-94c4368541ea.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-3592f841-8390-4399-ae8a-94c4368541ea.png) 科大讯飞西安丝路总部主要是算法岗,不过我看网上喷的比较多。据说是活多钱少。 @@ -102,7 +102,7 @@ tag: **绿盟** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-b70c6624-f4f1-453f-9ec8-5db22661bb18.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-b70c6624-f4f1-453f-9ec8-5db22661bb18.png) 绿盟算是中型企业里比较香的,绿盟在安全领域还是比较强的。工资比较低,硕士校招才能给到 14k x 14,但是这家公司几乎不加班,员工的离职率也一直很低,我同学有违约中兴三方去绿盟的。 @@ -118,7 +118,7 @@ tag: **寒武纪** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-fdf08fc3-d216-4382-913d-5f8b78b8e7df.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-fdf08fc3-d216-4382-913d-5f8b78b8e7df.png) 寒武纪是做智能芯片的公司,背后站着中科院计算所。2019 年落户的西咸新区,相关信息比较少。我知道的是加班比较多,不过薪资也高。 @@ -132,7 +132,7 @@ tag: 感觉西安几家国企的性价比略低,工资不高,且大部分加班严重。校招应届硕士工资税前年薪基本都在 15w-20w 之间,本科生的年薪比硕士少 3w 左右。国企相较于私企涨薪会慢很多,不过相对稳定一些。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-e12acc1d-4b6d-4f2e-a352-7155b527fa07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-e12acc1d-4b6d-4f2e-a352-7155b527fa07.png) **中兴** @@ -144,7 +144,7 @@ tag: **联通** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-33ac9a8b-04d3-4adb-8482-e725e25bb2f9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-33ac9a8b-04d3-4adb-8482-e725e25bb2f9.png) 西安联通软件研究院,应届硕士的总包和移动差不多,大约 16-19 万。不过联通比较清闲,大部分部门都能下午六点就下班。目前在网上的评价相对好一些。 @@ -162,7 +162,7 @@ tag: **荣耀** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-19d21a8f-6a45-408a-9f0f-ae8dd40bdbb1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-19d21a8f-6a45-408a-9f0f-ae8dd40bdbb1.png) 荣耀目前的招人需求是很大的,工作强度未知,薪资水平目前是完全对标华为的。 @@ -180,7 +180,7 @@ tag: **农行软开** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-cfa06bd9-abd6-455e-bd5c-aef4c90f7edb.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-cfa06bd9-abd6-455e-bd5c-aef4c90f7edb.png) 农行软开目前是在西安工作的最好的几个选择之一,硕士每月到手 12k,本科生少几百块钱,年终奖 2-4 个月。大部分部门都能晚上 7 点以前下班,并且周末双休。目前农行软开有子公司化软件开发中心的计划,听消息说可以选择去子公司,也可以留在软开。目前西安的农行软开也越来越卷,大部分的 offer 都给了西电、西交、西工大这三个学校了,另外这三个学校的学生现在也不是想去农行就能去了。农行软开有个硬性规定是必须通过六级才能报。 @@ -206,7 +206,7 @@ tag: **邮政银行** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-946ce62c-0afc-48c8-b570-fab660d2ea13.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-946ce62c-0afc-48c8-b570-fab660d2ea13.png) 邮储银行软件开发中心在西安刚成立,还不太确定。目前宣传是年包 28 万以上,工作强度目测比较大。 @@ -220,7 +220,7 @@ tag: **三星** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-31302c7c-7dcd-4f12-b624-872b4878670d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-31302c7c-7dcd-4f12-b624-872b4878670d.png) 大部分应届生都是(11-14)k \*13.5k。965 工作制。几乎都是芯片、运维相关岗位。 @@ -236,7 +236,7 @@ SAP 是一家做企业软件的德企,技术十分强大。硕士年薪 20 万 **Thougtworks** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-167d941b-740e-4342-9d28-1ba753766398.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-167d941b-740e-4342-9d28-1ba753766398.png) 在 Thoughtworks 工作是很舒服的,开放式办公、扁平化管理、技术氛围浓厚。工资本硕都是 13k x 14。 @@ -252,7 +252,7 @@ ThougtWorks 的新人培养机制还是很赞的!对于应届生入职 ThougtW ### 研究所 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-872dc3ff-316d-4194-b549-f998b39feedf.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-872dc3ff-316d-4194-b549-f998b39feedf.png) 西安航空航天类的研究所特别多,我知道的招计算机方面的研究所有航天 504 所、771 所,航空 631 所、618 所、603 所,中电 20 所、39 所,兵器工业 203 所、204 所、205 所。 @@ -264,7 +264,7 @@ ThougtWorks 的新人培养机制还是很赞的!对于应届生入职 ThougtW ## 生活 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-980c455e-1858-4c52-8c4f-5c1b0ed996b4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-980c455e-1858-4c52-8c4f-5c1b0ed996b4.png) 陕西的资源基本都集中在西安,从人口上也能看出西安的资源有多集中。整个陕西三千多万人,在西安就有一千多万。并且这些年中央对西安的扶植力度越来越大。 @@ -272,25 +272,25 @@ ThougtWorks 的新人培养机制还是很赞的!对于应届生入职 ThougtW 西安的房价从 18 年到现在翻了一倍,但就目前的房价相较于其它同类型城市算是比较友好的。现在西安的房价最贵的在曲江、第二贵是高新区。其它地方的房价差不多一万六左右吧,不过今年的全运会过后可能会长一波。按照程序员的工资来说,在西安买房的问题不算很大,这也是程序员待在西安比较舒服的地方。对于程序员来说,租房的压力相对较小,我同学有在农行软开工作的,在附近租了一个一居室的开间四十平左右,一个月一千五,上班步行用不了十分钟。高新那边租房贵一些,你愿意合租的话压力也不大。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-b905fada-e4a5-4a0a-a704-cbf9f310be6c.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-b905fada-e4a5-4a0a-a704-cbf9f310be6c.png) 西安住建前段时间出了二手房交易参考价格,我贴在下面大家可以参考下,不过这个价格感觉低于市场价了。我感觉这直接打了个八折~西安住建发布二手房交易参考价格的链接在这里 https://mp.weixin.qq.com/s/Gis7kIJklWygTseztydDaw -![image-20210720212848036](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-c22be12f-8426-487d-b256-96d7e8950762.png) +![image-20210720212848036](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-c22be12f-8426-487d-b256-96d7e8950762.png) -![image-20210720212821035](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-fb06e9b2-ba0b-46d3-b84a-5449a77dc6ae.png) +![image-20210720212821035](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-fb06e9b2-ba0b-46d3-b84a-5449a77dc6ae.png) ### 教育资源 西安的教育资源很好。高中教育资源方面,西安的名校众多。西工大附中、西安铁一中、高新一中、交大附中、陕师大附中这些学校在全国都是很有名的,另外还有一批在陕西省很有名的高中也很不错。大学教育资源方面,整个陕西有三所 985,西工大和西安交大都在西安,西北农林科技大学就在离西安不远的杨凌。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-d905fc96-b354-45fa-b6aa-cfb4e2171eb7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-d905fc96-b354-45fa-b6aa-cfb4e2171eb7.png) 另外,还有像西安电子科技大学、西北大学、陕西师范大学、长安大学、第四军医大学这些不错的 211,还有像西安邮电、西安理工、西安科技大学、西安工业大学等等这些不错的双非院校。西安每年产出的人才的数量是很庞大的。这是很值得西安自豪的一点,但是这也造成了一个问题,西安就业十分的内卷。有一个现象是陕西人都愿意在西安,不愿意出来,计算去外面上学的陕西人毕业也大部分都回到了西安,另外在西安上过学的也大部分留在了西安。西安的几个效益比较好的研究所、银行软开的应聘难度比在北京的同级别单位都难很多。内卷不仅表现在计算机,计算机算好的,我了解到目前好多西安的小学老师都敢只要 211 以上毕业的硕士生。 ### 医疗资源 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-e6f524ec-e353-48c2-b89a-cd2ca6373924.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-e6f524ec-e353-48c2-b89a-cd2ca6373924.png) 医疗资源方面西安也很给力,交大一附院、交大二附院、唐都医院、西京医院、红会医院都是放在全国都很强的,另外其他一批省内比较有名的医院也很不错。 @@ -298,7 +298,7 @@ ThougtWorks 的新人培养机制还是很赞的!对于应届生入职 ThougtW 西安的交通方面不敢恭维,堵车那是一绝,我的感觉是西安比北京都要堵。西安的地铁 21 年初新开了 3 条线路,目前共有 8 条线路才勉强够的上需求。每逢法定节假日,旅游的人都会把西安挤炸。说到旅游,近年来西安对游客的吸引力是越来越大,一方面西安在弘扬大唐文化,另一方面西安的美食也叫一个美滴很。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/xian-e3de7963-bfcf-4b37-9306-ff9939202208.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/xian-e3de7963-bfcf-4b37-9306-ff9939202208.png) 另外,大唐不夜城这里的人是真的多,尤其是夏天,晚上的时候基本打不到车! diff --git a/docs/cityselect/zhengzhou.md b/docs/cityselect/zhengzhou.md index 48f368989..69f09809f 100644 --- a/docs/cityselect/zhengzhou.md +++ b/docs/cityselect/zhengzhou.md @@ -20,25 +20,25 @@ tag: 教育资源极度匮乏导致好的企业不来,好的企业不来又导致人才外流,恶性循环。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-0cd5c4ac-4e67-4b06-b5a3-20fad836824b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-0cd5c4ac-4e67-4b06-b5a3-20fad836824b.png) ### 数字郑州 -![img](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-b88e7ee0-5ab7-40af-8d1d-16cd3eae7b97.png) +![img](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-b88e7ee0-5ab7-40af-8d1d-16cd3eae7b97.png) 这个是阿里和郑州的政府合作的,目前评价大家对数字郑州的评价很不错呀,薪资也挺给力的,大家可以看下 Boss 上数字郑州的招聘岗位以及薪资报价呀。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-b1ef6dc9-0859-47fd-9058-a504bf01cd5a.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-b1ef6dc9-0859-47fd-9058-a504bf01cd5a.png) ### 中原银行 -![img](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-db7b6919-2158-4846-914f-f30e028c0234.png) +![img](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-db7b6919-2158-4846-914f-f30e028c0234.png) 中原银行的工资比较高,在郑州生活的话去中原银行是很不错的选择,不过想进中原银行的话,不是校招想进去有点难。薪资水平可以看下 Boss 上的招聘薪资水平。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-ce9e2a6e-8743-49b3-83aa-8719da78b7e1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-ce9e2a6e-8743-49b3-83aa-8719da78b7e1.png) @@ -46,19 +46,19 @@ tag: 浪潮在郑州的研发中心法定节假日加班是有加班费的,但平时加班就没有加班费了,每月要求加够50个小时的班。薪资水平大家也可以参考下 Boss 上放出的招聘薪资水平。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-ef0fe9a0-eff2-4037-96d2-ab84a2f76491.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-ef0fe9a0-eff2-4037-96d2-ab84a2f76491.png) ### 新华三 新华三大部分情况下能双休,周末加班也有加班费,不过涨薪很缓慢。在网上看到一个帖子,有人问 offer 选西安中兴还算郑州新华三,中兴和华三的职工都在互相劝退,说这是一个送命题。薪资水平大家还是参考下Boss上的招聘薪资水平吧。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-caa46657-f65d-4965-b553-c9f4502f3cc9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-caa46657-f65d-4965-b553-c9f4502f3cc9.png) -![点击并拖拽以移动](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-62496f27-fe20-45c6-b116-90a38e607a09) +![点击并拖拽以移动](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-62496f27-fe20-45c6-b116-90a38e607a09) ### UU 跑腿 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-624dd61d-2b19-4cb6-b4b6-e1b813fd6baf.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-624dd61d-2b19-4cb6-b4b6-e1b813fd6baf.png) UU 跑腿主要提供同城送件服务,是郑州本土最大的互联网公司,隶属于郑州时空隧道信息技术有限公司,地址位于郑州市金水区。 @@ -68,11 +68,11 @@ UU 跑腿的工作环境以及各种福利都还算不错! 薪资的话,看准网上的平均薪资是 10k 附近,其中后端开发的薪资在 14k 附近,前端开发的薪资在 10k 附近,软件测试的薪资在 10k 附近(薪资水平仅供参考,实际情况因人和岗位或许会有一些出入)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-b3a5d176-a10b-4637-a349-06197ee984a8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-b3a5d176-a10b-4637-a349-06197ee984a8.png) ### 中原消费金融 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-3317ba70-8310-465c-b36d-60f8e215960c.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-3317ba70-8310-465c-b36d-60f8e215960c.png) 河南中原消费金融股份有限公司是一家全国性非银行金融机构,地址位于郑州市郑东新区。 @@ -84,27 +84,27 @@ UU 跑腿的工作环境以及各种福利都还算不错! 注意:大家注意一个情况,中原消费的软件研发岗位大部分都搬迁到上海了,目前在郑州的大部分是行政岗位,只有少部分研发岗位。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-6de95df2-4c10-44af-9ddb-935cb358def6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-6de95df2-4c10-44af-9ddb-935cb358def6.png) ### 刀锋互娱 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-aa02294b-e809-49b6-9e96-4ec44db676c3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-aa02294b-e809-49b6-9e96-4ec44db676c3.png) 刀锋互娱是一家专注游戏服务市场的互联网公司,2019 年完成 A+轮融资,平台注册用户量突破千万。 旗下比较出名的产品有租号玩、一派陪玩,都是和游戏领域相关的产品。相信比较喜欢玩游戏的小伙伴应该对这个两个产品有了解。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-ec100440-a50e-4f27-88e4-4b075cf12676.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-ec100440-a50e-4f27-88e4-4b075cf12676.png) 整体面试不是很难,薪资相对来说也还可以。 薪资的话,看准网上的平均薪资是 16k 附近,其中后端开发(C++)的薪资在 20k 附近,前端开发的薪资在 8.5k 附近,软件测试的薪资在 9.5k 附近(薪资水平仅供参考,实际情况因人和岗位或许会有一些出入)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-1b7640e6-deee-42f6-8df7-639555c5d236.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-1b7640e6-deee-42f6-8df7-639555c5d236.png) ### 新开普 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-ca1cf626-13aa-4081-b6c0-285893458dde.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-ca1cf626-13aa-4081-b6c0-285893458dde.png) 新开普也是郑州的一家本土互联网公司,成立于郑州高新技术产业开发区,主要做 NFC 近场移动支付、金融 IC 卡等业务。 @@ -114,7 +114,7 @@ UU 跑腿的工作环境以及各种福利都还算不错! 薪资的话,看准网上的平均薪资是 7.6k 附近,其中后端开发(Java)的薪资在 9k 附近,前端开发的薪资在 9k 附近,软件测试的薪资在 5.5k 附近(薪资水平仅供参考,实际情况因人和岗位或许会有一些出入)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-dca4c23e-7310-4a60-b338-25f3e92d3339.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-dca4c23e-7310-4a60-b338-25f3e92d3339.png) ### 中移在线 @@ -136,11 +136,11 @@ UU 跑腿的工作环境以及各种福利都还算不错! 薪资的话,看准网上的平均薪资是 12k 附近,其中后端开发(Java)的薪资在 14k 附近,前端开发的薪资在 8k 附近,软件测试的薪资在 10k 附近(薪资水平仅供参考,实际情况因人和岗位或许会有一些出入)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-90273777-d5d0-424a-a8b0-e5df53455ef9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-90273777-d5d0-424a-a8b0-e5df53455ef9.png) ### 妙优车 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-b6ca12ab-ecb5-4b01-9cf3-21b22da0d67a.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-b6ca12ab-ecb5-4b01-9cf3-21b22da0d67a.png) 妙优车主要做的是汽车方面的业务,涵盖整车销售、汽车金融、汽车保险、汽车用品、汽车美容等方面。 @@ -150,7 +150,7 @@ UU 跑腿的工作环境以及各种福利都还算不错! 薪资这块的一般偏上,看准网上的平均薪资是 11k 附近,其中后端开发(Java)的薪资在 12k 附近,前端开发的薪资在 10k 附近(薪资水平仅供参考,实际情况因人和岗位或许会有一些出入)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-491c2bc1-5cea-48e6-ad47-80eb92ddaa3d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-491c2bc1-5cea-48e6-ad47-80eb92ddaa3d.png) ### 腾河 @@ -180,13 +180,13 @@ UU 跑腿的工作环境以及各种福利都还算不错! 科大讯飞在郑州金水区有一个小分部,大部分招聘的都是和技术无关的岗位,不过也有一个 Java 开发岗。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-dc1517e7-c93b-457f-96d2-0871c17bf916.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-dc1517e7-c93b-457f-96d2-0871c17bf916.png) ### 字节跳动 郑州也有字节跳动分部,不过都是和市场拓展与运营相关的岗位,像技术开发岗是没有的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-c7d11e68-2b85-4895-a3c5-44f6816ccfb2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-c7d11e68-2b85-4895-a3c5-44f6816ccfb2.png) 类似的还有美团、华为、阿里巴巴等大厂,这些公司在郑州招聘的基本也都是非技术岗位。 @@ -206,7 +206,7 @@ UU 跑腿的工作环境以及各种福利都还算不错! 以下房价数据来源于安居客,可以作为参考。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-95ec5784-5064-488c-bce8-4c076115a021.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-95ec5784-5064-488c-bce8-4c076115a021.png) ### 教育 @@ -214,7 +214,7 @@ UU 跑腿的工作环境以及各种福利都还算不错! 不过,211 院校仅有一所——郑州大学。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-03294872-3914-441b-ae6b-55200bce097a.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-03294872-3914-441b-ae6b-55200bce097a.png) 是的!作为偌大的河南省的省会,国家历史文化名城,也就只有一所 211! @@ -228,7 +228,7 @@ UU 跑腿的工作环境以及各种福利都还算不错! 另外,郑州市的优质医疗资源,在金水区、中原区、管城区比较集中; -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-6bbd8d11-dbe5-43c4-9354-b422b7fe1f77.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-6bbd8d11-dbe5-43c4-9354-b422b7fe1f77.png) ### 本地居民 @@ -246,15 +246,15 @@ UU 跑腿的工作环境以及各种福利都还算不错! 下图中的部分高铁线路正在修建,比如郑万高铁全线大概是 2021 年中旬通车。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-0a4d6b3c-a756-4509-a6fd-f3a65f018c84.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-0a4d6b3c-a756-4509-a6fd-f3a65f018c84.png) 郑州的地铁规划情况如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-264ab1f2-f355-466a-ab5d-2dc21668e888.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-264ab1f2-f355-466a-ab5d-2dc21668e888.gif) 目前的话,郑州地铁有 1 号线、2 号线、3 号线、4 号线、城郊线、5 号线、14 号线一期 7 条地铁线。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-c5e61f03-efcb-4e56-b9bf-ff7bcec3dce0) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-c5e61f03-efcb-4e56-b9bf-ff7bcec3dce0) ### 美食 @@ -262,7 +262,7 @@ UU 跑腿的工作环境以及各种福利都还算不错! 郑州好吃的还挺多的!去了郑州之后,一定要去喝胡辣汤,真的不要太好喝! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cityselect/zhengzhou-757eed5b-4b81-4bb0-a0e1-b24e510cc707.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cityselect/zhengzhou-757eed5b-4b81-4bb0-a0e1-b24e510cc707.png) 本地的美食还有烩面、焖饼、烧鸡等等都非常不错。 diff --git a/docs/collection/arraylist.md b/docs/collection/arraylist.md index b5e22f7d9..af6026f21 100644 --- a/docs/collection/arraylist.md +++ b/docs/collection/arraylist.md @@ -199,7 +199,7 @@ public void add(int index, E element) { “三妹,注意看,我画幅图来表示下。”我认真地做起了图。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/arraylist-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/arraylist-01.png) “二哥,那怎么**更新 ArrayList 中的元素**呢?”三妹继续问。 @@ -296,7 +296,7 @@ private void fastRemove(int index) { “三妹,注意看,我画幅图来表示下。”我认真地做起了图。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/arraylist-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/arraylist-02.png) diff --git a/docs/collection/fail-fast.md b/docs/collection/fail-fast.md index 665043b2e..be5f7d906 100644 --- a/docs/collection/fail-fast.md +++ b/docs/collection/fail-fast.md @@ -51,7 +51,7 @@ System.out.println(list); 这段代码看起来没有任何问题,但运行起来就报错了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/fail-fast-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/fail-fast-01.png) 根据错误的堆栈信息,我们可以定位到 ArrayList 的第 901 行代码。 @@ -152,7 +152,7 @@ modCount 的值变成了 4。 那其实在阿里巴巴的 Java 开发手册里也提到了,不要在 for-each 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator 方式。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/fail-fast-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/fail-fast-02.png) 那原因其实就是我们上面分析的这些,出于 fail-fast 保护机制。 diff --git a/docs/collection/gailan.md b/docs/collection/gailan.md index 9b3b34924..d9f87f881 100644 --- a/docs/collection/gailan.md +++ b/docs/collection/gailan.md @@ -14,7 +14,7 @@ tag: “好吧。”我只好摊摊手地说,“那我先画张集合框架的结构图等着你。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/gailan-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/gailan-01.png) “完了没?三妹。” @@ -124,7 +124,7 @@ HashSet(int initialCapacity, float loadFactor, boolean dummy) { 这是一个包含了 4 个元素的双端队列,和一个包含了 5 个元素的双端队列。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/gailan-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/gailan-02.png) head 指向队首的第一个有效的元素,tail 指向队尾第一个可以插入元素的空位,因为是循环数组,所以 head 不一定从是从 0 开始,tail 也不一定总是比 head 大。 diff --git a/docs/collection/hashmap-interview.md b/docs/collection/hashmap-interview.md index 8c949991f..1858450a8 100644 --- a/docs/collection/hashmap-interview.md +++ b/docs/collection/hashmap-interview.md @@ -48,7 +48,7 @@ static final int MIN_TREEIFY_CAPACITY = 64; JDK 8 中 HashMap 的结构示意图: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-interview-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-interview-01.png) ### 02、为什么链表改为红黑树的阈值是 8? @@ -130,7 +130,7 @@ HashMap中采用的是链地址法 。 2 的 N 次幂有助于减少碰撞的几率。如果 length 为2的幂次方,则 length-1 转化为二进制必定是11111……的形式,在与h的二进制与操作效率会非常的快,而且空间不浪费。我们来举个例子,看下图: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-interview-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-interview-02.png) 当 length =15时,6 和 7 的结果一样,这样表示他们在 table 存储的位置是相同的,也就是产生了碰撞,6、7就会在一个位置形成链表,4和5的结果也是一样,这样就会导致查询速度降低。 @@ -170,7 +170,7 @@ static final int tableSizeFor(int cap) { 6、如果冲突后是链表,判断该链表是否大于 8 ,如果大于 8 并且数组容量小于 64,就进行扩容;如果链表节点大于 8 并且数组的容量大于 64,则将这个结构转换为红黑树;否则,链表插入键值对,若 key 存在,就覆盖掉 value。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-interview-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-interview-03.png) ### 11、HashMap 的扩容方式? diff --git a/docs/collection/hashmap.md b/docs/collection/hashmap.md index 0254a8141..10d754b3b 100644 --- a/docs/collection/hashmap.md +++ b/docs/collection/hashmap.md @@ -117,7 +117,7 @@ static final int hash(Object key) { 看下面这个图。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hash-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hash-01.png) 某哈希值为 `11111111 11111111 11110000 1110 1010`,将它右移 16 位(h >>> 16),刚好是 `00000000 00000000 11111111 11111111`,再进行异或操作(h ^ (h >>> 16)),结果是 `11111111 11111111 00001111 00010101` @@ -231,7 +231,7 @@ void transfer(Entry[] newTable, boolean rehash) { 取模运算后,哈希冲突都到 table[1] 上了,因为余数为 1。那么扩容前的样子如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-resize-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-resize-01.png) 小数组的容量为 2, key 3、7、5 都在 table[1] 的链表上。 @@ -243,13 +243,13 @@ void transfer(Entry[] newTable, boolean rehash) { - key 7 取模(7%4)后是 3,放在 table[3] 上的链表头部。 - key 5 取模(5%4)后是 1,放在 table[1] 上。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-resize-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-resize-02.png) 按照我们的预期,扩容后的 7 仍然应该在 3 这条链表的后面,但实际上呢? 7 跑到 3 这条链表的头部了。针对 JDK 7 中的这个情况,JDK 8 做了哪些优化呢? 看下面这张图。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-resize-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-resize-03.png) n 为 table 的长度,默认值为 16。 @@ -273,12 +273,12 @@ n 为 table 的长度,默认值为 16。 - 扩容后的容量是 32 - 扩容后的索引是 21(*1* 0101),也就是 5+16,也就是原来的索引+原来的容量 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-resize-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-resize-04.png) 也就是说,JDK 8 不需要像 JDK 7 那样重新计算 hash,只需要看原来的hash值新增的那个bit是1还是0就好了,是0的话就表示索引没变,是1的话,索引就变成了“原索引+原来的容量”。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-resize-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-resize-05.png) JDK 8 的这个设计非常巧妙,既省去了重新计算hash的时间,同时,由于新增的1 bit是0还是1是随机的,因此扩容的过程,可以均匀地把之前的节点分散到新的位置上。 @@ -442,7 +442,7 @@ static final float DEFAULT_LOAD_FACTOR = 0.75f; 具体是用这么一个公式来表示的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-loadfactor-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-loadfactor-01.png) 等号的左边,P 表示概率,N表示某种函数关系,t 表示时间,n 表示数量。 @@ -502,35 +502,35 @@ more: less than 1 in ten million 于是,n次事件里面,碰撞为0的概率,由上面公式得: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-loadfactor-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-loadfactor-02.png) 这个概率值需要大于0.5,我们认为这样的hashmap可以提供很低的碰撞率。所以: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-loadfactor-03png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-loadfactor-03png) 这时候,我们对于该公式其实最想求的时候长度s的时候,n为多少次就应该进行扩容了?而负载因子则是$n/s$的值。所以推导如下: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-loadfactor-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-loadfactor-04.png) 所以可以得到 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-loadfactor-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-loadfactor-05.png) 其中 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-loadfactor-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-loadfactor-06.png) 这就是一个求 `∞⋅0`函数极限问题,这里我们先令$s = m+1(m \to \infty)$则转化为 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-loadfactor-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-loadfactor-07.png) 我们再令 $x = \frac{1}{m} (x \to 0)$ 则有, -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-loadfactor-08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-loadfactor-08.png) 所以, -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-loadfactor-09.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-loadfactor-09.png) 考虑到 HashMap的容量有一个要求:它必须是2的n 次幂(这个[之前的文章](https://mp.weixin.qq.com/s/aS2dg4Dj1Efwujmv-6YTBg)讲过了,点击链接回去可以再温故一下)。当加载因子选择了0.75就可以保证它与容量的乘积为整数。 @@ -616,33 +616,33 @@ void transfer(Entry[] newTable, boolean rehash) { 扩容前的样子假如是下面这样子。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-thread-nosafe-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-thread-nosafe-01.png) 那么正常扩容后就是下面这样子。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-thread-nosafe-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-thread-nosafe-02.png) 假设现在有两个线程同时进行扩容,线程 A 在执行到 `newTable[i] = e;` 被挂起,此时线程 A 中:e=3、next=7、e.next=null -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-thread-nosafe-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-thread-nosafe-03.png) 线程 B 开始执行,并且完成了数据转移。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-thread-nosafe-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-thread-nosafe-04.png) 此时,7 的 next 为 3,3 的 next 为 null。 随后线程A获得CPU时间片继续执行 `newTable[i] = e`,将3放入新数组对应的位置,执行完此轮循环后线程A的情况如下: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-thread-nosafe-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-thread-nosafe-05.png) 执行下一轮循环,此时 e=7,原本线程 A 中 7 的 next 为 5,但由于 table 是线程 A 和线程 B 共享的,而线程 B 顺利执行完后,7 的 next 变成了 3,那么此时线程 A 中,7 的 next 也为 3 了。 采用头部插入的方式,变成了下面这样子: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-thread-nosafe-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-thread-nosafe-06.png) 好像也没什么问题,此时 next = 3,e = 3。 @@ -650,7 +650,7 @@ void transfer(Entry[] newTable, boolean rehash) { 接下来当执行完 `e.next=newTable[i]` 即 3.next=7 后,3 和 7 之间就相互链接了,执行完 `newTable[i]=e` 后,3 被头插法重新插入到链表中,执行结果如下图所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-thread-nosafe-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-thread-nosafe-07.png) 套娃开始,元素 5 也就成了弃婴,惨~~~ @@ -660,7 +660,7 @@ void transfer(Entry[] newTable, boolean rehash) { 正常情况下,当发生哈希冲突时,HashMap 是这样的: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-thread-nosafe-08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-thread-nosafe-08.png) 但多线程同时执行 put 操作时,如果计算出来的索引位置是相同的,那会造成前一个 key 被后一个 key 覆盖,从而导致元素的丢失。 @@ -738,11 +738,11 @@ if ((p = tab[i = (n - 1) & hash]) == null) 两个线程都执行了 if 语句,假设线程 A 先执行了 ` tab[i] = newNode(hash, key, value, null)`,那 table 是这样的: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-thread-nosafe-09.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-thread-nosafe-09.png) 接着,线程 B 执行了 ` tab[i] = newNode(hash, key, value, null)`,那 table 是这样的: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/hashmap-thread-nosafe-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/hashmap-thread-nosafe-10.png) 3 被干掉了。 diff --git a/docs/collection/iterator-iterable.md b/docs/collection/iterator-iterable.md index 398fe9c94..9d0fc78dc 100644 --- a/docs/collection/iterator-iterable.md +++ b/docs/collection/iterator-iterable.md @@ -104,7 +104,7 @@ list.forEach(new Consumer() { 如果我们仔细观察ArrayList 或者 LinkedList 的“户口本”就会发现,并没有直接找到 Iterator 的影子。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/iterator-iterable-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/iterator-iterable-01.png) 反而找到了 Iterable! diff --git a/docs/collection/linkedlist.md b/docs/collection/linkedlist.md index 87ad5b7c7..3b85bb47a 100644 --- a/docs/collection/linkedlist.md +++ b/docs/collection/linkedlist.md @@ -64,7 +64,7 @@ private static class Node { 我画幅图给你们展示下吧。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/linkedlist-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/linkedlist-01.png) - 对于第一个节点来说,prev 为 null; - 对于最后一个节点来说,next 为 null; @@ -123,7 +123,7 @@ void linkLast(E e) { 此时还不能称之为链表,因为前后节点都是断裂的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/linkedlist-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/linkedlist-02.png) - 添加第二个元素的时候,first 和 last 都指向的是第一个节点。 - 然后新建一个节点 newNode,它的 prev 指向的是第一个节点,next 为 null。 @@ -131,7 +131,7 @@ void linkLast(E e) { 此时的链表还不完整。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/linkedlist-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/linkedlist-03.png) - 添加第三个元素的时候,first 指向的是第一个节点,last 指向的是最后一个节点。 - 然后新建一个节点 newNode,它的 prev 指向的是第二个节点,next 为 null。 @@ -139,7 +139,7 @@ void linkLast(E e) { 此时的链表已经完整了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/linkedlist-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/linkedlist-04.png) 我这个增的招式,还可以演化成另外两个: diff --git a/docs/collection/list-war-2.md b/docs/collection/list-war-2.md index 25715b2f5..04936c2bd 100644 --- a/docs/collection/list-war-2.md +++ b/docs/collection/list-war-2.md @@ -12,7 +12,7 @@ tag: ArrayList 实现了 List 接口,继承了 AbstractList 抽象类。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/list-war-2-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/list-war-2-01.png) 底层是基于数组实现的,并且实现了动态扩容 @@ -142,7 +142,7 @@ private void writeObject(java.io.ObjectOutputStream s) ### 02、LinkedList 是如何实现的? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/list-war-2-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/list-war-2-02.png) LinkedList 是一个继承自 AbstractSequentialList 的双向链表,因此它也可以被当作堆栈、队列或双端队列进行操作。 @@ -349,7 +349,7 @@ void linkBefore(E e, LinkedList.Node succ) { 找到指定位置上的元素(succ)之后,就开始执行 `linkBefore()` 方法了,先将 succ 的前一个节点(prev)存放到临时变量 pred 中,然后生成新的 Node 节点(newNode),并将 succ 的前一个节点变更为 newNode,如果 pred 为 null,说明插入的是队头,所以 first 为新节点;否则将 pred 的后一个节点变更为 newNode。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/list-war-2-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/list-war-2-03.png) 经过源码分析以后,小伙伴们是不是在想:“好像 ArrayList 在新增元素的时候效率并不一定比 LinkedList 低啊!” @@ -792,7 +792,7 @@ for (Iterator it = list.iterator(); it.hasNext();) { 迭代器只会调用一次 `node(int)` 方法,在执行 `list.iterator()` 的时候:先调用 AbstractSequentialList 类的 `iterator()` 方法,再调用 AbstractList 类的 `listIterator()` 方法,再调用 LinkedList 类的 `listIterator(int)` 方法,如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/collection/list-war-2-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/collection/list-war-2-04.png) 最后返回的是 LinkedList 类的内部私有类 ListItr 对象: diff --git a/docs/common-tool/collections.md b/docs/common-tool/collections.md index db8272449..801a2d2e9 100644 --- a/docs/common-tool/collections.md +++ b/docs/common-tool/collections.md @@ -15,7 +15,7 @@ Collections 是 JDK 提供的一个工具类,位于 java.util 包下,提供 Collections 的用法很简单,在 Intellij IDEA 中敲完 `Collections.` 之后就可以看到它提供的方法了,大致看一下方法名和参数就能知道这个方法是干嘛的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/common-tool/collections-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/common-tool/collections-01.png) 为了节省大家的学习时间,我将这些方法做了一些分类,并列举了一些简单的例子。 @@ -109,7 +109,7 @@ System.out.println("填充后的结果:" + list); [HashMap 是线程不安全](https://mp.weixin.qq.com/s/qk_neCdzM3aB6pVWVTHhNw)的,这个我们前面讲到了。那其实 ArrayList 也是线程不安全的,没法在多线程环境下使用,那 Collections 工具类中提供了多个 synchronizedXxx 方法,这些方法会返回一个同步的对象,从而解决多线程中访问集合时的安全问题。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/common-tool/collections-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/common-tool/collections-02.png) 使用起来也非常的简单: diff --git a/docs/common-tool/guava.md b/docs/common-tool/guava.md index 332eafb8f..2d2a3068f 100644 --- a/docs/common-tool/guava.md +++ b/docs/common-tool/guava.md @@ -14,7 +14,7 @@ tag: 我由 Google 公司开源,目前在 GitHub 上已经有 39.9k 的铁粉了,由此可以证明我的受欢迎程度。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/common-tool/guava-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/common-tool/guava-01.png) 我的身体里主要包含有这些常用的模块:集合 [collections] 、缓存 [caching] 、原生类型支持 [primitives support] 、并发库 [concurrency libraries] 、通用注解 [common annotations] 、字符串处理 [string processing] 、I/O 等。新版的 JDK 中已经直接把我引入了,可想而知我有多优秀,忍不住骄傲了。 @@ -43,7 +43,7 @@ tag: Doug Lea,java.util.concurrent 包的作者,曾说过一句话:“null 真糟糕”。Tony Hoare,图灵奖得主、快速排序算法的作者,当然也是 null 的创建者,也曾说过类似的话:“null 的使用,让我损失了十亿美元。”鉴于此,我用 Optional 来表示可能为 null 的对象。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/common-tool/guava-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/common-tool/guava-02.png) 代码示例如下所示。 @@ -250,7 +250,7 @@ LoadingCache 就是缓存的主要操作对象了,常用的就是其中的 put 我觉得适用于每一个 Java 项目,至于其他的一些功能,比如说散列、事件总线、数学运算、反射,就等待你去发掘了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/common-tool/guava-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/common-tool/guava-03.png) diff --git a/docs/common-tool/hutool.md b/docs/common-tool/hutool.md index a2cbbc83c..ff7bf1b29 100644 --- a/docs/common-tool/hutool.md +++ b/docs/common-tool/hutool.md @@ -14,7 +14,7 @@ Hutool 的作者在官网上说,Hutool 是 Hu+tool 的自造词(好像不用 看了一下开发团队的一个成员介绍,一个 Java 后端工具的作者竟然爱前端、爱数码,爱美女,嗯嗯嗯,确实“难得糊涂”(手动狗头)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/common-tool/hutool-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/common-tool/hutool-01.png) 废话就说到这,来吧,实操走起! @@ -40,7 +40,7 @@ Hutool 的设计思想是尽量减少重复的定义,让项目中的 util 包 Hutool 对不仅对 JDK 底层的文件、流、加密解密、转码、正则、线程、XML等做了封装,还提供了以下这些组件: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/common-tool/hutool-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/common-tool/hutool-02.png) 非常多,非常全面,鉴于此,我只挑选一些我喜欢的来介绍下(偷偷地告诉你,我就是想偷懒)。 @@ -156,11 +156,11 @@ long copySize = IoUtil.copy(in, out, IoUtil.DEFAULT_BUFFER_SIZE); 在实际编码当中,我们通常需要从某些文件里面读取一些数据,比如配置文件、文本文件、图片等等,那这些文件通常放在什么位置呢? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/common-tool/hutool-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/common-tool/hutool-03.png) 放在项目结构图中的 resources 目录下,当项目编译后,会出现在 classes 目录下。对应磁盘上的目录如下图所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/common-tool/hutool-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/common-tool/hutool-04.png) 当我们要读取文件的时候,我是不建议使用绝对路径的,因为操作系统不一样的话,文件的路径标识符也是不一样的。最好使用相对路径。 @@ -313,7 +313,7 @@ public class ConsoleDemo { - 是不是电话号码 - 等等 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/common-tool/hutool-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/common-tool/hutool-05.png) ```java Validator.isEmail("沉默王二"); @@ -340,7 +340,7 @@ biMap.getKey("沉默王三"); 在实际的开发工作中,其实我更倾向于使用 Guava 的 BiMap,而不是 Hutool 的。这里提一下,主要是我发现了 Hutool 在线文档上的一处错误,提了个 issue(从中可以看出我一颗一丝不苟的心和一双清澈明亮的大眼睛啊)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/common-tool/hutool-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/common-tool/hutool-06.png) ### 13、图片工具 @@ -382,7 +382,7 @@ ImgUtil.pressText(// 趁机让大家欣赏一下二哥帅气的真容。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/common-tool/hutool-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/common-tool/hutool-07.png) ### 14、配置文件 diff --git a/docs/cs/os.md b/docs/cs/os.md index 6c68204d2..f5acb90d1 100644 --- a/docs/cs/os.md +++ b/docs/cs/os.md @@ -9,7 +9,7 @@ tag: >作者:月伴飞鱼,转载链接:[https://mp.weixin.qq.com/s/G9ZqwEMxjrG5LbgYwM5ACQ](https://mp.weixin.qq.com/s/G9ZqwEMxjrG5LbgYwM5ACQ) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-5ad16ae7-059f-44f1-8236-697b203bb72e.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-5ad16ae7-059f-44f1-8236-697b203bb72e.png) ## 计算机结构 @@ -37,7 +37,7 @@ tag: 输入设备;输出设备;内存;中央处理器;总线。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-eff4b87b-9091-443c-988b-721e9fd59d2f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-eff4b87b-9091-443c-988b-721e9fd59d2f.png) ### 内存 @@ -105,11 +105,11 @@ L3- 缓存同样在 CPU 中,位置比 L2- 缓存距离 CPU 核心更远,大 L3 缓存大小也是看型号的,比如 i9 CPU 有 512KB L1 Cache;有 2MB L2 Cache; 有16MB L3 Cache。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-214a4294-df48-4ed4-8f6a-15c93c02037d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-214a4294-df48-4ed4-8f6a-15c93c02037d.png) 当 CPU 需要内存中某个数据的时候,如果寄存器中有这个数据,我们可以直接使用;如果寄存器中没有这个数据,我们就要先查询 L1 缓存;L1 中没有,再查询 L2 缓存;L2 中没有再查询 L3 缓存;L3 中没有,再去内存中拿。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-936b2476-4704-4928-bfd0-d7417d0f1ab8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-936b2476-4704-4928-bfd0-d7417d0f1ab8.png) @@ -169,7 +169,7 @@ ring0是指CPU的运行级别,是最高级别,ring1次之,ring2更次之 CPU 从程序计数器读取指令、到执行、再到下一条指令,这个过程会不断循环,直到程序执行结束,这个不断循环的过程被称为 **CPU 的指令周期**。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-0e135ca2-26fa-4495-8a4b-3c9c05a4341e.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-0e135ca2-26fa-4495-8a4b-3c9c05a4341e.png) ### 总线 @@ -274,7 +274,7 @@ Kernel 运行在超级权限模式下,所以拥有很高的权限。 **举例:** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-29b6f34f-a8b3-48ec-8e5c-aea1036ea16a.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-29b6f34f-a8b3-48ec-8e5c-aea1036ea16a.png) 如上图所示:内核程序执行在内核态(Kernal Mode),用户程序执行在用户态(User Mode)。 @@ -366,7 +366,7 @@ Kernel 运行在超级权限模式下,所以拥有很高的权限。 ### 协程 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-586071a1-3d5f-4873-963f-d47b8aeea594.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-586071a1-3d5f-4873-963f-d47b8aeea594.png) 协程,是一种比线程更加轻量级的存在,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。 @@ -419,7 +419,7 @@ Kernel 运行在超级权限模式下,所以拥有很高的权限。 因此当进程 1 执行到一半时,会先挂起,然后进程 2 开始执行;进程 2 一次可以执行完,然后进程 3 开始执行,不过进程 3 一次执行不完,在执行了 1 个时间片段后,进程 1 开始执行;就这样如此周而复始,这个就是分时技术。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-606eb7bd-e9e1-457a-bf61-66b152c5ada5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-606eb7bd-e9e1-457a-bf61-66b152c5ada5.png) ### 创建进程 @@ -429,7 +429,7 @@ Kernel 运行在超级权限模式下,所以拥有很高的权限。 另一方面,如果程序员希望执行完一段代价昂贵的初始化过程后,将当前程序的状态复制好几份,变成一个个单独执行的进程,那么操作系统提供了 fork 指令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-85a86f1b-eeff-401e-8bfe-19e5509e2a53.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-85a86f1b-eeff-401e-8bfe-19e5509e2a53.png) 也就是说,每次 fork 会多创造一个克隆的进程,这个克隆的进程,所有状态都和原来的进程一样,但是会有自己的地址空间。 @@ -449,7 +449,7 @@ Kernel 运行在超级权限模式下,所以拥有很高的权限。 ### 进程状态 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-5f202fa5-0922-4b41-b4f2-f8a9af37a176.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-5f202fa5-0922-4b41-b4f2-f8a9af37a176.png) **创建状态** @@ -517,7 +517,7 @@ Kernel 运行在超级权限模式下,所以拥有很高的权限。 共享内存示意图: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-8644f82a-eda6-4abf-b86a-13c02e7af5fd.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-8644f82a-eda6-4abf-b86a-13c02e7af5fd.png) 一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。 @@ -663,7 +663,7 @@ signal() 操作用于唤醒被阻塞的进程。 多级队列,就是多个队列执行调度,先考虑最简单的两级模型 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-bcc4bc31-75b9-43f2-9f16-7a0e419e5615.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-bcc4bc31-75b9-43f2-9f16-7a0e419e5615.png) 上图中设计了两个优先级不同的队列,从下到上优先级上升,上层队列调度紧急任务,下层队列调度普通任务。 @@ -677,7 +677,7 @@ signal() 操作用于唤醒被阻塞的进程。 > 比如下图这个模型: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-85f35508-c541-483b-9f34-a54a0c619fff.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-85f35508-c541-483b-9f34-a54a0c619fff.png) 紧急任务仍然走高优队列,非抢占执行。 @@ -819,7 +819,7 @@ Linux系统中有很多的守护进程,最典型的就是我们经常看到的 `fork`系统调用会通过复制一个现有进程来创建一个全新的进程,新进程被存放在一个叫做任务队列的双向循环链表中,链表中的每一项都是类型为`task_struct`的进程控制块`PCB`的结构。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-ee3d0c6b-9d93-4f9d-95f3-86e7d195a8f8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-ee3d0c6b-9d93-4f9d-95f3-86e7d195a8f8.png) 每个进程都由独特换不相同的进程标识符(PID),通过`getpid()`函数可获取当前进程的进程标识符,通过`getppid()`函数可获得父进程的进程标识符。 @@ -892,12 +892,12 @@ CPU想要找到x在内存中的实际存放位置,只需要用进程的起始 1. **块式管理**:将内存分为几个固定大小的块,每个块中只包含一个进程,如果程序运行需要内存的话,操作系统就分配给它一块,如果程序运行只需要很小的空间的话,分配的这块内存很大一部分几乎被浪费了,这些在每个块中未被利用的空间,我们称之为碎片。 2. **页式管理**:把主存分为大小相等且固定的一页一页的形式,页较小,相对相比于块式管理的划分力度更大,提高了内存利用率,减少了碎片,页式管理通过页表对应逻辑地址和物理地址。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-2d1e6109-5c99-4ab8-b9b4-00c851386d65.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-2d1e6109-5c99-4ab8-b9b4-00c851386d65.png) 1. **段式管理**: 页式管理虽然提高了内存利用率,但是页式管理其中的页实际并无任何实际意义, 段式管理把主存分为一段段的,每一段的空间又要比一页的空间小很多 ,段式管理通过段表对应逻辑地址和物理地址。 2. **段页式管理机制:**段页式管理机制结合了段式管理和页式管理的优点,简单来说段页式管理机制就是把主存先分成若干段,每个段又分成若干页,也就是说**段页式管理机制**中段与段之间以及段的内部的都是离散的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-06d985c8-7c5a-46eb-8f04-e7a010a90f40.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-06d985c8-7c5a-46eb-8f04-e7a010a90f40.png) ### 虚拟地址 @@ -907,7 +907,7 @@ CPU想要找到x在内存中的实际存放位置,只需要用进程的起始 实际上完成虚拟地址转换为物理地址转换的硬件是 CPU 中含有一个被称为**内存管理单元(Memory Management Unit, MMU)**的硬件 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-ad8515bb-ce0c-43b3-b9ff-9d4d5fa0994e.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-ad8515bb-ce0c-43b3-b9ff-9d4d5fa0994e.png) **为什么要有虚拟地址空间** @@ -930,7 +930,7 @@ CPU想要找到x在内存中的实际存放位置,只需要用进程的起始 每当为一个虚拟页面寻找到一个物理页面之后,就在页表里增加一条记录来保留该映射关系,当然,随着虚拟页面进出物理内存,页表的内容也会不断更新变化。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-169790fd-8ef1-400f-a4a4-e291a729d2cb.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-169790fd-8ef1-400f-a4a4-e291a729d2cb.png) ### 虚拟内存 @@ -954,7 +954,7 @@ CPU想要找到x在内存中的实际存放位置,只需要用进程的起始 > 一定容量的内存和外存:在载入程序的时候,只需要将程序的一部分装入内存,而将其他部分留在外存,然后程序就可以执行了; -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-d2104a4e-cb28-4d90-b8f1-92cb15b0bb31.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-d2104a4e-cb28-4d90-b8f1-92cb15b0bb31.png) @@ -968,7 +968,7 @@ CPU想要找到x在内存中的实际存放位置,只需要用进程的起始 缺页中断的处理步骤如下,省略了中间很多的步骤,只保留最核心的几个步骤: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-5101231b-d8da-418e-b60c-574bcfa8b579.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-5101231b-d8da-418e-b60c-574bcfa8b579.png) ### 页面置换算法 @@ -1001,7 +1001,7 @@ CPU想要找到x在内存中的实际存放位置,只需要用进程的起始 Page 到 Frame 的映射,需要一种叫作页表的结构。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-d3ce7f2b-275c-422c-8793-f3e0e1894ecc.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-d3ce7f2b-275c-422c-8793-f3e0e1894ecc.png) 上图展示了 Page、Frame 和页表 (PageTable)三者之间的关系。 @@ -1058,7 +1058,7 @@ Page 大小和 Frame 大小通常相等,页表中记录的某个 Page 对应 3. 如果该页不在快表中,就访问内存中的页表,再从页表中得到物理地址,同时将页表中的该映射表项添加到快表中; 4. 当快表填满后,又要登记新页时,就按照一定的淘汰策略淘汰掉快表中的一个页。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-a9551753-2578-474e-a452-192d01382d22.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-a9551753-2578-474e-a452-192d01382d22.png) ### 内存管理单元 @@ -1066,7 +1066,7 @@ Page 大小和 Frame 大小通常相等,页表中记录的某个 Page 对应 ![](https://img-blog.csdnimg.cn/2da3a2f130cf415cb0b42c19fda70f30.png"> -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-51d4172c-068b-46b2-a7bc-fb4a6d39955b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-51d4172c-068b-46b2-a7bc-fb4a6d39955b.png) 当 CPU 需要执行一条指令时,如果指令中涉及内存读写操作,CPU 会把虚拟地址给 MMU,MMU 自动完成虚拟地址到真实地址的计算;然后,MMU 连接了地址总线,帮助 CPU 操作真实地址。 @@ -1096,7 +1096,7 @@ Page 大小和 Frame 大小通常相等,页表中记录的某个 Page 对应 又称循环首次适应法,由首次适应法演变而成,不同之处是分配内存时从上一次查找结束的位置开始继续查找 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-a072428d-1d01-4c93-b5bb-8d2f9721d2ba.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-a072428d-1d01-4c93-b5bb-8d2f9721d2ba.png) > 最佳适应算法(Best Fit) @@ -1108,7 +1108,7 @@ Page 大小和 Frame 大小通常相等,页表中记录的某个 Page 对应 又称最大适应算法,空闲分区以容量递减的次序链接,找到第一个能满足要求的空闲分区(也就是最大的分区)就进行分配 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-4673dda6-3940-4a15-ba09-6acfe095ff62.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-4673dda6-3940-4a15-ba09-6acfe095ff62.png) **总结** @@ -1166,7 +1166,7 @@ Page 大小和 Frame 大小通常相等,页表中记录的某个 Page 对应 操作系统会以进程为单位,分配系统资源(CPU时间片、内存等资源),进程是资源分配的最小单位。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-6a48d3af-0342-4529-a45e-3ff739d8b22c.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-6a48d3af-0342-4529-a45e-3ff739d8b22c.png) 调度:线程作为CPU调度和分配的基本单位,进程作为拥有资源的基本单位; @@ -1319,7 +1319,7 @@ CPU处理程序的时候一旦程序不在内存中,会产生缺页异常; 下面这张图显示了异常处理的流程: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/os-1555a2c9-121e-4b08-a685-2d789fc3b66b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/os-1555a2c9-121e-4b08-a685-2d789fc3b66b.png) > 相同点 diff --git a/docs/cs/wangluo.md b/docs/cs/wangluo.md index db54bd4fc..63ff7f526 100644 --- a/docs/cs/wangluo.md +++ b/docs/cs/wangluo.md @@ -10,7 +10,7 @@ tag: >作者:月伴飞鱼,转载链接:[https://mp.weixin.qq.com/s/7EddtzpwIRvYfw34QE4zvw](https://mp.weixin.qq.com/s/7EddtzpwIRvYfw34QE4zvw) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-608345cf-8378-4b34-bc91-ca6d2fa25da7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-608345cf-8378-4b34-bc91-ca6d2fa25da7.png) ## OSI七层模型 @@ -94,7 +94,7 @@ Linux给WIndows发包,不同系统语法不一致,如exe不能在`Linux`下 一层物理层时数据被称为**比特流**(Bits)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-d1fdc5fc-c955-4591-9c95-e297a64eccdc.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-d1fdc5fc-c955-4591-9c95-e297a64eccdc.png) ## TCP和IP模型 @@ -107,7 +107,7 @@ OSI模型注重通信协议必要的功能;TCP/IP更强调在计算机上实 - 第三层:网络层,主要是IP协议。主要负责寻址(找到目标设备的位置) - 第四层:数据链路层,主要是负责转换数字信号和物理二进制信号。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-fa4a4d20-f0db-4ba7-a31e-7772f2f132a8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-fa4a4d20-f0db-4ba7-a31e-7772f2f132a8.png) **四层网络协议的作用** @@ -127,7 +127,7 @@ OSI模型注重通信协议必要的功能;TCP/IP更强调在计算机上实 在数据链路层,对应的协议也会在IP数据包前端加上以太网的部首。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-1d3ce227-fd77-4b95-89dd-aeb786bb4b9e.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-1d3ce227-fd77-4b95-89dd-aeb786bb4b9e.png) 源设备和目标设备通过网线连接,就可以通过物理层的二进制传输数据。 @@ -135,7 +135,7 @@ OSI模型注重通信协议必要的功能;TCP/IP更强调在计算机上实 数据链路层>网络层>传输层>应用层,一层层的解码,最后就可以在浏览器中得到目标设备传送过来的**index.html**。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-731f7daa-1b47-4191-828f-c6e54d650604.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-731f7daa-1b47-4191-828f-c6e54d650604.png) **TCP/IP协议族** @@ -374,7 +374,7 @@ HTTPS 协议会对传输的数据进行加密,而加密过程是使用了非 HTTPS的整体过程分为证书验证和数据传输阶段,具体的交互过程如下: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-c1db0431-0eee-4c80-bb60-b7508a306864.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-c1db0431-0eee-4c80-bb60-b7508a306864.png) * Client发起一个HTTPS的请求 * Server把事先配置好的公钥证书返回给客户端。 @@ -395,7 +395,7 @@ HTTPS的整体过程分为证书验证和数据传输阶段,具体的交互过 通过数字证书的方式保证服务器公钥的身份,解决冒充的风险。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-45917c49-bb08-4f88-84d2-5e8ae53acc7c.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-45917c49-bb08-4f88-84d2-5e8ae53acc7c.png) ### 请求报文 @@ -403,7 +403,7 @@ HTTPS的整体过程分为证书验证和数据传输阶段,具体的交互过 HTTP 请求报文由3部分组成(请求行+请求头+请求体) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-3c00598c-43c2-44cd-96c6-ee4d40b97abd.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-3c00598c-43c2-44cd-96c6-ee4d40b97abd.png) **常见的HTTP报文头属性** @@ -431,7 +431,7 @@ HTTP 请求报文由3部分组成(请求行+请求头+请求体) 响应报文与请求报文一样,由三个部分组成(响应行,响应头,响应体) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-58884113-14dc-4cca-a63e-3320f31a4da5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-58884113-14dc-4cca-a63e-3320f31a4da5.png) **HTTP响应报文属性** @@ -499,7 +499,7 @@ TCP的全部功能体现在它首部中各字段的作用 > 1. 首部前20个字符固定、后面有4n个字节是根据需而增加的选项 > 2. 故 TCP首部最小长度 = 20字节 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-8011660d-24c8-460f-ac3d-b97ad9c99b13.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-8011660d-24c8-460f-ac3d-b97ad9c99b13.png) **端口**: @@ -565,7 +565,7 @@ TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲 ### 三次握手 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-f6a9438e-4eb8-4573-9ef5-30e07b8c31df.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-f6a9438e-4eb8-4573-9ef5-30e07b8c31df.png) **第一次握手**: @@ -625,7 +625,7 @@ TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲 ### 四次挥手 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-4a5e455f-5cf8-47a6-8fe4-a4c83a445f77.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-4a5e455f-5cf8-47a6-8fe4-a4c83a445f77.png) 挥手请求可以是Client端,也可以是Server端发起的,我们假设是Client端发起: @@ -688,7 +688,7 @@ TCP报文头有个字段叫Window,用于接收方通知发送方自己还有 发送方窗口内的序列号代表了那些已经被发送,但是还没有被确认的帧,或者是那些可以被发送的帧 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-d734c97b-d7d6-4af6-8674-524a81fb4dbe.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-d734c97b-d7d6-4af6-8674-524a81fb4dbe.png) 滑动窗口由四部分组成每个字节的数据都有唯一顺序的编码,随着时间发展,未确认部分与可以发送数据包编码部分向右移动,形式滑动窗口 @@ -717,7 +717,7 @@ TCP报文头有个字段叫Window,用于接收方通知发送方自己还有 主要的方式就是接收方返回的 ACK 中会包含自己的接收窗口的大小,并且利用大小来控制发送方的数据发送。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-4bdc0051-8878-4ffc-b9db-71b73ec49cd0.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-4bdc0051-8878-4ffc-b9db-71b73ec49cd0.png) **流量控制引发的死锁** @@ -796,7 +796,7 @@ TCP报文头有个字段叫Window,用于接收方通知发送方自己还有 注意:拥塞避免并非完全能够避免了阻塞,而是使网络比较不容易出现拥塞。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-314eba07-1388-4e8b-9307-8dd8d03b0dfe.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-314eba07-1388-4e8b-9307-8dd8d03b0dfe.png) **快重传算法** @@ -816,13 +816,13 @@ TCP报文头有个字段叫Window,用于接收方通知发送方自己还有 所以此时不执行慢开始算法,而是将cwnd设置为ssthresh减半后的值,然后执行拥塞避免算法,使cwnd缓慢增大。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-3a424e43-405f-494d-b700-093781b63035.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-3a424e43-405f-494d-b700-093781b63035.png) ### Socket 即套接字,是应用层 与 `TCP/IP` 协议族通信的中间软件抽象层,表现为一个封装了 TCP / IP协议族 的编程接口(API) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-eff20ef6-9d35-4075-8c53-ab52c7a46ac7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-eff20ef6-9d35-4075-8c53-ab52c7a46ac7.png) `Socket`不是一种协议,而是一个编程调用接口(`API`),属于传输层(主要解决数据如何在网络中传输) @@ -874,7 +874,7 @@ TFTP、DNS、DHCP、TFTP、SNMP(简单网络管理协议)、RIP基于不可靠 UDP的报文段共有2个字段:数据字段 + 首部字段 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-a5d0d209-01db-4ee7-b63b-bf2659545702.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-a5d0d209-01db-4ee7-b63b-bf2659545702.png) **UDP报文中每个字段的含义如下:** @@ -927,7 +927,7 @@ IP地址表示为:`xxx.xxx.xxx.xxx` IP地址分A、B、C、D、E五类,其中A、B、C这三类是比较常用的IP地址,D、E类为特殊地址。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-212f3f9b-b07f-4cc9-81eb-55bb478f1b66.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-212f3f9b-b07f-4cc9-81eb-55bb478f1b66.png) ### 子网掩码 @@ -971,7 +971,7 @@ IP地址分A、B、C、D、E五类,其中A、B、C这三类是比较常用的I 但是A与C,A与D,B与C,B与D它们之间不属于同一网段,所以它们通信是要经过本地网关,然后路由器根据对方IP地址,在路由表中查找恰好有匹配到对方IP地址的直连路由,于是从另一边网关接口转发出去实现互连 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-e1d68baa-9d8c-4595-bcb5-0dc5f6e240fe.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-e1d68baa-9d8c-4595-bcb5-0dc5f6e240fe.png) **子网掩码和IP地址的关系** @@ -987,7 +987,7 @@ IP地址分A、B、C、D、E五类,其中A、B、C这三类是比较常用的I 将得出的结果转化为十进制,便得到网络地址。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-5fab32f0-20d1-4c05-bfb9-47928ceac65d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-5fab32f0-20d1-4c05-bfb9-47928ceac65d.png) ### 网关 @@ -1051,9 +1051,9 @@ DNS通过主机名,最终得到该主机名对应的IP地址的过程叫做域 将主机域名转换为ip地址,属于应用层协议,使用UDP传输。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-7d129644-dfa7-4151-ae2f-9f3f084c6be9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-7d129644-dfa7-4151-ae2f-9f3f084c6be9.png) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/cs/wangluo-4ed70ed6-ceeb-4761-8fd4-eb2fe092273d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/cs/wangluo-4ed70ed6-ceeb-4761-8fd4-eb2fe092273d.png) 第一步,客户端向本地DNS服务器发送解析请求 diff --git a/docs/download/java.md b/docs/download/java.md index 34d265735..1a43c546f 100644 --- a/docs/download/java.md +++ b/docs/download/java.md @@ -27,13 +27,13 @@ tag: 这是我看过的一些书: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/download/java-1.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/download/java-1.jpg) 那其实很多人在学习编程的时候,很容易陷入一个误区,就是没有计划、没有路线,就导致看似投入了很多精力,但最后的学习效果却有点对不住付出的时间和精力。 为此,我花了将近一个月的时间,整理了这样一条学习路线,并且把我读过的电子书全部做了归类:**入门→工具→框架→数据库→并发编程→底层→性能优化→设计模式→操作系统→计算机网络→数据结构与算法→面试→架构→管理** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/download/java-2.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/download/java-2.jpg) 就连颈椎康复指南都有了,这波良心吧?大家可以通过下面的方式获取,我想不管是科班还是非科班的,只要你喜欢计算机、喜欢编程,应该都会有很大的帮助。 diff --git a/docs/download/jianli.md b/docs/download/jianli.md index 5f11b8662..f6358a4c4 100644 --- a/docs/download/jianli.md +++ b/docs/download/jianli.md @@ -38,7 +38,7 @@ tag: 首先来看一下这位学弟的专业技能部分。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/download/jianli-2c93d164-55f7-4d2f-b0d0-f0857644a265) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/download/jianli-2c93d164-55f7-4d2f-b0d0-f0857644a265) 其实,这部分他写的已经挺好的了,简单大方、条理清晰。 @@ -68,13 +68,13 @@ tag: 下面是我当时给他的修改意见。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/download/jianli-f5c60946-3cdd-4ac6-9e20-8360808f8612) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/download/jianli-f5c60946-3cdd-4ac6-9e20-8360808f8612) ### 项目经历 其实一份校招简历,最重要的就是项目经历、实习经历(有则加分,没有也行)、专业技能了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/download/jianli-a715eaa8-86c5-41ce-aee7-cd1873e2eec8) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/download/jianli-a715eaa8-86c5-41ce-aee7-cd1873e2eec8) 可以从这位学弟的描述中看出来,他毕业后的这四个月里确实做了不少的工作,但感觉没说出来自己所做工作的意义和难点所在。 @@ -108,7 +108,7 @@ tag: 以下是修改意见标注 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/download/jianli-5b6ff7c0-c8ad-4874-8ee8-7593e3ee1f5f) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/download/jianli-5b6ff7c0-c8ad-4874-8ee8-7593e3ee1f5f) 至此,帮他把第一个项目修改完毕了,**针对第二个项目的修改**,我就不多BB了,直接贴上来吧。 @@ -122,7 +122,7 @@ tag: **下面是当时给他做的标注**: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/download/jianli-f2aec848-d462-491b-9fe1-7d939ea16ed8) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/download/jianli-f2aec848-d462-491b-9fe1-7d939ea16ed8) 其实对于该部分的修改,我是紧紧抓住一个要点,那就是尽可能使用具化的指标即明确的信息而不是概括性的描述。 @@ -136,12 +136,12 @@ tag: 首先是原文。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/download/jianli-eabee6e7-0fcf-4fb7-9ca8-0ada1c604acf) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/download/jianli-eabee6e7-0fcf-4fb7-9ca8-0ada1c604acf) 我给的修改意见批注。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/download/jianli-bb7279ad-dd46-47ea-8f13-37d5caaf404e) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/download/jianli-bb7279ad-dd46-47ea-8f13-37d5caaf404e) 1、在工作经历这里,注意细节问题,比如你的岗位名称是什么?你在实习过程中,你的工作有哪些?在这过程中用到了哪些技能? diff --git a/docs/download/progit.md b/docs/download/progit.md index a6ccf044b..ee68eb004 100644 --- a/docs/download/progit.md +++ b/docs/download/progit.md @@ -8,7 +8,7 @@ tag: 今天给大家分享一本个人最近看过觉得非常不错的Git开源手册,可能有些小伙伴也看过了,我是最近在通勤路上用PAD看的。这本开源手册,它除了有**PDF版**,还有**epub电子书版**,非常适合电子阅读: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/download/progit-41240dae-f097-4986-b1a3-20f6a8035732) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/download/progit-41240dae-f097-4986-b1a3-20f6a8035732) 需要的小伙伴请扫描下方的二维码关注作者的原创公众号「**沉默王二**」回复关键字「**git**」就可以拉取到下载链接了。 @@ -18,27 +18,27 @@ tag: 这本手册在豆瓣上评价极高,之前9.3,现在也有9.1的高分,其作者是GitHub的员工,内容主要侧重于各种场合中的惯用法和底层原理的讲述,手册中还针对不同的使用场景,设计了几个合适的版本管理策略。简而言之,这本手册无论是对于初学者还是想进一步了解Git工作原理的开发者都非常合适。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/download/progit-66757d24-4bcb-4084-9e7f-8e9e32d517c4) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/download/progit-66757d24-4bcb-4084-9e7f-8e9e32d517c4) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/download/progit-b829ef9a-3c8f-4e91-b88b-3eeb1692d8d9) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/download/progit-b829ef9a-3c8f-4e91-b88b-3eeb1692d8d9) 这个手册一共分为十章,详细内容如下: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/download/progit-33b772d5-fa59-4181-8831-9efe9c1b11ea) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/download/progit-33b772d5-fa59-4181-8831-9efe9c1b11ea) **手册中部分内容展示如下:** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/download/progit-f8c62f18-ceaa-4de1-b7ad-961cd1418bfb) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/download/progit-f8c62f18-ceaa-4de1-b7ad-961cd1418bfb) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/download/progit-d82932c6-5a30-4ed6-86e5-e4ca468e8a13) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/download/progit-d82932c6-5a30-4ed6-86e5-e4ca468e8a13) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/download/progit-ca7c7781-7c3f-4729-9cd7-2c050b660e4e) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/download/progit-ca7c7781-7c3f-4729-9cd7-2c050b660e4e) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/download/progit-91c726c1-a10d-41fb-8b2a-a5228d741106) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/download/progit-91c726c1-a10d-41fb-8b2a-a5228d741106) **需要该Git手册PDF+epub电子书的小伙伴:** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/download/progit-eff913ad-4635-498f-8a04-01a03380e84a) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/download/progit-eff913ad-4635-498f-8a04-01a03380e84a) 可扫描下方的二维码关注作者的原创公众号「**沉默王二**」回复关键字「**git**」就可以拉取到下载链接了。 diff --git a/docs/elasticsearch/rumen.md b/docs/elasticsearch/rumen.md index 4a841cf06..56f6028b2 100644 --- a/docs/elasticsearch/rumen.md +++ b/docs/elasticsearch/rumen.md @@ -13,7 +13,7 @@ tag: 经过三天三夜的学习,总算是入了 Elasticsearch 的门,我就决定把这些心得体会分享出来,感兴趣的小伙伴可以作为参考。遇到文章中有错误的地方,不要手下留情,过来捶我,只要不打脸就好。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/elasticsearch/rumen-ebb2bdbc-2cdb-4540-b48f-41f92c848f2f) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/elasticsearch/rumen-ebb2bdbc-2cdb-4540-b48f-41f92c848f2f) ### 01、Elasticsearch 是什么 @@ -24,7 +24,7 @@ tag: Elastic Stack 又是什么呢?整个架构图如下图(来源于网络,侵删)所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/elasticsearch/rumen-04b04318-25c9-4eb5-895e-9c608a4b26f9) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/elasticsearch/rumen-04b04318-25c9-4eb5-895e-9c608a4b26f9) 信息量比较多,对吧?那就记住一句话吧,Elasticsearch 是 Elastic Stack 的核心。 @@ -44,7 +44,7 @@ Elasticsearch 是由 Java 开发的,所以早期的版本需要先在电脑上 Elasticsearch 是免安装的,只需要把 zip 包解压就可以了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/elasticsearch/rumen-07da0521-74eb-4a90-b17f-59258e622609) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/elasticsearch/rumen-07da0521-74eb-4a90-b17f-59258e622609) 1)bin 目录下是一些脚本文件,包括 Elasticsearch 的启动执行文件。 @@ -62,11 +62,11 @@ Elasticsearch 是免安装的,只需要把 zip 包解压就可以了。 直接双击 bin 目录下的 elasticsearch.bat 文件就可以启动 Elasticsearch 服务了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/elasticsearch/rumen-7dd19afd-1aeb-49b6-a07c-f11e139fe3d3) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/elasticsearch/rumen-7dd19afd-1aeb-49b6-a07c-f11e139fe3d3) 输出的日志信息有点多,不用细看,注意看到有“started”的字样就表明启动成功了。为了进一步确认 Elasticsearch 有没有启动成功,可以在浏览器的地址栏里输入 `http://localhost:9200` 进行查看(9200 是 Elasticsearch 的默认端口号)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/elasticsearch/rumen-51f269c2-7482-494a-8a04-6585f20176a7) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/elasticsearch/rumen-51f269c2-7482-494a-8a04-6585f20176a7) 你看,为了 Search。 @@ -82,29 +82,29 @@ Elasticsearch 是免安装的,只需要把 zip 包解压就可以了。 最新的版本是 7.6.2,284M 左右,体积和 Elasticsearch 差不多。选择下载 Windows 版,zip 格式的,完成后直接解压就行了。下载的过程中又去洗了 6 颗葡萄吃,狗头。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/elasticsearch/rumen-12372ee6-acc0-4425-964b-ca32886f17ce) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/elasticsearch/rumen-12372ee6-acc0-4425-964b-ca32886f17ce) 包目录不再一一解释了,进入 bin 目录下,双击运行 kibana.bat 文件,启动 Kibana 服务。整个过程比 Elasticsearch 要慢一些,当看到 `[Kibana][http] http server running` 的信息后,说明服务启动成功了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/elasticsearch/rumen-784d70ef-b6e7-4312-85f1-36ace9b2a5bd) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/elasticsearch/rumen-784d70ef-b6e7-4312-85f1-36ace9b2a5bd) 在浏览器地址栏输入 `http://localhost:5601` 查看 Kibana 的图形化界面。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/elasticsearch/rumen-e6f64545-a925-4bb4-a25e-44129832fb4e) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/elasticsearch/rumen-e6f64545-a925-4bb4-a25e-44129832fb4e) 由于当前的 Elasticsearch 服务端中还没有任何数据,所以我们可以选择「Try Our Sample Data」导入 Kibana 提供的模拟数据体验一下。下图是导入电商数据库的看板页面,是不是很丰富? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/elasticsearch/rumen-a16d99ff-272d-43bb-aa94-23b240cc464b) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/elasticsearch/rumen-a16d99ff-272d-43bb-aa94-23b240cc464b) 打开 Dev Tools 面板,可以看到一个简单的 DSL 查询语句(一种完全基于 JSON 的特定于领域的语言),点击「运行」按钮后就可以看到 JSON 格式的数据了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/elasticsearch/rumen-5c44bd79-d3a9-49fb-9414-04dc38840cfb) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/elasticsearch/rumen-5c44bd79-d3a9-49fb-9414-04dc38840cfb) ### 04、Elasticsearch 的关键概念 在进行下一步之前,需要先来理解 Elasticsearch 中的几个关键概念,比如说什么是索引,什么是类型,什么是文档等等。Elasticsearch 既然是一个数据引擎,它里面的一些概念就和 MySQL 有一定的关系。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/elasticsearch/rumen-ad2b2f8c-5a19-4c5e-9bc7-cf7ba17830bf) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/elasticsearch/rumen-ad2b2f8c-5a19-4c5e-9bc7-cf7ba17830bf) 看完上面这幅图(来源于网络,侵删),是不是瞬间就清晰了。向 Elasticsearch 中存储数据,其实就是向 Elasticsearch 中的 index 下面的 type 中存储 JSON 类型的数据。 @@ -175,7 +175,7 @@ public class ElasticsearchTest { 也可以通过 Kibana 的 Dev Tools 面板查看“writer”索引,结果如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/elasticsearch/rumen-64baa243-0075-436e-a070-f28813fee284) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/elasticsearch/rumen-64baa243-0075-436e-a070-f28813fee284) diff --git a/docs/exception/gailan.md b/docs/exception/gailan.md index 821cde434..bd929cf2e 100644 --- a/docs/exception/gailan.md +++ b/docs/exception/gailan.md @@ -83,7 +83,7 @@ checked 异常(检查型异常)在源代码里必须显式地捕获或者抛 “我先画一幅思维导图给你感受一下。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/exception/gailan-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/exception/gailan-01.png) 首先,Exception 和 Error 都继承了 Throwable 类。换句话说,只有 Throwable 类(或者子类)的对象才能使用 throw 关键字抛出,或者作为 catch 的参数类型。 @@ -111,7 +111,7 @@ Class clz = Class.forName("com.itwanger.s41.Demo1"); 如果没做处理,比如说在 Intellij IDEA 环境下,就会提示你这行代码可能会抛出 `java.lang.ClassNotFoundException`。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/exception/gailan-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/exception/gailan-02.png) 建议你要么使用 try-catch 进行捕获: @@ -220,7 +220,7 @@ Exception in thread "main" java.lang.ArithmeticException: 年纪未满 18 岁, `Class.forName()` 方法在执行的时候可能会遇到 `java.lang.ClassNotFoundException` 异常,一个检查型异常,如果没有做处理,IDEA 就会提示你,要么在方法签名上声明,要么放在 try-catch 中。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/exception/throw-throws-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/exception/throw-throws-01.png) “那什么情况下使用 throws 而不是 try-catch 呢?”三妹问。 @@ -457,7 +457,7 @@ static int test2 () { “三妹,来看一下源码的文档注释就全明白了!” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/exception/try-catch-finally-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/exception/try-catch-finally-01.png) 至于参数 status 的值也很好理解,如果是异常退出,设置为非 0 即可,通常用 1 来表示;如果是想正常退出程序,用 0 表示即可。 diff --git a/docs/exception/npe.md b/docs/exception/npe.md index 638ad2491..d16f9ce9b 100644 --- a/docs/exception/npe.md +++ b/docs/exception/npe.md @@ -171,7 +171,7 @@ Object听到这话,皱了皱眉,他沉默了一会儿,缓缓站起身子 我见他好像魔怔了,仿佛在思考什么,于是迈步走到他刚才站立的地方看着前面,原来,这是他们的族谱!这里是异常的祠堂! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/exception/npe-1) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/exception/npe-1) 看完这张族谱,我恍然大悟,好像明白了什么。突然,我的脑袋里出现了一个冰冷的机器声音:“获取异常族谱,历练完成度+100。” diff --git a/docs/git/git-qiyuan.md b/docs/git/git-qiyuan.md index c2bfd5b37..00c14b501 100644 --- a/docs/git/git-qiyuan.md +++ b/docs/git/git-qiyuan.md @@ -12,7 +12,7 @@ tag: Git 是一个分布式版本控制系统,缔造者是大名鼎鼎的林纳斯·托瓦茲 (Linus Torvalds),Git 最初的目的是为了能更好的管理 Linux 内核源码。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/git-qiyuan-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/git-qiyuan-01.png) PS:**为了能够帮助更多的 Java 爱好者,已将《Java 程序员进阶之路》开源到了 GitHub(本篇已收录)。如果你也喜欢这个专栏,觉得有帮助的话,可以去点个 star,这样也方便以后进行更系统化的学习**: @@ -54,7 +54,7 @@ Junio Hamano 觉得 Linus 设计的这些命令对于普通用户不太友好, 如今,Git 已经成为全球软件开发者的标配。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/git-qiyuan-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/git-qiyuan-02.png) 原本的 Git 只适用于 Unix/Linux 平台,但随着 Cygwin、msysGit 环境的成熟,以及 TortoiseGit 这样易用的GUI工具,Git 在 Windows 平台下也逐渐成熟。 @@ -65,7 +65,7 @@ Junio Hamano 觉得 Linus 设计的这些命令对于普通用户不太友好, Git 和传统的版本控制工具 CVS、SVN 有不小的区别,前者关心的是文件的整体性是否发生了改变,后两者更关心文件内容上的差异。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/git-qiyuan-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/git-qiyuan-03.png) 除此之外,Git 更像是一个文件系统,每个使用它的主机都可以作为版本库,并且不依赖于远程仓库而离线工作。开发者在本地就有历史版本的副本,因此就不用再被远程仓库的网络传输而束缚。 @@ -88,7 +88,7 @@ Git 中的绝大多数操作都只需要访问本地文件和资源,一般不 Git 的工作流程是这样的: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/git-qiyuan-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/git-qiyuan-04.png) - 在工作目录中修改文件 @@ -102,11 +102,11 @@ Git 的工作流程是这样的: >https://git-scm.com/downloads -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/git-qiyuan-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/git-qiyuan-05.png) 我个人使用的 macOS 系统,可以直接使用 `brew install git` 命令安装,非常方便。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/git-qiyuan-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/git-qiyuan-06.png) 安装成功后,再使用 `git --version` 就可以查看版本号了,我本机上安装的是 2.23.0 版本。 @@ -287,7 +287,7 @@ TREE: 目录树对象。在 Linus 的设计里,TREE 对象就是一个时间 另外,由于 TREE 上记录文件名及属性信息,对于修改文件属性或修改文件名、移动目录而不修改文件内容的情况,可以复用 BLOB 对象,节省存储资源。而 Git 在后来的开发演进中又优化了 TREE 的设计,变成了某一时间点文件夹信息的抽象,TREE 包含其子目录的 TREE 的对象信息(SHA1)。这样,对于目录结构很复杂或层级较深的 Git 库 可以节约存储资源。历史信息被记录在第三种对象 CHANGESET 里。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/neibushixian-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/neibushixian-01.png) CHANGESET:即 Commit 对象。一个 CHANGESET 对象中记录了该次提交的 TREE 对象信息(SHA1),以及提交者(committer)、提交备注(commit message)等信息。 @@ -304,7 +304,7 @@ Linus 解释了“当前目录缓存”的设计,该缓存就是一个二进 - 1. 能够快速的复原缓存的完整内容,即使不小心把当前工作区的文件删除了,也可以从缓存中恢复所有文件; - 2. 能够快速找出缓存中和当前工作区内容不一致的文件。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/neibushixian-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/neibushixian-02.png) Linus 在 Git 的第一次代码提交里便完成了 Git 的最基础功能,并可以编译使用。代码极为简洁,加上 Makefile 一共只有 848 行。感兴趣的话可以通过上一段所述方法 checkout Git 最早的 commit 上手编译玩玩,只要有 Linux 环境即可。 @@ -332,7 +332,7 @@ Linus 在 Git 的第一次代码提交里便完成了 Git 的最基础功能, 一般来说,日常使用只要记住下图中这 6 个命令就可以了,但是熟练使用 Git,恐怕要记住60~100个命令~ -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/mingling-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/mingling-01.png) @@ -641,7 +641,7 @@ $ git archive 但是,太方便了也会产生副作用。如果你不加注意,很可能会留下一个枝节蔓生、四处开放的版本库,到处都是分支,完全看不出主干发展的脉络。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/fenzhi-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/fenzhi-01.png) 那有没有一个好的分支策略呢?答案当然是有的。 @@ -650,7 +650,7 @@ $ git archive 首先,代码库应该有一个、且仅有一个主分支。所有提供给用户使用的正式版本,都在这个主分支上发布。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/fenzhi-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/fenzhi-02.png) Git主分支的名字,默认叫做Master。它是自动建立的,版本库初始化以后,默认就是在主分支在进行开发。 @@ -658,7 +658,7 @@ Git主分支的名字,默认叫做Master。它是自动建立的,版本库 主分支只用来发布重大版本,日常开发应该在另一条分支上完成。我们把开发用的分支,叫做Develop。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/fenzhi-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/fenzhi-03.png) 这个分支可以用来生成代码的最新隔夜版本(nightly)。如果想正式对外发布,就在Master分支上,对Develop分支进行"合并"(merge)。 @@ -680,11 +680,11 @@ Git创建Develop分支的命令: 这里稍微解释一下上一条命令的--no-ff参数是什么意思。默认情况下,Git执行"快进式合并"(fast-farward merge),会直接将Master分支指向Develop分支。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/fenzhi-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/fenzhi-04.png) 使用--no-ff参数后,会执行正常合并,在Master分支上生成一个新节点。为了保证版本演进的清晰,我们希望采用这种做法。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/fenzhi-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/fenzhi-05.png) ### 3、临时性分支 @@ -702,7 +702,7 @@ Git创建Develop分支的命令: **第一种是功能分支**,它是为了开发某种特定功能,从Develop分支上面分出来的。开发完成后,要再并入Develop。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/fenzhi-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/fenzhi-06.png) 功能分支的名字,可以采用feature-*的形式命名。 @@ -755,7 +755,7 @@ Git创建Develop分支的命令: 修补bug分支是从Master分支上面分出来的。修补结束以后,再合并进Master和Develop分支。它的命名,可以采用fixbug-*的形式。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/fenzhi-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/fenzhi-07.png) 创建一个修补bug分支: ``` @@ -791,7 +791,7 @@ Git创建Develop分支的命令: 新建一个文件夹,比如说 testgit,然后使用 `git init` 命令就可以把这个文件夹初始化为 Git 仓库了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-01.png) 初始化Git 仓库成功后,可以看到多了一个 .git 的目录,没事不要乱动,免得破坏了 Git 仓库的结构。 @@ -802,17 +802,17 @@ Git创建Develop分支的命令: 第二步,使用 `git commit` 命令告诉 Git,把文件提交到仓库。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-02.png) 可以使用 `git status` 来查看是否还有文件未提交。 也可以在文件中新增一行内容“传统美德不能丢,记得点赞哦~”,再使用 `git status` 来查看结果。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-03.png) 如果想查看文件到底哪里做了修改,可以使用 `git diff` 命令: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-04.png) 确认修改的内容后,可以使用 `git add` 和 `git commit` 再次提交。 @@ -822,11 +822,11 @@ Git创建Develop分支的命令: 现在我已经对 readme.txt 文件做了三次修改了。可以通过 `git log` 命令来查看历史记录: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-05.png) 也可以通过 `gitk` 这个命令来启动图形化界面来查看版本历史。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-06.png) 如果想回滚的话,比如说回滚到上一个版本,可以执行以下两种命令: @@ -835,15 +835,15 @@ Git创建Develop分支的命令: 2)`git reset --hard HEAD~100`,如果回滚到前 100 个版本,用这个命令比上一个命令更方便。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-07.png) 那假如回滚错了,想恢复,不记得版本号了,可以先执行 `git reflog` 命令查看版本号: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-08.png) 然后再通过 `git reset --hard` 命令来恢复: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-09.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-09.png) ### 3、工作区和暂存区的区别 @@ -865,7 +865,7 @@ Git 在提交文件的时候分两步,第一步 `git add` 命令是把文件 原子性带来的好处是显而易见的,这使得我们可以把项目整体还原到某个时间点,就这一点,SVN 就完虐 CVS 这些代码版本管理系统了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-10.png) Git 作为逼格最高的代码版本管理系统,自然要借鉴 SVN 这个优良特性的。但不同于 SVN 的是,Git 一开始搞的都是命令行,没有图形化界面,如果想要像 SVN 那样一次性选择多个文件或者不选某些文件(见上图),还真特喵的是个麻烦事。 @@ -881,7 +881,7 @@ Git 作为逼格最高的代码版本管理系统,自然要借鉴 SVN 这个 我们先用 `git status` 命令查看一下状态,再用 `git add` 将文件添加到暂存区,最后再用 `git commit` 一次性提交到 Git 仓库。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-11.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-11.png) ### 4、撤销修改 @@ -895,17 +895,17 @@ Git 作为逼格最高的代码版本管理系统,自然要借鉴 SVN 这个 答案当然是有了,其实在我们执行 `git status` 命令查看 Git 状态的时候,结果就提示我们可以使用 `git restore` 命令来撤销这次操作的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-12.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-12.png) 那其实在 git version 2.23.0 版本之前,是可以通过 `git checkout` 命令来完成撤销操作的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-13.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-13.png) checkout 可以创建分支、导出分支、切换分支、从暂存区删除文件等等,一个命令有太多功能就容易让人产生混淆。2.23.0 版本改变了这种混乱局面,git switch 和 git restore 两个新的命令应运而生。 switch 专注于分支的切换,restore 专注于撤销修改。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-14.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-14.png) ### 5、远程仓库 @@ -923,7 +923,7 @@ Git 是一款分布式版本控制系统,所以同一个 Git 仓库,可以 **第一步,通过 `ls -al ~/.ssh` 命令检查 SSH 密钥是否存在** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-15.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-15.png) 如果没有 id_rsa.pub、id_ecdsa.pub、id_ed25519.pub 这 3 个文件,表示密钥不存在。 @@ -937,21 +937,21 @@ ssh-keygen -t ed25519 -C "your_email@example.com 然后一路回车: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-16.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-16.png) 记得复制一下密钥,在 id_ed25519.pub 文件中: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-17.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-17.png) **第三步,添加 SSH 密钥到 GitHub 帐户** 在个人账户的 settings 菜单下找到 SSH and GPG keys,将刚刚复制的密钥添加到 key 这一栏中,点击「add SSH key」提交。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-18.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-18.png) Title 可不填写,提交成功后会列出对应的密钥: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-19.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-19.png) **为什么 GitHub 需要 SSH 密钥呢**? @@ -961,17 +961,17 @@ Title 可不填写,提交成功后会列出对应的密钥: 点击新建仓库,填写仓库名称等信息: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-20.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-20.png) **第五步,把本地仓库同步到 GitHub** 复制远程仓库的地址: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-21.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-21.png) 在本地仓库中执行 `git remote add` 命令将 GitHub 仓库添加到本地: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-22.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-22.png) 当我们第一次使用Git 的 push 命令连接 GitHub 时,会得到一个警告⚠️: @@ -985,28 +985,28 @@ Are you sure you want to continue connecting (yes/no/[fingerprint])? yes 接下来,我们使用 `git push` 命令将当前本地分支推送到 GitHub。加上了 -u 参数后,Git 不但会把本地的 master 分支推送的远程 master 分支上,还会把本地的 master 分支和远程的master 分支关联起来,在以后的推送或者拉取时就可以简化命令(比如说 `git push github master`)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-23.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-23.png) 此时,我们刷一下 GitHub,可以看到多了一个 master 分支,并且本地的两个文件都推送成功了! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-24.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-24.png) 从现在开始,只要本地做了修改,就可以通过 `git push` 命令推送到 GitHub 远程仓库了。 还可以使用 `git clone` 命令将远程仓库拷贝到本地。比如说我现在有一个 3.4k star 的仓库 JavaBooks, -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-25.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-25.png) 然后我使用 `git clone` 命令将其拷贝到本地。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/jibenshiyong-26.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/jibenshiyong-26.png) ## 八、详解 sparse-checkout 命令 前天不是搭建了一个《Java 程序员进阶之路》的网站嘛,其中用到了 Git 来作为云服务器和 GitHub 远程仓库之间的同步工具。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/sparse-checkout-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/sparse-checkout-01.png) @@ -1019,7 +1019,7 @@ Are you sure you want to continue connecting (yes/no/[fingerprint])? yes 首先给大家通报一下,一天前[上线的《Java 程序员进阶之路》网站](https://tobebetterjavaer.com),目前访问次数已经突破 1000 了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/sparse-checkout-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/sparse-checkout-03.png) 正所谓**不积跬步无以至千里,不积小流无以成江海**。 @@ -1034,7 +1034,7 @@ Are you sure you want to continue connecting (yes/no/[fingerprint])? yes 大家可以先看一下我这个 GitHub 仓库的目录结构哈。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/sparse-checkout-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/sparse-checkout-04.png) - docs 是文档目录,里面是 md 文件,所有的教程原稿都在这里。 - codes 是代码目录,里面是教程的配套源码。 @@ -1042,11 +1042,11 @@ Are you sure you want to continue connecting (yes/no/[fingerprint])? yes 这样就可以利用 GitHub 来做免费的图床,并且还可以白票 jsDelivr CDN 的全球加速,简直不要太爽! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/sparse-checkout-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/sparse-checkout-05.png) 比如说 images 目录下有一张 logo 图 logo-01.png: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/sparse-checkout-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/sparse-checkout-06.png) 如果使用 GitHub 仓库的原始路径来访问的话,速度贼慢! @@ -1054,7 +1054,7 @@ Are you sure you want to continue connecting (yes/no/[fingerprint])? yes 使用 jsDelivr 加速后就不一样了,速度飞起! ->https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/logo-01.png +>http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/logo-01.png 简单总结下 GitHub 作为图床的正确用法,就两条: @@ -1074,7 +1074,7 @@ Are you sure you want to continue connecting (yes/no/[fingerprint])? yes 最后还是浏览 Git 官方手册(也可以看[Pro Git](https://mp.weixin.qq.com/s/RpFzXOa2VlFNd7ylLmr9LQ))才找到了一个牛逼的命令:**git sparse-checkout,它可以帮助我们在拉取远程仓库的时候只同步那些我们想要的目录和文件**。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/sparse-checkout-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/sparse-checkout-07.png) 具体怎么用,可以看官方文档: >https://git-scm.com/docs/git-sparse-checkout @@ -1083,7 +1083,7 @@ Are you sure you want to continue connecting (yes/no/[fingerprint])? yes 第一步,通过 `git remote add -f orgin git@github.com:itwanger/toBeBetterJavaer.git` 命令从 GitHub 上拉取仓库。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/sparse-checkout-08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/sparse-checkout-08.png) 第二步,启用 sparse-checkout,并初始化 @@ -1091,30 +1091,30 @@ Are you sure you want to continue connecting (yes/no/[fingerprint])? yes 然后再执行 `git sparse-checkout init` 初始化。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/sparse-checkout-09.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/sparse-checkout-09.png) 第三步,使用 sparse-checkout 来拉取我们想要的仓库目录 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/sparse-checkout-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/sparse-checkout-10.png) 比如说,我们只想拉取 docs 目录,可以执行 `git sparse-checkout set docs` 命令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/sparse-checkout-11.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/sparse-checkout-11.png) 如果是第一次使用 sparse-checkout 的话,还需要执行一下 `git pull orgin master` 命令拉取一次。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/sparse-checkout-12.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/sparse-checkout-12.png) 第四步,验证是否生效 可以执行 `ls -al` 命令来确认 sparse-checkout 是否生效。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/sparse-checkout-13.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/sparse-checkout-13.png) 如图所示,确实只拉取到了 docs 目录。 假如还想要拉取其他文件或者目录的话,可以通过 `git sparse-checkout add` 命令来添加。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/sparse-checkout-14.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/sparse-checkout-14.png) 这就实现了,**远程仓库和云服务器仓库之间的定制化同步,需要什么目录和文件就同步什么目录和文件,不需要的可以统统不要**。 @@ -1122,13 +1122,13 @@ GitHub 仓库可以免费用,空间也无限大,但云服务可是要抠抠 我对比了一下,远程仓库大概 145 M,图片就占了 72 M,妥妥地省下了一半的存储空间。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/sparse-checkout-15.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/sparse-checkout-15.png) 如何禁用 git sparse-checkout 呢? 也简单,只需要执行一下 `git sparse-checkout disable` 命令就可以了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/sparse-checkout-16.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/sparse-checkout-16.png) 可以看到,那些我们不想要的目录和文件统统都又回来了。 @@ -1136,7 +1136,7 @@ GitHub 仓库可以免费用,空间也无限大,但云服务可是要抠抠 也简单,只需要执行一下 `git sparse-checkout reapply` 命令就可以了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/sparse-checkout-17.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/sparse-checkout-17.png) 简单总结下:如果你要把一个庞大到撑满你硬盘的远程仓库拉取到本地,而你只需要其中的一部分目录和文件,那就可以试一试 `git sparse-checkout` 了。 @@ -1147,7 +1147,7 @@ GitHub 仓库可以免费用,空间也无限大,但云服务可是要抠抠 不得不说,Git 实在是太强大了。就一行命令,解决了困扰我一天的烦恼,我的 80G 存储空间的云服务器又可以再战 3 年了,从此以后再也不用担心了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/sparse-checkout-18.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/sparse-checkout-18.png) Git 是真的牛逼,Linus 是真的牛逼,神不愧是神! diff --git a/docs/git/progit.md b/docs/git/progit.md index 6875e8c3b..e506305a9 100644 --- a/docs/git/progit.md +++ b/docs/git/progit.md @@ -1,36 +1,36 @@ 今天给大家分享一本个人最近看过觉得非常不错的Git开源手册,可能有些小伙伴也看过了,我是最近在通勤路上用PAD看的。这本开源手册,它除了有**PDF版**,还有**epub电子书版**,非常适合电子阅读,有需要的小伙伴可以在文末自行下载: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/progit-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/progit-01.png) 相信看完对于个人Git知识体系的梳理和掌握是非常有帮助的。 这本手册在豆瓣上评价极高,之前9.3,现在也有9.1的高分,其作者是GitHub的员工,内容主要侧重于各种场合中的惯用法和底层原理的讲述,手册中还针对不同的使用场景,设计了几个合适的版本管理策略。简而言之,这本手册无论是对于初学者还是想进一步了解Git工作原理的开发者都非常合适。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/progit-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/progit-02.png) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/progit-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/progit-03.png) 这个手册一共分为十章,详细内容如下: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/progit-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/progit-04.png) **手册中部分内容展示如下:** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/progit-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/progit-05.png) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/progit-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/progit-06.png) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/progit-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/progit-07.png) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/progit-08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/progit-08.png) **需要该Git手册PDF+epub电子书的小伙伴:** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/git/progit-09.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/git/progit-09.png) 可直接长按扫码关注下方二维码,回复 「**git**」 即可下载: -![(长按扫码识别)](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/itwanger.png) +![(长按扫码识别)](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/itwanger.png) 好了,这次资源分享就到这里!后续如果遇到有用的工具或者资源,依然还会持续分享,也欢迎大家多多安利和交流,一起分享成长。 diff --git a/docs/gongju/DBeaver.md b/docs/gongju/DBeaver.md index 8f2d3a756..8c0b0059e 100644 --- a/docs/gongju/DBeaver.md +++ b/docs/gongju/DBeaver.md @@ -21,11 +21,11 @@ DBeaver 是由 Java 编写的,默认使用 JDK 11 进行编译。社区版基 >https://github.com/dbeaver/dbeaver -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-1.png) DBeaver 支持几乎所有主流的数据库,包括关系型数据库和非关系数据库。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-2.png) ### 二、安装 DBeaver @@ -33,11 +33,11 @@ DBeaver 支持几乎所有主流的数据库,包括关系型数据库和非关 >官方下载地址:https://dbeaver.io/download/ -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-3.png) 根据自己电脑的操作系统下载对应的安装包,完整安装后,第一步要做的是配置 Maven 镜像,否则在后续下载数据库驱动的时候会非常的慢。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-4.png) 因为 DBeaver 是基于 [Maven 构建](https://github.com/itwanger/toBeBetterJavaer/blob/master/docs/maven/maven.md)的,数据库驱动也就是链接数据库的 JDBC 驱动是通过 Maven 仓库下载的。选择「首选项」→「Maven」,添加阿里云镜像地址: @@ -47,34 +47,34 @@ DBeaver 支持几乎所有主流的数据库,包括关系型数据库和非关 和配置 Maven 镜像一样,如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-5.png) 配置完成后,记得把阿里云镜像仓库置顶。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-6.png) ### 三、管理数据源 像使用 Navicat 一样,我们需要先建立连接,这里就以 MySQL 为例。点击「连接」小图标,选择数据库。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-7.png) 点击下一步,这时候需要填写数据库连接信息。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-8.png) 点击「测试链接」,如果使用默认的 Maven 仓库时,下载驱动会非常慢,如下图所示,还容易失败「踩过的坑就不要再踩了」。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-9.png) 如果你前面按照我说的配置了阿里云的 Maven 镜像,程序就不一样了,点了「测试链接」,瞬间会弹出「连接已成功」的提示框。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-10.png) 链接成功后,就可以看到数据库中的表啊、视图啊、索引啊等等。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-11.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-11.png) ### 四、管理表 @@ -84,81 +84,81 @@ DBeaver 支持几乎所有主流的数据库,包括关系型数据库和非关 选择一张表,双击后就可以看到表的属性了,可以查看表的列、约束(主键)、外键、索引等等信息。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-12.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-12.png) 点击「DDL(Data Definition Language,数据定义语言)」可以看到详细的建表语句。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-13.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-13.png) 点击「数据」可以查看表的数据,底部有「新增」、「修改」、「删除」等行操作按钮。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-14.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-14.png) 可以在顶部的过滤框中填写筛选条件,然后直接查询结果。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-15.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-15.png) 如果不想显示某一列的话,可以直接点击「自定义结果集」图表,将某个字段的状态设置为不可见即可。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-16.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-16.png) **02、新增表** 在左侧选择「表」,然后右键选择「新建表」即可建表id。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-17.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-17.png) 之后在右侧列的区域右键,选择「新建列」即可添加字段。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-18.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-18.png) 比如说我们新建一个主键 ID,如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-19.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-19.png) 在 DBeaver 中,`[v]` 表示真,`[]` 表示否。紧接着在「约束」里选择 ID 将其设置为主键。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-20.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-20.png) 最后点击保存,会弹出一个建表语句的预览框,点击「执行」即可完成表的创建。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-21.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-21.png) ### 五、执行 SQL 右键数据库表,选择右键菜单中的「SQL 编辑器」可以打开 SQL 编辑面板。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-22.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-22.png) 然后编辑 SQL 语句,点击运行的小图标就可以查询数据了。这个过程会有语法提示,非常 nice。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-23.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-23.png) DBeaver 有一个很亮眼的操作就是,可以直接选中一条结果集,然后右键生成 SQL。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-24.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-24.png) 比如说 insert 语句,这样再插入一条重复性内容的时候就非常方便了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-25.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-25.png) ### 六、外观配置 可以在首选项里对外观进行设置,比如说把主题修改为暗黑色。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-26.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-26.png) 然后界面就变成了暗黑系。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-27.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-27.png) 还可以设置字体大小等。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-28.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-28.png) 从整体的风格来看,DBeaver 和 Eclipse 有些类似,事实上也的确如此,DBeaver 是基于 Eclipse 平台构建的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/DBeaver-29.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/DBeaver-29.png) ### 七、总结 diff --git a/docs/gongju/chiner.md b/docs/gongju/chiner.md index 66490b829..9054a9292 100644 --- a/docs/gongju/chiner.md +++ b/docs/gongju/chiner.md @@ -14,7 +14,7 @@ tag: 今天我给大家推荐的这款国人开源的数据库设计工具 chiner,界面漂亮,功能强大,体验后给我的感觉是真香...... -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-1.png) ### 一、 关于 PowerDesigner @@ -23,7 +23,7 @@ PowerDesigner 是一款功能非常强大的建模工具,可以和 Rational Ro 不过,说句实在话,PowerDesigner 的界面偏古典一些,下面是我用 PowerDesigner 设计 DB 的效果。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-2.png) ### 二、关于 chiner @@ -37,7 +37,7 @@ chiner,发音:[kaɪˈnər],使用React+Electron+Java技术体系构建的 在此,我们必须得为每一位开源作者奉上最真诚的掌声,希望他们的产品都能有一番天地。也希望,未来我的产品出现在大家的面前时,能给它多一点点包容和支持。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-3.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-3.gif) ### 三、安装 chiner @@ -47,15 +47,15 @@ chiner 支持 Windows、macOS 和 Linux,下载地址如下所示: 码云做了外部链接的拦截,导致直接复制链接到地址栏才能完成下载。我这里以 macOS 为例。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-4.png) 安装完成后首次打开的样子是这样的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-5.png) chiner 提供了非常贴心的操作手册和参考模板,如果时间比较充分的话,可以先把操作手册过一遍,写得非常详细。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-6.png) ### 四、上手 chiner @@ -67,31 +67,31 @@ chiner 提供了非常贴心的操作手册和参考模板,如果时间比较 第二步,选择导入 PowerDesigner 文件。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-7.png) 第三步,选择要添加的数据表。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-8.png) 第四步,导入完成后,就可以点开单表进行查看了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-9.png) 第五步,当完成重新设计后,就可以选择导出 DDL 到数据库表了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-10.png) 当然了,也可以直接配置数据库 DB,这样就可以直接连接导入导出了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-11.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-11.png) 导出的 SQL 文件可以直接通过宝塔面板上传到服务器端,然后再直接导入到数据库。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-12.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-12.png) 如果需要用到数据库说明文档的话,也可以直接通过导出到 Word 文档来完成。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-13.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-13.png) **02、维护数据类型** @@ -99,41 +99,41 @@ chiner 自带了几种常见的数据类型,比如字串、小数、日期等 比如说默认的字串类型关联到其他数据库的类型如下所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-14.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-14.png) 数据域是在数据类型的基础上,基于当前项目定义的有一定业务含义的数据类型,比如说我这里维护了一个长度为 90 的名称数据域。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-15.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-15.png) 当我需要把某个数据字段的数据域设置成「名称」的时候,长度就会自动填充为 90,不需要手动再去设置。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-16.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-16.png) **03、维护数据表** 第一步,选中数据表,右键选择「新增数据表」 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-17.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-17.png) 第二步,填写数据表名 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-18.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-18.png) 点击「确定」后,chiner 会帮我们自动生成一些常见常用的字段,比如说创建人、创建时间、更新人、更新时间等,非常的智能化。通常来说,这些字段都是必须的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-19.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-19.png) 如果这些默认字段不满足需求的时候,还可以点击「设置」新增默认字段,比如说删除标记,一般来说为了安全起见,数据库都会采用非物理删除。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-20.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-20.png) 一般来说,我们更习惯字段小写命名,因此可以直接选中一列,然后选择大小写转换。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-21.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-21.png) 就变成小写了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-22.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-22.png) **04、维护关系图** @@ -141,11 +141,11 @@ chiner 自带了几种常见的数据类型,比如字串、小数、日期等 第二步,把需要关联的表拖拽到右侧的面板当中,然后按照字段进行连线,非常的方便。比如说班级和学院表、班级和专业表的关系,就如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-23.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-23.png) 来看一下整体给出来的关系图,还是非常清爽的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/chiner-24.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/chiner-24.png) ### 五、尾声 diff --git a/docs/gongju/fastjson.md b/docs/gongju/fastjson.md index 3fdfeea3f..e8fc1beda 100644 --- a/docs/gongju/fastjson.md +++ b/docs/gongju/fastjson.md @@ -12,7 +12,7 @@ tag: 我是 fastjson,是个地地道道的杭州土著,但我始终怀揣着一颗走向全世界的雄心。这不,我在 GitHub 上的简介都换成了英文,国际范十足吧? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/fastjson-0576767f-c447-49f1-83a3-6971782c4d52.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/fastjson-0576767f-c447-49f1-83a3-6971782c4d52.png) 如果你的英语功底没有我家老板 666 的话,我可以简单地翻译下(说人话,不装逼)。 @@ -233,7 +233,7 @@ public class IdentityHashMap { 再比如说,使用 asm 技术来避免反射导致的开销。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/fastjson-86a38cb0-3acc-4132-8e1f-48ebeaa52b47.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/fastjson-86a38cb0-3acc-4132-8e1f-48ebeaa52b47.png) 我承认,快的同时,也带来了一些安全性的问题。尤其是 AutoType 的引入,让黑客有了可乘之机。 @@ -257,14 +257,14 @@ public class IdentityHashMap { 在于黑客的反复较量中,我虽然变得越来越稳重成熟了,但与此同时,让我的用户为此也付出了沉重的代价。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/fastjson-6868c673-8799-4326-baab-1050a5a4e9a3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/fastjson-6868c673-8799-4326-baab-1050a5a4e9a3.png) 网络上也出现了很多不和谐的声音,他们声称我是最垃圾的国产开源软件之一,只不过凭借着一些投机取巧赢得了国内开发者的信赖。 但更多的是,对我的不离不弃。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/fastjson-85a44233-6eb2-4164-a091-6b65fc5f001a.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/fastjson-85a44233-6eb2-4164-a091-6b65fc5f001a.png) 最令我感到为之动容的一句话是: @@ -274,7 +274,7 @@ public class IdentityHashMap { 为了彻底解决 AutoType 带来的问题,在 1.2.68 版本中,我引入了 safeMode 的安全模式,无论白名单和黑名单,都不支持 AutoType,这样就可以彻底地杜绝攻击。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/fastjson-57146979-cb99-4236-94f9-1cd5276e8269.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/fastjson-57146979-cb99-4236-94f9-1cd5276e8269.png) 安全模式下,`checkAutoType()` 方法会直接抛出异常。 diff --git a/docs/gongju/forest.md b/docs/gongju/forest.md index 46388d38a..1b0e3d482 100644 --- a/docs/gongju/forest.md +++ b/docs/gongju/forest.md @@ -143,11 +143,11 @@ Forest 的字面意思是森林的意思,更内涵点的话,可以拆成For **虽然 star 数还不是很多,但 star 趋势图正在趋于爬坡阶段,大家可以拿来作为一个练手项目,我觉得还是不错的选择**。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/forest-55b54f3f-88a7-458b-b8e0-b0d60e916d5e.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/forest-55b54f3f-88a7-458b-b8e0-b0d60e916d5e.png) Forest 本身是处理前端过程的框架,是对后端 HTTP API 框架的进一步封装。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/forest-41345eec-fe16-4fcf-9448-0cb8c57d515f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/forest-41345eec-fe16-4fcf-9448-0cb8c57d515f.png) 前端部分: diff --git a/docs/gongju/gson.md b/docs/gongju/gson.md index ca9244ac5..37b1efe5f 100644 --- a/docs/gongju/gson.md +++ b/docs/gongju/gson.md @@ -243,7 +243,7 @@ class Bar{ 假如你 debug 的时候,进入到 `toJson()` 方法的内部,就可以观察到。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/gson-402ff6b5-a460-45de-ab62-ede6fbf6b61e.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/gson-402ff6b5-a460-45de-ab62-ede6fbf6b61e.png) foo 的实际类型为 `Foo`,但我女朋友在调用 `foo.getClass()` 的时候,只会得到 Foo,这就意味着她并不知道 foo 的实际类型。 @@ -283,11 +283,11 @@ Bar bar1 = foo1.get(); debug 进入 `toJson()` 方法内部查看的话,就可以看到 foo 的真实类型了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/gson-1c6eac43-6f0b-4a00-ae6c-2db29f911719.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/gson-1c6eac43-6f0b-4a00-ae6c-2db29f911719.png) `fromJson()` 在反序列化的时候,和此类似。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/gson-5afe5cd1-4966-4b16-adcb-fc04edfff406.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/gson-5afe5cd1-4966-4b16-adcb-fc04edfff406.png) 这样的话,bar1 就可以通过 `foo1.get()` 到了。 diff --git a/docs/gongju/jackson.md b/docs/gongju/jackson.md index 39d68e75a..60653e537 100644 --- a/docs/gongju/jackson.md +++ b/docs/gongju/jackson.md @@ -19,7 +19,7 @@ Java 之所以牛逼,很大的功劳在于它的生态非常完备,JDK 没 当我们通过 starter 新建一个 Spring Boot 的 Web 项目后,就可以在 Maven 的依赖项中看到 Jackson 的身影。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/jackson-4340975c-e254-4287-88e0-66f73fe88889.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/jackson-4340975c-e254-4287-88e0-66f73fe88889.png) Jackson 有很多优点: @@ -47,7 +47,7 @@ Jackson 的核心模块由三部分组成: jackson-databind 依赖于 jackson-core 和 jackson-annotations,所以添加完 jackson-databind 之后,Maven 会自动将 jackson-core 和 jackson-annotations 引入到项目当中。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/jackson-24990211-7a18-44d7-aff0-6ac9e3cf0561.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/jackson-24990211-7a18-44d7-aff0-6ac9e3cf0561.png) Maven 之所以讨人喜欢的一点就在这,能偷偷摸摸地帮我们把该做的做了。 diff --git a/docs/gongju/junit.md b/docs/gongju/junit.md index 657d080a8..a0188713c 100644 --- a/docs/gongju/junit.md +++ b/docs/gongju/junit.md @@ -16,7 +16,7 @@ tag: 微软公司之前有这样一个统计:bug 在单元测试阶段被发现的平均耗时是 3.25 小时,如果遗漏到系统测试则需要 11.5 个小时。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/junit-5b0afb32-c60e-4218-98b1-44288705e472.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/junit-5b0afb32-c60e-4218-98b1-44288705e472.png) 经我这么一说,你应该已经很清楚单元测试的重要性了。那在你最初编写测试代码的时候,是不是经常这么做?就像下面这样。 @@ -56,17 +56,17 @@ public class Factorial { 第一步,直接在当前的代码编辑器窗口中按下 `Command+N` 键(Mac 版),在弹出的菜单中选择「Test...」。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/junit-fe29e8b8-9264-4aa3-9139-6ebb39af88a1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/junit-fe29e8b8-9264-4aa3-9139-6ebb39af88a1.png) 勾选上要编写测试用例的方法 `fact()`,然后点击「OK」。 此时,IDEA 会自动在当前类所在的包下生成一个类名带 Test(惯例)的测试类。如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/junit-756305d6-7166-4737-8665-89d24a1eefae.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/junit-756305d6-7166-4737-8665-89d24a1eefae.png) 如果你是第一次使用我的话,IDEA 会提示你导入我的依赖包。建议你选择最新的 JUnit 5.4。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/junit-91bf986d-3586-4175-9ca2-959e5eb62e9c.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/junit-91bf986d-3586-4175-9ca2-959e5eb62e9c.png) 导入完毕后,你可以打开 pom.xml 文件确认一下,里面多了对我的依赖。 @@ -95,11 +95,11 @@ void fact() { 第三步,你可以在邮件菜单中选择「Run FactorialTest」来运行测试用例,结果如下所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/junit-7daf1a3d-a321-4d42-9d16-134043161a29.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/junit-7daf1a3d-a321-4d42-9d16-134043161a29.png) 测试失败了,因为第 20 行的预期结果和实际不符,预期是 100,实际是 120。此时,你要么修正实现代码,要么修正测试代码,直到测试通过为止。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/junit-5b71c36f-684d-4d30-b1a1-faef453603ae.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/junit-5b71c36f-684d-4d30-b1a1-faef453603ae.png) 不难吧?单元测试可以确保单个方法按照正确的预期运行,如果你修改了某个方法的代码,只需确保其对应的单元测试通过,即可认为改动是没有问题的。 @@ -124,7 +124,7 @@ public class Calculator { 新建测试用例的时候记得勾选`setUp` 和 `tearDown`。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/junit-afa3c969-a2d2-439c-b440-5b7480592d52.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/junit-afa3c969-a2d2-439c-b440-5b7480592d52.png) 生成后的代码如下所示。 diff --git a/docs/gongju/knife4j.md b/docs/gongju/knife4j.md index 8df22ff80..780ab2fb4 100644 --- a/docs/gongju/knife4j.md +++ b/docs/gongju/knife4j.md @@ -20,15 +20,15 @@ Knife4j 的前身是 swagger-bootstrap-ui,是 springfox-swagger-ui 的增强 U springfox-swagger-ui 的界面长这个样子,说实话,确实略显丑陋。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/knife4j-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/knife4j-1.png) swagger-bootstrap-ui 增强后的样子长下面这样。单纯从直观体验上来看,确实增强了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/knife4j-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/knife4j-2.png) 改良后的 Knife4j 不仅在界面上更加优雅、炫酷,功能上也更加强大:后端 Java 代码和前端 UI 模块分离了出来,在微服务场景下更加灵活;更提供了专注于 Swagger 的增强解决方案。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/knife4j-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/knife4j-3.png) 官方文档: @@ -91,7 +91,7 @@ public class SwaggerConfig { 在项目路径后面添加上 `swagger-ui` 就可以了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/knife4j-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/knife4j-4.png) 在 Controller 类中,可以看到常见的 Swagger 注解 @Api 和 @ApiOperation: @@ -142,7 +142,7 @@ public class SwaggerConfig {} >访问地址:http://localhost:9002/doc.html -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/knife4j-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/knife4j-5.png) 如果项目中加了权限认证的话,记得给 Knife4j 添加白名单。我的项目用的是 SpringSecurity,所以需要在 application.yml 文件中添加。 @@ -163,47 +163,47 @@ secure: Knife4j 和 Swagger 一样,也是支持头部登录认证的,点击「authorize」菜单,添加登录后的信息即可保持登录认证的 token。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/knife4j-6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/knife4j-6.png) 如果某个 API 需要登录认证的话,就会把之前填写的信息带过来。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/knife4j-7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/knife4j-7.png) **2)支持 JSON 折叠** Swagger 是不支持 JSON 折叠的,当返回的信息非常多的时候,界面就会显得非常的臃肿。Knife4j 则不同,可以对返回的 JSON 节点进行折叠。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/knife4j-8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/knife4j-8.png) **3)离线文档** Knife4j 支持把 API 文档导出为离线文档(支持 markdown 格式、HTML 格式、Word 格式), -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/knife4j-9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/knife4j-9.png) 使用 Typora 打开后的样子如下,非常的大方美观。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/knife4j-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/knife4j-10.png) **4)全局参数** 当某些请求需要全局参数时,这个功能就很实用了,Knife4j 支持 header 和 query 两种方式。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/knife4j-11.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/knife4j-11.png) 之后进行请求的时候,就会把这个全局参数带过去。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/knife4j-12.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/knife4j-12.png) **5)搜索 API 接口** Swagger 是没有搜索功能的,当要测试的接口有很多的时候,当需要去找某一个 API 的时候就傻眼了,只能一个个去拖动滚动条去找。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/knife4j-13.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/knife4j-13.png) 在文档的右上角,Knife4j 提供了文档搜索功能,输入要查询的关键字,就可以检索筛选了,是不是很方便? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/knife4j-14.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/knife4j-14.png) 目前支持搜索接口的地址、名称和描述。 @@ -213,7 +213,7 @@ Swagger 是没有搜索功能的,当要测试的接口有很多的时候,当 >https://doc.xiaominfo.com/knife4j/documentation/enhance.html -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/knife4j-15.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/knife4j-15.png) 如果项目中之前使用过 Swagger 生成接口文档,切换到 Knife4j 可以说是非常的丝滑,只需要两步: diff --git a/docs/gongju/log4j.md b/docs/gongju/log4j.md index 0bff7b2d2..40887e731 100644 --- a/docs/gongju/log4j.md +++ b/docs/gongju/log4j.md @@ -12,7 +12,7 @@ tag: 这不,我在战国时代读者群里发现了这么一串聊天记录: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/log4j-cacd9e88-4a4d-4127-a18b-f99b2e2296a3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/log4j-cacd9e88-4a4d-4127-a18b-f99b2e2296a3.png) 竟然有小伙伴不知道“打日志”是什么意思,不知道该怎么学习,还有小伙伴回答说,只知道 Log4j! @@ -22,7 +22,7 @@ tag: (说好的不在乎,怎么在乎起来了呢?手动狗头) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/log4j-58282c4d-8178-45bd-8ba3-26740f6dd4a3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/log4j-58282c4d-8178-45bd-8ba3-26740f6dd4a3.png) 管他呢,**我行我素**吧,保持初心不改就对了!这篇文章就来说说 Log4j,这个打印日志的鼻祖。Java 中的日志打印其实是个艺术活,我保证,这句话绝不是忽悠。 @@ -37,7 +37,7 @@ tag: 之所以这样打印日志,是因为很方便,上手难度很低,尤其是在 IDEA 的帮助下,只需在键盘上按下 `so` 两个字母就可以调出 `System.out.println()`。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/log4j-64dbb12b-8f6b-4ee3-ab5a-60519dd9112f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/log4j-64dbb12b-8f6b-4ee3-ab5a-60519dd9112f.png) 在本地环境下,使用 `System.out.println()` 打印日志是没问题的,可以在控制台看到信息。但如果是在生产环境下的话,`System.out.println()` 就变得毫无用处了。 @@ -63,7 +63,7 @@ OFF,最高级别,意味着所有消息都不会输出了。 这个级别是基于 Log4j 的,和 java.util.logging 有所不同,后者提供了更多的日志级别,比如说 SEVERE、FINER、FINEST。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/log4j-4919cd20-e524-43a2-8b41-9eab6ac0c1e4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/log4j-4919cd20-e524-43a2-8b41-9eab6ac0c1e4.png) ### 03、错误的日志记录方式是如何影响性能的 @@ -86,7 +86,7 @@ if(logger.isDebugEnabled()){ 切记,在生产环境下,一定不要开启 DEBUG 级别的日志,否则程序在大量记录日志的时候会变很慢,还有可能在你不注意的情况下,悄悄地把磁盘空间撑爆。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/log4j-fd2149c5-2d0c-4c15-897d-d5fa06cce71f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/log4j-fd2149c5-2d0c-4c15-897d-d5fa06cce71f.png) ### 04、为什么选择 Log4j 而不是 java.util.logging @@ -123,7 +123,7 @@ public class JavaUtilLoggingDemo { 程序运行后会在 target 目录下生成一个名叫 javautillog.txt 的文件,内容如下所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/log4j-222181d4-04ba-4487-8386-69b8737d2d5c.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/log4j-222181d4-04ba-4487-8386-69b8737d2d5c.png) 再来看一下 Log4j 的使用方式。 @@ -349,7 +349,7 @@ if(logger.isDebugEnabled()) { 8)不要在日志文件中打印密码、银行账号等敏感信息。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/log4j-42d3a052-daeb-450a-a775-a32f983dd688.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/log4j-42d3a052-daeb-450a-a775-a32f983dd688.png) ### 06、 总结 diff --git a/docs/gongju/log4j2.md b/docs/gongju/log4j2.md index 0f26d9881..b78c10290 100644 --- a/docs/gongju/log4j2.md +++ b/docs/gongju/log4j2.md @@ -19,7 +19,7 @@ SLF4J 和 Logback 作为 Log4j 的替代品,在很多方面都做了必要的 上一篇也说了,老板下死命令要我把日志系统切换到 Logback,我顺利交差了,老板很开心,夸我这个打工人很敬业。为了表达对老板的这份感谢,我决定偷偷摸摸地试水一下 Log4j 2,尽管它还不是个成品,可能会会项目带来一定的隐患。但谁让咱是一个敬岗爱业的打工人呢。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/log4j2-a9461265-7652-4512-9219-6b3e82392415.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/log4j2-a9461265-7652-4512-9219-6b3e82392415.png) ### 01、Log4j 2 强在哪 @@ -30,7 +30,7 @@ Log4j 2 的异步 Logger 使用的是无锁数据结构,而 Logback 和 Log4j 下图说明了多线程方案中无锁数据结构对吞吐量的影响。 Log4j 2 随着线程数量的扩展而更好地扩展:具有更多线程的应用程序可以记录更多的日志。其他日志记录库由于存在锁竞争的关系,在记录更多线程时,总吞吐量保持恒定或下降。这意味着使用其他日志记录库,每个单独的线程将能够减少日志记录。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/log4j2-43f0b03d-5c4a-4af3-9e4c-177956246740.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/log4j2-43f0b03d-5c4a-4af3-9e4c-177956246740.png) 性能方面是 Log4j 2 的最大亮点,至于其他方面的一些优势,比如说下面这些,可以忽略不计,文字有多短就代表它有多不重要。 @@ -91,7 +91,7 @@ Log4j 2 竟然没有在控制台打印“ log4j2”,还抱怨我们没有为 可以在方法中打个断点,然后 debug 一下,你就会看到下图中的内容。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/log4j2-4ba440d9-c0b6-4ad2-b538-9d303cc99d90.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/log4j2-4ba440d9-c0b6-4ad2-b538-9d303cc99d90.png) 通过源码,你可以看得到,Log4j 2 会去寻找 4 种类型的配置文件,后缀分别是 properties、yaml、json 和 xml。前缀是 log4j2-test 或者 log4j2。 @@ -264,7 +264,7 @@ for (int i = 1;i < 20; i++) { 再次运行 Demo 类,可以看到根目录下多了 3 个日志文件: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/log4j2-07af98ca-cf94-427e-adb6-bd935e32a8d0.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/log4j2-07af98ca-cf94-427e-adb6-bd935e32a8d0.png) 结合日志文件名,再来看 RollingFile 的配置,就很容易理解了。 @@ -277,7 +277,7 @@ for (int i = 1;i < 20; i++) { 先来看一下 DefaultRolloverStrategy 的属性: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/log4j2-32b853ce-8beb-496b-b66f-31b650c257ab.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/log4j2-32b853ce-8beb-496b-b66f-31b650c257ab.png) 再来看 filePattern 的值 `rolling-%d{yyyy-MM-dd}-%i.log`,其中 `%d{yyyy-MM-dd}` 很好理解,就是年月日;其中 `%i` 是什么意思呢? @@ -309,7 +309,7 @@ for (int i = 1;i < 20; i++) { 运行 Demo 后,可以在 gz 目录下看到以下文件: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/log4j2-1b04167d-a11f-4447-9062-cb3cdd59aa73.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/log4j2-1b04167d-a11f-4447-9062-cb3cdd59aa73.png) 到此为止,Log4j 2 的基本使用示例就已经完成了。测试环境搞定,我去问一下老板,要不要在生产环境下使用 Log4j 2。 diff --git a/docs/gongju/logback.md b/docs/gongju/logback.md index 80bdb1797..ffc44728e 100644 --- a/docs/gongju/logback.md +++ b/docs/gongju/logback.md @@ -14,25 +14,25 @@ tag: 我们项目之前用的 Log4j,在我看来,已经足够用了,毕竟是小公司,性能上的要求没那么苛刻。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/logback-320329e9-a754-427f-8a19-2e4f809b6a6f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/logback-320329e9-a754-427f-8a19-2e4f809b6a6f.png) ### 01、Logback 强在哪 1)非常自然地实现了 SLF4J,不需要像 Log4j 和 JUL 那样加一个适配层。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/logback-6ba1b465-5533-49dd-b875-48a10ba29f8e.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/logback-6ba1b465-5533-49dd-b875-48a10ba29f8e.png) 2)Spring Boot 的默认日志框架使用的是 Logback。一旦某款工具库成为了默认选项,那就说明这款工具已经超过了其他竞品。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/logback-2696cd8b-7e8c-4476-9a06-272fd22fa4b6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/logback-2696cd8b-7e8c-4476-9a06-272fd22fa4b6.png) 注意看下图(证据找到了,来自 [Spring Boot 官网](https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-logging)): -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/logback-82d78a15-8ae0-4377-a7af-aebd5cda4fda.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/logback-82d78a15-8ae0-4377-a7af-aebd5cda4fda.png) 也可以通过源码的形式看得到: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/logback-2df2d06e-1b01-428b-8444-d765056e25bb.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/logback-2df2d06e-1b01-428b-8444-d765056e25bb.png) 3)支持自动重新加载配置文件,不需要另外创建扫描线程来监视。 @@ -53,7 +53,7 @@ tag: Maven 会自动导入另外两个依赖: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/logback-1f7d8e00-4be6-4863-940c-037862ad2c41.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/logback-1f7d8e00-4be6-4863-940c-037862ad2c41.png) logback-core 是 Logback 的核心,logback-classic 是 SLF4J 的实现。 @@ -121,7 +121,7 @@ StatusPrinter.print(lc); Logback 的配置文件非常灵活,最基本的结构为 `` 元素,包含 0 或多个 `` 元素,其后跟 0 或多个 `` 元素,其后再跟最多只能存在一个的 `` 元素。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/logback-b81ab795-2a2c-44c3-a4b8-b96ef78dcd88.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/logback-b81ab795-2a2c-44c3-a4b8-b96ef78dcd88.png) **1)配置 appender**,也就是配置日志的输出目的地,通过 name 属性指定名字,通过 class 属性指定目的地: @@ -142,7 +142,7 @@ pattern 用来指定日志的输出格式: 反例(没有指定 -5 的情况): -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/logback-b30bc0ca-5c78-4853-922b-36bb0c7d8628.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/logback-b30bc0ca-5c78-4853-922b-36bb0c7d8628.png) - `%logger{length}`:logger 的名称,length 用来缩短名称。没有指定表示完整输出;0 表示只输出 logger 最右边点号之后的字符串;其他数字表示输出小数点最后边点号之前的字符数量。 @@ -233,7 +233,7 @@ log4j.appender.E.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - 粘贴到该网址的文本域: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/logback-6c934584-3624-4f40-8108-13bfffc0c40b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/logback-6c934584-3624-4f40-8108-13bfffc0c40b.png) 点击「Translate」,可以得到以下内容: @@ -290,7 +290,7 @@ log4j.appender.E.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - 可以确认一下内容,发现三个 appender 都在。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/logback-7a0edcdf-8706-4a83-9c09-413fc07967ad.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/logback-7a0edcdf-8706-4a83-9c09-413fc07967ad.png) 但是呢,转换后的文件并不能直接使用,需要稍微做一些调整,因为: @@ -394,7 +394,7 @@ public class Test { 运行后,可以在 target 目录下看到两个文件:debug.log 和 errror.log。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/logback-536aa50e-b195-403e-8409-85e4f6966522.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/logback-536aa50e-b195-403e-8409-85e4f6966522.png) 到此为止,项目已经从 Log4j 切换到 Logback 了,过程非常的丝滑顺畅,嘿嘿。 diff --git a/docs/gongju/slf4j.md b/docs/gongju/slf4j.md index c410553ff..4db79942e 100644 --- a/docs/gongju/slf4j.md +++ b/docs/gongju/slf4j.md @@ -13,11 +13,11 @@ tag: (为什么我把这段文字手敲了下来呢,因为我发现阿里巴巴开发手册上的有语病,瞧下面红色标出的部分) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/slf4j-94ba034a-c6e6-46e0-bff3-b658bf35945f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/slf4j-94ba034a-c6e6-46e0-bff3-b658bf35945f.png) (维护和统一,把统一放在最后面读起来真的是别扭,和的有点牵强,请问手册的小编是数学老师教的语文吧?) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/slf4j-a7a6e0ae-cbee-428e-8a45-2f5a33243625.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/slf4j-a7a6e0ae-cbee-428e-8a45-2f5a33243625.png) 那看到这条强制性的规约,我就忍不住想要问:“为什么阿里巴巴开发手册会强制使用 SLF4J 作为 Log4J 的门面担当呢?”究竟这背后藏了什么“不可告人”的秘密? @@ -33,11 +33,11 @@ SLF4J 是 Simple Logging Facade for Java 的缩写(for≈4),也就是简 SLF4J 的作者就是 Log4J 和 Logback 的作者,他的 GitHub 主页长下面这样: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/slf4j-c72cd63d-b15b-401c-8399-ad0355f1f802.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/slf4j-c72cd63d-b15b-401c-8399-ad0355f1f802.png) 一股秋风瑟瑟的清冷感扑面而来,有没有?可能巨佬不屑于维护他的 GitHub 主页吧?我的 GitHub 主页够凄惨了,没想到巨佬比我还惨,终于可以吹牛逼地说,“我,沉默王二,GitHub 主页比 SLF4J、Log4J 和 Logback 的作者 Ceki Gulcu 绿多了。。。。。。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/slf4j-cdc9e0fb-71ab-42e7-8024-7e9cfd9b30c3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/slf4j-cdc9e0fb-71ab-42e7-8024-7e9cfd9b30c3.png) 1996 年初,欧洲安全电子市场项目决定编写自己的跟踪 API,最后该 API 演变成了 Log4j,已经推出就备受宠爱。 @@ -45,17 +45,17 @@ SLF4J 的作者就是 Log4J 和 Logback 的作者,他的 GitHub 主页长下 2002 年 8 月,Apache 就推出了自己的日志包,也就是阿里巴巴开发手册上提到的 JCL(Jakarta Commons Logging)。JCL 的野心很大,它在 JUL 和 Log4j 的基础上提供了一个抽象层的接口,方便使用者在 JUL 和 Log4j 之间切换。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/slf4j-5b40fac4-0ab1-467d-9dc1-85c43ed879e7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/slf4j-5b40fac4-0ab1-467d-9dc1-85c43ed879e7.png) 但 JCL 好像并不怎么招人喜欢,有人是这样抱怨的: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/slf4j-795d5543-7bd1-450a-8a35-a151be106b3b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/slf4j-795d5543-7bd1-450a-8a35-a151be106b3b.png) Ceki Gulcu 也觉得 JCL 不好,要不然他也不会在 2005 年自己撸一个名叫 SLF4J 的新项目,对吧?但出来混总是要付出代价的,SLF4J 只有接口,没有实现,总不能强逼着 Java 和 Apache 去实现 SLF4J 接口吧?这太难了,不现实。 但巨佬之所以称之为巨佬,是因为他拥有超出普通人的惊人之处,他在 SLF4J 和 JUL、Log4j、JCL 之间搭了三座桥: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/slf4j-3044b416-ff14-408f-b933-71993b7ddeee.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/slf4j-3044b416-ff14-408f-b933-71993b7ddeee.png) 巨佬动手,丰衣足食,有没有?狠起来连自己的 Log4j 都搭个桥。 @@ -71,13 +71,13 @@ Ceki Gulcu 也觉得 JCL 不好,要不然他也不会在 2005 年自己撸一 假设我们正在开发一套系统,打算用 SLF4J 作为门面,Log4j 作为日志系统,我们在项目中使用了 A 框架,而 A 框架的门面是 JCL,日志系统是 JUL,那就相等于要维护两套日志系统,对吧? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/slf4j-e66a78a6-1ef6-42c1-86fa-a4c57b3ef160.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/slf4j-e66a78a6-1ef6-42c1-86fa-a4c57b3ef160.png) 这就难受了! Ceki Gulcu 想到了这个问题,并且帮我们解决了!来看 SLF4J 官网给出的解决方案。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/slf4j-5e1021d6-6a81-492b-b8d3-f9438014b53b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/slf4j-5e1021d6-6a81-492b-b8d3-f9438014b53b.png) - 使用 jcl-over-slf4j.jar 替换 commons-logging.jar - 引入 jul-to-slf4j.jar @@ -117,7 +117,7 @@ public class Demo { 调试这段代码的过程中你会发现,Log 的实现有四种: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/slf4j-a1db024d-1b29-47b2-a1f8-70b899d5b7c0.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/slf4j-a1db024d-1b29-47b2-a1f8-70b899d5b7c0.png) 如果没有绑定 Log4j 的话,就会默认选择 Jdk14Logger——它返回的 Logger 对象,正是 java.util.logging.Logger,也就是 JUL。 @@ -198,15 +198,15 @@ private static Log logger = LogFactory.getLog(Demo.class); SLF4J 除了提供这种解决方案,绑定 Log4j 替换 JUL 和 JCL;还提供了绑定 Logback 替换 JUL、JCL、Log4j 的方案: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/slf4j-d3162919-47e1-4760-beba-7b77cdf42e71.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/slf4j-d3162919-47e1-4760-beba-7b77cdf42e71.png) 还有绑定 JUL 替换 JCL 和 Log4j 的方案: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/slf4j-a7d80721-ec0f-4b99-a59b-15f8344c3819.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/slf4j-a7d80721-ec0f-4b99-a59b-15f8344c3819.png) 太强了,有木有?有的话请在留言区敲出 666。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/slf4j-e9233a42-d13e-4d7d-9d9e-b049d08303aa.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/slf4j-e9233a42-d13e-4d7d-9d9e-b049d08303aa.png) ### 03、SLF4J 比 Log4J 强在哪 @@ -283,7 +283,7 @@ public class Log4jSLF4JDemo { 看到了吧,使用占位符要比“+”操作符方便的多。并且此时不再需要 `isDebugEnabled()` 先进行判断,`debug()` 方法会在字符串拼接之前执行。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/slf4j-5e831353-b2a3-4a39-80e3-47a044009d95.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/slf4j-5e831353-b2a3-4a39-80e3-47a044009d95.png) 如果只是 Log4J 的话,会先进行字符串拼接,再执行 `debug()` 方法,来看示例代码: @@ -296,13 +296,13 @@ logger.debug(name + ",年纪:" + age + ",是个非常不要脸的程序员 在调试这段代码的时候,你会发现的,如下图所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/slf4j-4ae3eda7-cd0d-4094-9331-d6070a39c8ea.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/slf4j-4ae3eda7-cd0d-4094-9331-d6070a39c8ea.png) 这也就意味着,如果日志系统的级别不是 DEBUG,就会多执行了字符串拼接的操作,白白浪费了性能。 注意,阿里巴巴开发手册上还有一条「**强制**」级别的规约: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/slf4j-51004734-620a-4c1a-8aa7-6d7f3e1781d6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/slf4j-51004734-620a-4c1a-8aa7-6d7f3e1781d6.png) 这是因为如果参数是基本数据类型的话,会先进行自动装箱(`Integer.valueOf()`)。测试代码如下所示: @@ -318,7 +318,7 @@ logger.debug("\u6C89\u9ED8\u738B\u4E8C\uFF0C{}\u5C81", Integer.valueOf(18)); 如果参数需要调用其他方法的话,`debug()` 方法会随后调用。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/slf4j-aec16f40-7849-4a1d-9507-9119707e6c79.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/slf4j-aec16f40-7849-4a1d-9507-9119707e6c79.png) 也就是说,如果不 `isDebugEnabled()` 的话,在不是 DEBUG 级别的情况下,会多执行自动装箱和调用其他方法的操作——程序的性能就下降了! diff --git a/docs/gongju/tabby.md b/docs/gongju/tabby.md index 6f2c55a00..023ed67da 100644 --- a/docs/gongju/tabby.md +++ b/docs/gongju/tabby.md @@ -23,7 +23,7 @@ tag: 答案是有的,它就是 **Tabby**! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/tabby-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/tabby-01.png) GitHub 上已经有 21.4k 的 star 了,这说明 Tabby 非常的受欢迎: @@ -37,7 +37,7 @@ Tabby 是一个高度可定制化的 跨平台的终端工具,支持 Windows 直接到官网 [tabby.sh](tabby.sh) 点击「download」按钮就可以跳转到下载页面,最新的 release 版本是 1.0.164。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/tabby-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/tabby-02.png) Linux 和 Windows 的比较好选,macOS 分为两个版本,一个是 arm64,一个是 x86-64,什么意思呢? @@ -57,7 +57,7 @@ Apple M1 是苹果公司的第一款基于ARM架构的自研处理器单片系 按照提示,一步步安装就 OK 了。完成后打开,这界面还是非常炫酷的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/tabby-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/tabby-03.png) ## 二、SSH 连接 @@ -69,49 +69,49 @@ SSH,也就是 Secure Shell(安全外壳协议),是一种加密的网络 点击「setting」→「profiles & connections」→「new profile」。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/tabby-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/tabby-04.png) 填写服务器的 IP 地址和密码,然后点击「save」。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/tabby-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/tabby-05.png) 之后点击「运行」按钮,就可以进入到终端页面了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/tabby-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/tabby-06.png) 好了,现在可以对服务器进行操作了,执行下 top 命令可以查看服务器上正在运行的进程信息。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/tabby-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/tabby-07.png) ### 三、SFTP 传输文件 Tabby 集成了 SFTP,所以上传下载文件就变得非常的简单。只需要点击一下「SFTP」图标就可以打开文件传输窗口。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/tabby-08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/tabby-08.png) 上传的时候支持拖拽,完成后会弹出文件传输成功的提示消息。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/tabby-09.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/tabby-09.png) 下载的时候点击要下载的文件,然后会弹出存储对话框,选择对应的文件夹,以及修改对应的文件名点击「存储」就可以了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/tabby-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/tabby-10.png) ### 四、配置 Tabby 「Settings」 的面板下有一个「Appearance」的菜单,可以对 Tabby 的外观进行设置,比如说调整字体,比如说自定义样式。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/tabby-11.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/tabby-11.png) 「Appearance」的菜单可以对 Tabby 的配色方案进行修改,里面的主题非常多,不过我感觉默认的就挺不错,毕竟是官方推荐的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/tabby-12.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/tabby-12.png)  「Plugins」 菜单中还有不少插件可供扩展。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/tabby-13.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/tabby-13.png) * [clickable-links](https://github.com/Eugeny/tabby-clickable-links) - 使终端中的路径和 URL 可点击 * [docker](https://github.com/Eugeny/tabby-docker) - 连接到 Docker 容器 @@ -121,25 +121,25 @@ Tabby 集成了 SFTP,所以上传下载文件就变得非常的简单。只需 这里重点说一下「sync config」 这个插件,可以将配置同步到Github或者Gitee的插件。点击「Get」就可以安装,之后会提示你重启生效。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/tabby-14.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/tabby-14.png) 生效后点击「Sync Config」菜单,就可以看到配置项了,类型可以选择 GitHub、Gitee、GitLab。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/tabby-15.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/tabby-15.png) 这里以 Gitee 为例,进入个人 Gitee 主页,左侧菜单中选择「私人令牌」,然后点击「生成新令牌」。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/tabby-16.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/tabby-16.png) 提交后会生成 token,复制到 Tabby 的 Token 输入框中,然后点击「Upload config」,就可以看到配置信息同步成功了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/tabby-17.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/tabby-17.png)  「Window」 菜单中可以对当前窗口进行设置,比如说改变窗口的主题为 Paper,改变 tab 的位置到底部等等。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/tabby-18.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/tabby-18.png) ### 五、总结 diff --git a/docs/gongju/warp.md b/docs/gongju/warp.md index 675337617..cd155c0b4 100644 --- a/docs/gongju/warp.md +++ b/docs/gongju/warp.md @@ -21,7 +21,7 @@ tag: >还记得之前给大家推荐的 [Tabby](https://mp.weixin.qq.com/s/HeUAPe4LqqjfzIeWDe8KIg) 吗?是时候喜新厌旧了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/warp-e0411889-e506-480f-a719-eba4f2d229b4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/warp-e0411889-e506-480f-a719-eba4f2d229b4.png) Warp,一个超级牛叉的 terminal,号称是 21 世纪的终端,还未正式发布,就获得了两千三百万美元的融资。 @@ -29,7 +29,7 @@ Warp,一个超级牛叉的 terminal,号称是 21 世纪的终端,还未正 Warp 在 GitHub 上也已经开源,目前已经有 2.8k+ 的 star 了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/warp-17a2270c-3bd1-47eb-a205-b7defde42895.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/warp-17a2270c-3bd1-47eb-a205-b7defde42895.png) >GitHub 地址:[https://github.com/warpdotdev/Warp](https://github.com/warpdotdev/Warp) @@ -42,7 +42,7 @@ Warp 号称自己“Reinvent the Terminal”,也就是重新定义了终端, 直接到官网 `warp.dev` 点击「download now」就可以下载最新版了。下载完成后,双击安装包就可以安装了。完成后打开,界面还是非常清爽的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/warp-188834c7-70b7-4f9c-a817-b4a691625fd1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/warp-188834c7-70b7-4f9c-a817-b4a691625fd1.png) Warp 支持 GitHub 账户登录。不过,如果你在登录的过程中因为某些原因无法完成跳转,可以通过下面的链接自行解决。 @@ -50,19 +50,19 @@ Warp 支持 GitHub 账户登录。不过,如果你在登录的过程中因为 如果顺利登录,会跳转到这个页面。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/warp-84c0b513-57f3-4ab4-8c77-508c10c923c5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/warp-84c0b513-57f3-4ab4-8c77-508c10c923c5.png) 填写一些 Warp 的调查信息后,就会跳转到 Warp 的初始界面。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/warp-304639e9-7554-45b4-a199-e7c0c3b40c33.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/warp-304639e9-7554-45b4-a199-e7c0c3b40c33.png) >需要注意的是,Warp 目前仅支持 macOS 版,Linux 和 Windows 用户还需要等待一段时间。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/warp-a4d43a50-0ad9-4f91-ad4e-c1c0788bb580.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/warp-a4d43a50-0ad9-4f91-ad4e-c1c0788bb580.png) 其实 macOS 版也是刚刚公测,我这份攻略绝壁是热乎乎的。想要第一时间关注 Warp 版本信息的话,可以戳下图中提到的链接填写自己的邮箱。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/warp-f622d505-b136-4b9d-95c5-a6872e1423e1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/warp-f622d505-b136-4b9d-95c5-a6872e1423e1.png) ### 二、使用 Warp @@ -73,17 +73,17 @@ Warp 解决的第一个痛点,就是减少配置、方便输入、优化输出 普通的终端在你键入 tab 的时候,是这样提示的,就是简单地帮你罗列下。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/warp-078017c6-a872-466a-8aa2-f202c9371493.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/warp-078017c6-a872-466a-8aa2-f202c9371493.png) 而 Warp 就非常的时髦,会给你滚动可选的列表形式展示出来。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/warp-4e205289-d8c4-49a9-90ba-08aef8beb627.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/warp-4e205289-d8c4-49a9-90ba-08aef8beb627.png) Warp 的智能提示也更加“智能化”,它会猜测你下一步的命令到底输入什么。 比如说我的工作目录下有一个 README.md 的文件,那当我输入 `echo '沉默王二' >>`的时候它会把 `README.md` 提示在后面。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/warp-8948ab59-3ce8-4b04-80a5-ecd7663e1034.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/warp-8948ab59-3ce8-4b04-80a5-ecd7663e1034.png) **2)智能记忆** @@ -91,24 +91,24 @@ Warp 会记录上一次执行的命令,在顶部会有一个提示的按钮, 点击「clear」之前。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/warp-6055bfaa-a146-4cf8-a6f4-aa493dbfa60b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/warp-6055bfaa-a146-4cf8-a6f4-aa493dbfa60b.png) 点击「clear」之后。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/warp-181dff97-bd6f-4c41-94c8-8e9ac5567460.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/warp-181dff97-bd6f-4c41-94c8-8e9ac5567460.png) **3)区域选择** 传统的终端,在复制区域命令和输出结果的时候需要全部手动选择,而 Warp 是可以点选的,之后可以通过右键菜单进行复制粘贴(可以选择只复制命令或者输出,也可以都选),非常方便。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/warp-23c0a936-2371-4cf4-acf1-555bceecac44.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/warp-23c0a936-2371-4cf4-acf1-555bceecac44.png) **4)历史命令** 传统的终端在通过 up-down 键选择历史命令的时候,一次只能提示一个命令。而 Warp 会把历史命令做成一个滚动的可以选择的列表。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/warp-da43dca3-d8d1-43ad-9308-f8ec1c2b871b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/warp-da43dca3-d8d1-43ad-9308-f8ec1c2b871b.png) **5)命令导航** @@ -116,7 +116,7 @@ Warp 会记录上一次执行的命令,在顶部会有一个提示的按钮, 同时按下 Ctrl+Shift+R 可以打开命令导航,Warp 集成了很多工具的命令导航。比如说我们要执行 `git reset` 命令,那么到底格式什么,应该怎么执行,Warp 都提示的非常到位。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/warp-b9a4fd3f-24b2-4f6a-8a70-58fa1b313df3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/warp-b9a4fd3f-24b2-4f6a-8a70-58fa1b313df3.png) 这让我想起了 macOS 的效率工具 Alfred,可以搜索任何你想要的命令。 @@ -126,23 +126,23 @@ Warp 还提供了 AI 智能搜索,快捷键可以在 setting→keyboard shortc 可调整为自己喜欢的快捷键。我目前设置的是 `Ctrl+shift+>`。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/warp-04300bc5-5d0d-494b-955c-1d270133227a.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/warp-04300bc5-5d0d-494b-955c-1d270133227a.png) 比如说我问它“how many lines were changed in the last 2 commits?” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/warp-871e51e9-c2ac-4ecb-bfcb-ff339f05bd61.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/warp-871e51e9-c2ac-4ecb-bfcb-ff339f05bd61.png) Warp 解决的第二个痛点是增加协作功能。不过由于我目前没有邀请其他用户参与,还无法使用共享功能,后面有小伙伴体验的话,可以通过我分享的链接下载试一波。 >https://app.warp.dev/referral/25KR3Y -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/warp-d8952a84-a0a7-4c3d-b237-87cdc997bb4c.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/warp-d8952a84-a0a7-4c3d-b237-87cdc997bb4c.png) ### 三、配置 Warp 输入 Command+P 快捷键可以打开 Warp 的命令面板。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/warp-369d16f6-f897-4c9f-bbea-631d561e145b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/warp-369d16f6-f897-4c9f-bbea-631d561e145b.png) 键入 `sett` 关键字就可以打开配置页。 @@ -150,7 +150,7 @@ Warp 解决的第二个痛点是增加协作功能。不过由于我目前没有 大概有十多种主题可选,比如说这个女生非常喜欢的粉色系。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/warp-6b5dbf4c-7bb0-4926-b9e9-b64333cc2ed8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/warp-6b5dbf4c-7bb0-4926-b9e9-b64333cc2ed8.png) 更多主题可以到 GitHub 仓库的 theme 页。 @@ -159,7 +159,7 @@ Warp 解决的第二个痛点是增加协作功能。不过由于我目前没有 至于快捷键配置,如果不确定有哪些快捷键可以尝试,直接点击 Warp 顶部的这个温馨提示「welcome tips」就可以了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/gongju/warp-aa785da8-bb39-4851-97f5-b7f8baaccf34.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/gongju/warp-aa785da8-bb39-4851-97f5-b7f8baaccf34.png) ### 四、总结 diff --git a/docs/ide/4-debug-skill.md b/docs/ide/4-debug-skill.md index 143b3aa2a..3f2cc52ca 100644 --- a/docs/ide/4-debug-skill.md +++ b/docs/ide/4-debug-skill.md @@ -37,7 +37,7 @@ public static void main(String[] args) { 假如我们想在第 15 行查看每次调用,随即出来的 i 的值到底是多少,我们没必要在这个地方添加任何 log,在正常加断点的地方使用快捷键 `Shift + 鼠标左键`,就会弹出下面的内容 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/ide/4-debug-skill-e69c965f-f7e5-4e91-a92d-a43a1d0aced4) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/ide/4-debug-skill-e69c965f-f7e5-4e91-a92d-a43a1d0aced4) 勾选上 `Evaluate and log`, 并自定义你想查看的 log/变量,比如这里的 `"interested" + i`, 这样以 Debug 模式运行程序(正常模式运行,不会打印这些 log): @@ -83,7 +83,7 @@ Process finished with exit code 2. 在「眼睛」图标上鼠标右键 3. 在弹框中勾选上`Field access` 和`Field modification` 两个选项 -![image.gif](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/ide/4-debug-skill-72c23537-3f66-4283-b939-a265b7628a1a.gif) +![image.gif](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/ide/4-debug-skill-72c23537-3f66-4283-b939-a265b7628a1a.gif) 如果修改字段值的方法比较多,也可以在 `Condition` 的地方定义断点进入条件, 有了这个功能的加成,相信你阅读源码会顺畅许多 @@ -95,19 +95,19 @@ Process finished with exit code 这时我们就用到了 `Exception Breakpoints`, 当抛出异常时,在 catch 的地方打上断点,可以通过下图的几个位置获取栈顶异常类型,比如这里的 `NumberFormatException` -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/ide/4-debug-skill-c4c511af-b00d-458b-a4a1-97d1fe1e84b8) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/ide/4-debug-skill-c4c511af-b00d-458b-a4a1-97d1fe1e84b8) 知道异常类型后,就可以按照如下步骤添加异常断点了: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/ide/4-debug-skill-4c35cab7-83d2-45b4-8a27-ebeceb41ce08) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/ide/4-debug-skill-4c35cab7-83d2-45b4-8a27-ebeceb41ce08) 然后在弹框中选择 NumberFormatException -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/ide/4-debug-skill-a98e7885-1e84-4c38-8de1-ae04d3013176.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/ide/4-debug-skill-a98e7885-1e84-4c38-8de1-ae04d3013176.gif) 重新以 Debug 模式运行程序: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/ide/4-debug-skill-498ad99d-a15d-4a4e-a01b-b0c11cf8f72e.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/ide/4-debug-skill-498ad99d-a15d-4a4e-a01b-b0c11cf8f72e.gif) 程序「一路绿灯式」定位到抛出异常的位置,同时指出当时的变量信息,三个字:稳,准,狠,还有谁? @@ -120,11 +120,11 @@ Process finished with exit code 勾选上绿色框线上的内容,同样可以自定义跳转条件 Condition -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/ide/4-debug-skill-b81dc459-5a9c-4e0e-b24e-350943299eda) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/ide/4-debug-skill-b81dc459-5a9c-4e0e-b24e-350943299eda) 当以 Debug 模式运行程序的时候,会自动进入实现类的方法(注意断点形状): -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/ide/4-debug-skill-edbc1de2-4dd6-49a3-9a6a-5948d19aabee) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/ide/4-debug-skill-edbc1de2-4dd6-49a3-9a6a-5948d19aabee) 看到这你应该想到常见的 Runnable 接口中的 run 方法了,同样是有作用的,大家可以自行去尝试了 @@ -132,7 +132,7 @@ Process finished with exit code 相信有以上四种调试技巧的加成,无论是工作debug 还是私下阅读源码,都可以轻松驾驭了。最后,来看看 IDEA 支持的各种断点调试类型,如果你只知道红色小圆点,那咱在留言区好好说说吧 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/ide/4-debug-skill-92ad72da-4bf1-4bc4-b21d-78c33114dc96) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/ide/4-debug-skill-92ad72da-4bf1-4bc4-b21d-78c33114dc96) ----- diff --git a/docs/io/BIONIOAIO.md b/docs/io/BIONIOAIO.md index c7a28fb36..05d423936 100644 --- a/docs/io/BIONIOAIO.md +++ b/docs/io/BIONIOAIO.md @@ -10,13 +10,13 @@ tag: 周末午后,在家里面进行电话面试,我问了面试者几个关于 IO 的问题,其中包括什么是 BIO、NIO 和 AIO?三者有什么区别?具体如何使用等问题,但是面试者回答的并不是很满意。于是我在面试评价中写道:"对 Java 的 IO 提醒理解不够深入"。恰好被女朋友看到了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-1) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-1) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-2) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-2) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-3.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-3.gif) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-4) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-4) Java IO @@ -30,11 +30,11 @@ IO,常协作 I/O,是 Input/Output 的简称,即输入/输出。通常指 IO 是 Java 中比较重要,且比较难的知识点,主要是因为随着 Java 的发展,目前有三种 IO 共存。分别是 BIO、NIO 和 AIO。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-5) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-5) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-6.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-6.gif) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-7.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-7.gif) Java BIO @@ -56,13 +56,13 @@ Java AIO,全程 Asynchronous IO,是**异步非阻塞**的 IO。是一种非 在 NIO 的基础上引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-8) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-8) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-9) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-9) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-10) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-10) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-11.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-11.gif) 三种 IO 的区别 @@ -84,13 +84,13 @@ Java AIO,全程 Asynchronous IO,是**异步非阻塞**的 IO。是一种非 同步 VS 异步:水壶是不是在水烧开之后主动通知人。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-12.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-12.gif) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-13) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-13) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-14) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-14) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-15) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-15) 适用场景 @@ -100,9 +100,9 @@ NIO 方式适用于连接数目多且连接比较短(轻操作)的架构, AIO 方式适用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用 OS 参与并发操作,编程比较复杂,JDK7 开始支持。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-16.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-16.gif) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-17.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-17.gif) 使用方式 @@ -274,19 +274,19 @@ public class WriteToFile { } ``` -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-18.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-18.gif) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-19.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-19.gif) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-20.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-20.gif) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-21) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-21) 滴滴滴,水开了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-22) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-22) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/BIONIOAIO-23) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/BIONIOAIO-23) diff --git a/docs/io/shangtou.md b/docs/io/shangtou.md index aa91b5545..3a5e1183c 100644 --- a/docs/io/shangtou.md +++ b/docs/io/shangtou.md @@ -10,7 +10,7 @@ tag: “老王,Java IO 也太上头了吧?”新兵蛋子小二向头顶很凉快的老王抱怨道,“你瞧,我就按照传输方式对 IO 进行了一个简单的分类,就能搞出来这么多的玩意!” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/shangtou-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/shangtou-01.png) 好久没搞过 IO 了,老王看到这幅思维导图也是吃了一惊。想想也是,他当初学习 Java IO 的时候头也大,乌央乌央的一片,全是类,估计是所有 Java 包里面类最多的,一会是 Input 一会是 Output,一会是 Reader 一会是 Writer,真不知道 Java 的设计者是怎么想的。 @@ -28,7 +28,7 @@ tag: 通常来说,一个字母或者一个字符占用一个字节,一个汉字占用两个字节。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/shangtou-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/shangtou-02.png) 具体还要看字符编码,比如说在 UTF-8 编码下,一个英文字母(不分大小写)为一个字节,一个中文汉字为三个字节;在 Unicode 编码中,一个英文字母为一个字节,一个中文汉字为两个字节。 @@ -91,7 +91,7 @@ tag: 文件操作算是 IO 中最典型的操作了,也是最频繁的操作。那其实你可以换个角度来思考,比如说按照 IO 的操作对象来思考,IO 就可以分类为:文件、数组、管道、基本数据类型、缓冲、打印、对象序列化/反序列化,以及转换等。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/shangtou-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/shangtou-03.png) **1)文件** @@ -257,7 +257,7 @@ CPU 很快,它比内存快 100 倍,比磁盘快百万倍。那也就意味 为了减少程序和硬盘的交互,提升程序的效率,就引入了缓冲流,也就是类名前缀带有 Buffer 的那些,比如说 BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/io/shangtou-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/io/shangtou-04.png) 缓冲流在内存中设置了一个缓冲区,只有缓冲区存储了足够多的带操作的数据后,才会和内存或者硬盘进行交互。简单来说,就是一次多读/写点,少读/写几次,这样程序的性能就会提高。 diff --git a/docs/java8/Lambda.md b/docs/java8/Lambda.md index 26d97c5d4..d8904d118 100644 --- a/docs/java8/Lambda.md +++ b/docs/java8/Lambda.md @@ -10,7 +10,7 @@ tag: 今天分享的主题是《Lambda 表达式入门》,这也是之前一些读者留言强烈要求我写一写的,不好意思,让你们久等了,现在来满足你们,为时不晚吧? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/java8/Lambda-1) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/java8/Lambda-1) ### 01、初识 Lambda @@ -67,7 +67,7 @@ public class LamadaTest { 是不是很妙!比起匿名内部类,Lambda 表达式不仅易于理解,更大大简化了必须编写的代码数量。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/java8/Lambda-2) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/java8/Lambda-2) ### 02、Lambda 语法 @@ -135,7 +135,7 @@ public static void main(String[] args) { 和匿名内部类一样,不要在 Lambda 表达式主体内对方法内的局部变量进行修改,否则编译也不会通过:Lambda 表达式中使用的变量必须是 final 的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/java8/Lambda-3) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/java8/Lambda-3) 这个问题发生的原因是因为 Java 规范中是这样规定的: >Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression @@ -339,7 +339,7 @@ this = com.cmower.java_demo.journal.LamadaTest@3feba861 符合我们分析的预期。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/java8/Lambda-4) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/java8/Lambda-4) ### 04、最后 diff --git a/docs/java8/optional.md b/docs/java8/optional.md index dbf850acb..200112ac7 100644 --- a/docs/java8/optional.md +++ b/docs/java8/optional.md @@ -13,7 +13,7 @@ date: 2019-01-01 当然了,我们程序员是富有责任心的,不会坐视不管,于是就有了大量的 null 值检查。尽管有时候这种检查完全没有必要,但我们已经习惯了例行公事。终于,Java 8 看不下去了,就引入了 Optional,以便我们编写的代码不再那么刻薄呆板。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/java8/optional-1) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/java8/optional-1) ### 01、没有 Optional 会有什么问题 @@ -350,7 +350,7 @@ public class OptionalMapFilterDemo { -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/java8/optional-2) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/java8/optional-2) 好了,我亲爱的读者朋友,以上就是本文的全部内容了——可以说是史上最佳 Optional 指南了,能看到这里的都是最优秀的程序员,二哥必须要伸出大拇指为你点个赞。 diff --git a/docs/java8/stream.md b/docs/java8/stream.md index aa79b6289..daa30859b 100644 --- a/docs/java8/stream.md +++ b/docs/java8/stream.md @@ -13,7 +13,7 @@ date: 2019-01-01 两个星期以前,就有读者强烈要求我写一篇 Java Stream 流的文章,我说市面上不是已经有很多了吗,结果你猜他怎么说:“就想看你写的啊!”你看你看,多么苍白的喜欢啊。那就“勉为其难”写一篇吧,嘻嘻。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/java8/stream-1) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/java8/stream-1) 单从“Stream”这个单词上来看,它似乎和 java.io 包下的 InputStream 和 OutputStream 有些关系。实际上呢,没毛关系。Java 8 新增的 Stream 是为了解放程序员操作集合(Collection)时的生产力,之所以能解放,很大一部分原因可以归功于同时出现的 Lambda 表达式——极大的提高了编程效率和程序可读性。 @@ -312,6 +312,6 @@ Collectors 是一个收集器的工具类,内置了一系列收集器实现, 周杰伦, 王力宏, 陶喆, 林俊杰 ``` -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/java8/stream-2) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/java8/stream-2) diff --git a/docs/jvm/asm.md b/docs/jvm/asm.md index b419599ee..be91534d9 100644 --- a/docs/jvm/asm.md +++ b/docs/jvm/asm.md @@ -27,7 +27,7 @@ ASM是一种通用Java字节码操作和分析框架。它可以用于修改现 我们编写的java文件,会通过javac命令编译为class文件,JVM最终会执行该类型文件来运行程序。下图所示为class文件结构。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/asm-43844b78-c01f-4990-b038-3c91ff2eeb34.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/asm-43844b78-c01f-4990-b038-3c91ff2eeb34.jpg) 下面我们通过一个简单的实例来进行说明。下面是我们编写的一个简单的java文件,只是简单的函数调用. @@ -186,7 +186,7 @@ SourceFile: "Test.java" JVM的指令集是基于栈而不是寄存器,基于栈可以具备很好的跨平台性。在线程中执行一个方法时,我们会创建一个栈帧入栈并执行,如果该方法又调用另一个方法时会再次创建新的栈帧然后入栈,方法返回之际,原栈帧会返回方法的执行结果给之前的栈帧,随后虚拟机将会丢弃此栈帧。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/asm-e31b7e50-1d48-4eef-9552-6fa7e6c68fed.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/asm-e31b7e50-1d48-4eef-9552-6fa7e6c68fed.jpg) ### 局部变量表 @@ -238,9 +238,9 @@ public int sub(int, int); a = b + c 的字节码执行过程中操作数栈以及局部变量表的变化如下图所示 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/asm-4670450e-6199-4562-9cf4-354234c734c8.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/asm-4670450e-6199-4562-9cf4-354234c734c8.jpg) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/asm-9808d639-327f-4796-80d4-1809be0b9106.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/asm-9808d639-327f-4796-80d4-1809be0b9106.jpg) ## ASM操作 @@ -341,7 +341,7 @@ mv.visitEnd(); 可以一键生成对应的ASM API代码 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/asm-3c8c8db4-5b6a-4576-b147-62965d0e0c1c.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/asm-3c8c8db4-5b6a-4576-b147-62965d0e0c1c.jpg) ---- diff --git a/docs/jvm/bytecode.md b/docs/jvm/bytecode.md index f5db098c1..756091ea3 100644 --- a/docs/jvm/bytecode.md +++ b/docs/jvm/bytecode.md @@ -21,7 +21,7 @@ tag: 如今的 Java 虚拟机非常强大,不仅支持 Java 语言,还支持很多其他的编程语言,比如说 Groovy、Scala、Koltin 等等。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/bytecode-dd31bbd6-c75c-4426-9437-c0f57ea3b86f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/bytecode-dd31bbd6-c75c-4426-9437-c0f57ea3b86f.png) 来看一段代码吧。 @@ -36,7 +36,7 @@ public class Main { 编译生成 Main.class 文件后,可以在命令行使用 `xxd Main.class` 打开 class 文件(我用的是 Intellij IDEA,在 macOS 环境下)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/bytecode-bd941085-ff0e-4abf-a5f9-afb0493bfed7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/bytecode-bd941085-ff0e-4abf-a5f9-afb0493bfed7.png) @@ -52,7 +52,7 @@ public class Main { Java 内置了一个反编译命令 javap,可以通过 `javap -help` 了解 javap 的基本用法。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/bytecode-84b7af5c-93b1-4f63-bb30-946ab3d7e98c.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/bytecode-84b7af5c-93b1-4f63-bb30-946ab3d7e98c.png) OK,我们输入命令 `javap -v -p Main.class` 来查看一下输出的内容。 @@ -185,7 +185,7 @@ flags: (0x0021) ACC_PUBLIC, ACC_SUPER 类访问标记,一共有 8 种。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/bytecode-d12d6983-f427-40d2-bb4b-3a2c6c4c7806.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/bytecode-d12d6983-f427-40d2-bb4b-3a2c6c4c7806.png) 表明当前类是 `ACC_PUBLIC | ACC_SUPER`。位运算符 `|` 的意思是如果相对应位是 0,则结果为 0,否则为 1,所以 `0x0001 | 0x0020` 的结果是 `0x0021`(需要转成二进制进行运算)。 @@ -333,7 +333,7 @@ Java 虚拟机是在加载字节码文件的时候才进行的动态链接,也 关于字段类型的描述符映射表如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/bytecode-cbf16ce9-7853-4050-a1c0-8b874f3b0c1e.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/bytecode-cbf16ce9-7853-4050-a1c0-8b874f3b0c1e.png) 到此为止,第 2 个常量算是摸完了。组合起来的意思就是,声明了一个类型为 int 的字段 age。 @@ -365,7 +365,7 @@ private int age; 字段的访问标志和类的访问标志非常类似。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/bytecode-5f328e11-3486-4eb4-8fa9-5c5febfab894.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/bytecode-5f328e11-3486-4eb4-8fa9-5c5febfab894.png) @@ -374,7 +374,7 @@ private int age; 方法表用来描述接口或者类中声明的方法,包括类方法和成员方法,以及构造方法。方法的修饰符和字段略有不同,比如说 volatile 和 transient 不能用来修饰方法,再比如说方法的修饰符多了 synchronized、native、strictfp 和 abstract。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/bytecode-fd434d5c-ffc6-4a24-9787-98e573035068.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/bytecode-fd434d5c-ffc6-4a24-9787-98e573035068.png) 下面这部分为构造方法,返回类型为 void,访问标志为 public。 diff --git a/docs/jvm/class-file-jiegou.md b/docs/jvm/class-file-jiegou.md index e9ac8e67c..6f8d92945 100644 --- a/docs/jvm/class-file-jiegou.md +++ b/docs/jvm/class-file-jiegou.md @@ -13,11 +13,11 @@ tag: CS 的世界里流行着这么一句话,“计算机科学领域的任何问题都可以通过增加一个中间层来解决”。对于 Java 来说,JVM 就是这么一个产物,“Write once, Run anywhere”之所以能实现,靠得就是 JVM,它能在不同的操作系统下运行同一份源代码编译后的 class 文件。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-dfd7ce0d-1da2-4547-b2d7-57e0350f5911.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-dfd7ce0d-1da2-4547-b2d7-57e0350f5911.png) Java 是跨平台的,JVM 作为中间层,自然要针对不同的操作系统提供不同的实现。拿 JDK 11 来说,它的实现就有上图中提到的这么多种。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-b1386f9e-c69b-44b0-a8d0-69ffbe9ed31f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-b1386f9e-c69b-44b0-a8d0-69ffbe9ed31f.png) 通过不同操作系统的 JVM,我们的源代码就可以不用根据不同的操作系统编译成不同的二进制可执行文件了,跨平台的目标也就实现了。那这个 class 文件到底是什么玩意呢?它是怎么被 JVM 识别的呢? @@ -56,7 +56,7 @@ class Hello { 可以在 terminal 面板下用 `xxd Hello.class` 命令来查看。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-cb4afe63-6a8e-4ae1-a822-d4163c814daa.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-cb4afe63-6a8e-4ae1-a822-d4163c814daa.png) 咦?完全看不懂的样子呢。它是 class 文件的一种十六进制形式,`xxd` 这个命令的神奇之处就是它能将一个给定文件转换成十六进制形式。 @@ -90,7 +90,7 @@ public class ConstantTest { 布尔值 true 的十六进制是 0x01、字符 a 的十六进制是 0x61,字节 66 的十六进制是 0x42,短整型 67 的十六进制是 0x43,整型 68 的十六进制是 0x44。所以编译生成的整型常量在 class 文件中的位置如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-bbe4c673-c3a5-4952-901d-35446f91a3af.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-bbe4c673-c3a5-4952-901d-35446f91a3af.png) 第一个字节 0x03 表示常量的类型为 *CONSTANT_Integer_info*,是 JVM 中定义的 14 种常量类型之一,对应的还有 *CONSTANT_Float_info*、*CONSTANT_Long_info*、*CONSTANT_Double_info*,对应的标识分别是 0x04、0x05、0x06。 @@ -104,7 +104,7 @@ public class ConstantTest { 来看一下它在 class 文件中的位置。05 开头,7f ff ff ff ff ff ff ff 结尾,果然占 8 个字节,以前知道 long 型会占 8 个字节,但没有直观的感受,现在有了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-2c876f52-1cc1-4076-807a-d85a1cb80e75.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-2c876f52-1cc1-4076-807a-d85a1cb80e75.png) 接下来,我们再来看一段代码。 @@ -116,21 +116,21 @@ class Hello { “hello”是一个字符串,它的十六进制为 `68 65 6c 6c 6f`,我们来看一下它在 class 文件中的位置。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-801ed589-658c-407e-ac64-81fd525d7324.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-801ed589-658c-407e-ac64-81fd525d7324.png) 前面还有 3 个字节,第一个字节 0x01 是标识,标识类型为 *CONSTANT_Uft8_info*,第二个和第三个自己 0x00 0x05 用来表示第三部分字节数组的长度。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-ae4f38c9-68fe-40ad-91c6-3e7fd360de05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-ae4f38c9-68fe-40ad-91c6-3e7fd360de05.png) 与 *CONSTANT_Uft8_info* 类型对应的,还有一个 *CONSTANT_String_info*,用来表示字符串对象(之前代码中的 s),标识是 0x08。前者存储了字符串真正的值,后者并不包含字符串的内容,仅仅包含了一个指向常量池中 *CONSTANT_Uft8_info* 的索引。来看一下它在 class 文件中的位置。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-4e093bef-d592-4be7-847e-0ef5900c5fa4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-4e093bef-d592-4be7-847e-0ef5900c5fa4.png) *CONSTANT_String_info* 通过索引 19 来找到 *CONSTANT_Uft8_info*。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-85af064d-5dc6-4187-b4f3-3501ccfc99b3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-85af064d-5dc6-4187-b4f3-3501ccfc99b3.png) 除此之外,还有 *CONSTANT_Class_info*,用来表示类和接口,结构和 *CONSTANT_String_info* 类似,第一个字节是标识,值为 0x07,后面两个字节是常量池索引,指向 *CONSTANT_Utf8_info*——字符串存储的是类或者接口的全路径限定名。 @@ -138,11 +138,11 @@ class Hello { 先不着急,这里给大家介绍一款可视化字节码的工具 jclasslib bytecode viewer,可以直接在 IDEA 的插件市场安装。安装完成后,选中 class 文件,然后在 View 菜单里找到 Show Bytecode With Jclasslib 子菜单,就可以查看 class 文件的关键信息了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-ac6cc8cf-ed25-4bbb-8685-d473ecf15a60.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-ac6cc8cf-ed25-4bbb-8685-d473ecf15a60.png) 从上图中可以看到,常量池的总大小为 23,索引为 04 的 *CONSTANT_Class_info* 指向的是是索引为 21 的 *CONSTANT_Uft8_info*,值为 `com/itwanger/jvm/Hello`。21 的十六进制为 0x15,有了这个信息,我们就可以找到 *CONSTANT_Class_info* 在 class 文件中的位置了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-74816960-b8f7-42f3-9001-c05ebd25f58d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-74816960-b8f7-42f3-9001-c05ebd25f58d.png) 0x07 是第一个字节,*CONSTANT_Class_info* 的标识符,然后是两个字节,标识索引。 @@ -159,15 +159,15 @@ class Hello { 用 jclasslib 可以看到 *CONSTANT_NameAndType_info* 包含的索引有两个。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-70cd8902-136c-42a4-ab57-d6baf202e462.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-70cd8902-136c-42a4-ab57-d6baf202e462.png) 一个是 4,一个是 5,可以通过下图来表示 *CONSTANT_NameAndType_info* 的构成。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-5ac7d4c4-b905-462c-90f7-58b46fc5dda1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-5ac7d4c4-b905-462c-90f7-58b46fc5dda1.png) 对应 class 文件中的位置如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-eba9e047-d6fb-43e7-8c27-3683a076ccdd.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-eba9e047-d6fb-43e7-8c27-3683a076ccdd.png) 接下来是 *CONSTANT_Fieldref_info* 、*CONSTANT_Methodref_info* 和 *CONSTANT_InterfaceMethodref_info*,它们三个的结构比较类似,可以通过下面的伪代码来表示。 @@ -193,7 +193,7 @@ CONSTANT_*ref_info { 紧跟着常量池之后的区域就是访问标记(Access flags),这个标记用于识别类或接口的访问信息,比如说到底是 class 还是 interface?是 public 吗?是 abstract 抽象类吗?是 final 类吗?等等。总共有 16 个标记位可供使用,但常用的只有其中 7 个。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-1f5d3154-9a28-4cfa-935e-43d7e023036e.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-1f5d3154-9a28-4cfa-935e-43d7e023036e.png) 来看一个简单的枚举代码。 @@ -205,11 +205,11 @@ public enum Color { 通过 jclasslib 可以看到访问标记的信息有 `0x4031 [public final enum]`。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-d4873db5-1a9d-4e05-9765-59a71b083fe5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-d4873db5-1a9d-4e05-9765-59a71b083fe5.png) 对应 class 文件中的位置如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-774e8289-582b-4762-9dce-b0590ee5ad3f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-774e8289-582b-4762-9dce-b0590ee5ad3f.png) ### 05、this_class、super_class、interfaces @@ -227,7 +227,7 @@ class Hello { 通过 jclasslib 可以看到类的继承关系。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-77c4ecff-6d36-405d-93da-ee06431bf312.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-77c4ecff-6d36-405d-93da-ee06431bf312.png) - this_class 指向常量池中索引为 2 的 *CONSTANT_Class_info*。 - super_class 指向常量池中索引为 3 的 *CONSTANT_Class_info*。 @@ -235,7 +235,7 @@ class Hello { 对应 class 文件中的位置如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-6d5d9189-12a2-45d3-b811-92deede2f78d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-6d5d9189-12a2-45d3-b811-92deede2f78d.png) ### 06、字段表 @@ -271,7 +271,7 @@ field_info { 对应到 class 文件中的位置如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-5a40ed62-4ff2-4101-b2d5-15760032f563.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-5a40ed62-4ff2-4101-b2d5-15760032f563.png) ### 07、方法表 @@ -289,7 +289,7 @@ public class MethodsTest { 先用 jclasslib 看一下大概的信息。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-cbe6d025-84a5-4fea-821b-a4234f47c6cd.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-cbe6d025-84a5-4fea-821b-a4234f47c6cd.png) - 访问标记是 public static 的。 - 方法名为 main。 @@ -297,7 +297,7 @@ public class MethodsTest { 对应到 class 文件中的位置如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-f3932093-46f3-4ef0-8598-bbd70515a9bd.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-f3932093-46f3-4ef0-8598-bbd70515a9bd.png) ### 08、属性表 @@ -313,7 +313,7 @@ public class AttributeTest { 只有一个常量 DEFAULT_SIZE,它属于字段中的一种,就是加了 final 的静态变量。先通过 jclasslib 看一下它当中一个很重要的属性——ConstantValue,用来表示静态变量的初始值。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-dee995d6-e285-4a31-b11f-e93c3599cd8e.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-dee995d6-e285-4a31-b11f-e93c3599cd8e.png) - Attribute name index 指向常量池中值为“ConstantValue”的常量。 @@ -322,11 +322,11 @@ public class AttributeTest { 我画了一副图,可以完整的表示字段的结构,包含属性表在内。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-53f73e24-f060-45d2-8e29-34263c31847b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-53f73e24-f060-45d2-8e29-34263c31847b.png) 对应到 class 文件中的位置如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-423341a7-3aeb-4ac9-95e8-a1e7f7847255.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-423341a7-3aeb-4ac9-95e8-a1e7f7847255.png) 来看下面这段代码。 @@ -343,7 +343,7 @@ public class MethodCode { main 方法中调用了 foo 方法。通过 jclasslib 看一下它当中一个很重要的属性——Code, 方法的关键信息都存储在里面。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-e76339a8-0aab-418b-9722-4b3c8591693c.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-e76339a8-0aab-418b-9722-4b3c8591693c.png) - Attribute name index 指向常量池中值为“Code”的常量。 @@ -356,7 +356,7 @@ main 方法中调用了 foo 方法。通过 jclasslib 看一下它当中一个 对应 class 文件中的位置如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-file-jiegou-b5853549-b17b-48eb-8eb3-a393fb5d655f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-file-jiegou-b5853549-b17b-48eb-8eb3-a393fb5d655f.png) 到此为止,class 文件的内部算是剖析得差不多了,希望能对大家有所帮助。第一次拿刀,手有点颤,如果哪里有不足的地方,欢迎大家在评论区毫不留情地指出来! diff --git a/docs/jvm/class-load.md b/docs/jvm/class-load.md index bec6eb4df..4e518ea5e 100644 --- a/docs/jvm/class-load.md +++ b/docs/jvm/class-load.md @@ -17,7 +17,7 @@ tag: Java 在诞生的时候喊出了一个非常牛逼的口号:“Write Once, Run Anywhere”,为了达成这个目的,Sun 公司发布了许多可以在不同平台(Windows、Linux)上运行的 Java 虚拟机(JVM)——负责载入和执行 Java 编译后的字节码。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-load-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-load-01.png) 到底 Java 字节码是什么样子,我们借助一段简单的代码来看一看。 @@ -168,7 +168,7 @@ sun.misc.Launcher$ExtClassLoader@15db9742 如果以上三种类加载器不能满足要求的话,程序员还可以自定义类加载器(继承 `java.lang.ClassLoader` 类),它们之间的层级关系如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/class-load-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/class-load-02.png) 这种层次关系被称作为**双亲委派模型**:如果一个类加载器收到了加载类的请求,它会先把请求委托给上层加载器去完成,上层加载器又会委托上上层加载器,一直到最顶层的类加载器;如果上层加载器无法完成类的加载工作时,当前类加载器才会尝试自己去加载这个类。 diff --git a/docs/jvm/compile-jdk.md b/docs/jvm/compile-jdk.md index 6ead5f05f..4abc420a7 100644 --- a/docs/jvm/compile-jdk.md +++ b/docs/jvm/compile-jdk.md @@ -50,7 +50,7 @@ tag: 一图胜千言,各平台上的编译器支持如下表所示,按平台选择即可: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-3b66d5b6-272f-47bd-88f7-47146a06ef06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-3b66d5b6-272f-47bd-88f7-47146a06ef06.png) ### **4、其他工具** @@ -89,7 +89,7 @@ tag: `Mercurial`可以理解为和`Git`一样,是另外一种代码管理工具,安装好之后就有一个`hg`命令可用。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-cd8a19ba-e9f5-4a4a-a23c-17688f0f459d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-cd8a19ba-e9f5-4a4a-a23c-17688f0f459d.png) 而`OpenJDK`的源码已经提前托管到`http://hg.openjdk.java.net/`。 @@ -110,7 +110,7 @@ tag: 下载地址:`https://jdk.java.net/` -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-1bbbb1f8-da01-46e1-a793-487a25193c68.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-1bbbb1f8-da01-46e1-a793-487a25193c68.png) 选择你想要的版本下载即可。 @@ -132,27 +132,27 @@ tag: **配置JDK 8完成:** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-27593edc-03e2-4a42-baf3-ed5e5096b3cb.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-27593edc-03e2-4a42-baf3-ed5e5096b3cb.png) **配置JDK 11完成:** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-8526d944-a36e-4d37-93a0-9ad4ad53f927.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-8526d944-a36e-4d37-93a0-9ad4ad53f927.png) **注:** 如果这一步出错,大概率是某个软件环境未装,或者即使装了,但版本不匹配,控制台打印日志里一般是会提醒的。 比如我在配置`JDK 8`的时候,就遇到了一个`errof:GCC compiler is required`的问题: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-2957399f-6451-46dc-a003-76e5159265e9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-2957399f-6451-46dc-a003-76e5159265e9.png) 明明系统里已经有编译器,但还是报这个错误。通过后来修改 `jdk源码根目录/common/autoconf/generated-configure.sh`文件,将相关的两行代码注释后就配置通过了 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-ffa10d36-3a77-48aa-ae0c-d3daf67f9a19.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-ffa10d36-3a77-48aa-ae0c-d3daf67f9a19.png) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-a6f6e416-639e-4706-8b40-6152eb3cf85d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-a6f6e416-639e-4706-8b40-6152eb3cf85d.png) 配置完成,接下来开始执行真正的编译动作了! @@ -170,12 +170,12 @@ tag: **JDK 8编译完成:** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-89020f5a-0909-4c57-8c88-f655293a42a4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-89020f5a-0909-4c57-8c88-f655293a42a4.png) **JDK 11编译完成:** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-993fac94-2473-4f3b-9737-959510d2fe98.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-993fac94-2473-4f3b-9737-959510d2fe98.png) 从两张图的对比可以看出,编译`JDK 8`和`JDK 11`完成时在输出上还是有区别的。时间上的区别很大程度上来源于`JDK 11`的编译机配置要高不少。 @@ -198,7 +198,7 @@ tag: 进入该目录后,可以输入`./java -version`命令验证: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-f02dff40-f27e-476c-998b-bd6cdb5d3559.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-f02dff40-f27e-476c-998b-bd6cdb5d3559.png) 其次,编译生成的成品`JDK`套装,可以在目录 @@ -208,7 +208,7 @@ tag: 下找到,如图所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-1c781d34-776e-4acc-8d2b-b34bc59fda61.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-1c781d34-776e-4acc-8d2b-b34bc59fda61.png) 其中: @@ -218,7 +218,7 @@ tag: 进入`j2sdk-image`目录会发现,里面的内容和我们平时从网络上下载的成品`JDK`内容一致。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-7b7f147e-58c9-4eb5-b407-b8984cd72e1d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-7b7f147e-58c9-4eb5-b407-b8984cd72e1d.png) ### **2、JDK 11的编译输出** @@ -233,7 +233,7 @@ tag: 下看到,进入该目录后,也可以输入`./java -version`命令验证: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-f9b55425-f308-44e8-8812-ac59b2707c81.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-f9b55425-f308-44e8-8812-ac59b2707c81.png) 其次,编译生成的成品`JDK 11`套装,可以在目录 @@ -243,7 +243,7 @@ tag: 下找到,如图所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-4e96858f-f681-4498-b1c4-282d317a6a32.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-4e96858f-f681-4498-b1c4-282d317a6a32.png) 其中`jdk`目录就是编译生成的成品`JDK 11`套装。 @@ -256,20 +256,20 @@ tag: 新建一个最最基本的`Java`工程,比如命名为`JdkTest`,目的是把我们自己编译出的`JDK`给用上。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-2cf54b29-9b7e-46b2-8cde-4c36960aa09b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-2cf54b29-9b7e-46b2-8cde-4c36960aa09b.png) 我们点开`Project Structure`,选到`SDKs`选项,新添加上自己刚刚编译生成的JDK,并选为项目的JDK,看看是否能正常工作 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-ad8023d0-fbb7-48b1-856e-a8818677a0a5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-ad8023d0-fbb7-48b1-856e-a8818677a0a5.png) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-b8d87f09-6178-44c7-9572-a2852e81318d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-b8d87f09-6178-44c7-9572-a2852e81318d.png) 点击确定之后,我们运行之: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-3164cf31-8078-46d7-bee0-22e05b0c08de.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-3164cf31-8078-46d7-bee0-22e05b0c08de.png) 可以看到我们自己编译出的JDK已经用上了。 @@ -280,31 +280,31 @@ tag: 我们继续在上一步`JdkTest`项目的`Project Structure` → `SDKs`里将`JDK`源码关联到自行下载的JDK源码路径上: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-129ede68-c368-461e-92d6-38a8e5dee344.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-129ede68-c368-461e-92d6-38a8e5dee344.png) 这样方便我们对自己下载的`JDK源码`进行**阅读**、**调试**、**修改**、以及在源码里随意**做笔记**和**加注释**。 举个最简单的例子,比如我们打开`System.out.println()`这个函数的底层源码: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-c406b2a2-208a-4a54-a869-b3f526e93ccd.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-c406b2a2-208a-4a54-a869-b3f526e93ccd.png) 我们随便给它修改一下,加两行简单的标记,像这样: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-2a46b215-04e5-458a-b475-4dc31d7fe326.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-2a46b215-04e5-458a-b475-4dc31d7fe326.png) 为了使我们新加的代码行生效,我们必须要重新去JDK源码的根目录中再次执行 `make images`重新编译生成JDK方可生效: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-fd3cf88d-007e-4615-99b5-a3499c35ef40.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-fd3cf88d-007e-4615-99b5-a3499c35ef40.png) 因为之前已经全量编译过了,所以再次`make`的时候增量编译一般很快。 重新编译之后,我们再次运行`JdkTest`项目,就可以看到改动的效果了: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-33dc0de6-2690-4ba2-9fe7-0e450c44c07b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-33dc0de6-2690-4ba2-9fe7-0e450c44c07b.png) * * * @@ -319,14 +319,14 @@ tag: 比如,还是以上面例子中最简单的`System.out.println()`源码为例,我们添加几行中文注释: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-d6a44833-b908-4824-8862-1679bfdddfa3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-d6a44833-b908-4824-8862-1679bfdddfa3.png) 这时候我们去JDK源码目录下编译会发现满屏类似这样的报错: > 错误: 编码 ascii 的不可映射字符 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-ad0ca5a3-36c7-477d-bd58-d6731a87d762.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-ad0ca5a3-36c7-477d-bd58-d6731a87d762.png) 顿时有点懵,毕竟仅仅是加了几行注释。对于我们来说,源码里写点多行的中文注释基本是**刚需**,然而编译竟会报错,这还能不能让人愉快的玩耍了... 当时后背有点发凉。 @@ -339,12 +339,12 @@ tag: `jdk源码根目录/make/common/SetupJavaCompilers.gmk`文件中有两处指定了`ascii`相关的编码方式: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-a86933af-f6d5-4d45-b4ca-33069a212c52.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-a86933af-f6d5-4d45-b4ca-33069a212c52.png) 于是尝试将这两处`-encoding ascii`的均替换成`-encoding utf-8`: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/compile-jdk-c8117faf-d027-48d3-869b-32d0e98e8372.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/compile-jdk-c8117faf-d027-48d3-869b-32d0e98e8372.png) 然后再次执行`make images`编译,编译顺利通过! diff --git a/docs/jvm/cpu-percent-100.md b/docs/jvm/cpu-percent-100.md index 8d50fcc28..e1214727c 100644 --- a/docs/jvm/cpu-percent-100.md +++ b/docs/jvm/cpu-percent-100.md @@ -19,7 +19,7 @@ tag: 接着使用 `top -Hp pid` 将这个进程的线程显示出来。输入大写的 P 可以将线程按照 CPU 使用比例排序,于是得到以下结果。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/cpu-percent-100-e9b35104-fce9-40ea-ae91-8bbb7fd8aa96.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/cpu-percent-100-e9b35104-fce9-40ea-ae91-8bbb7fd8aa96.jpg) 果然某些线程的 CPU 使用率非常高。 @@ -30,7 +30,7 @@ tag: > 因为线程快照中线程 ID 都是16进制存放。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/cpu-percent-100-f8b051d5-f28d-481e-a0b2-e97151797e3b.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/cpu-percent-100-f8b051d5-f28d-481e-a0b2-e97151797e3b.jpg) 发现这是 `Disruptor` 的一个堆栈,前段时间正好解决过一个由于 Disruptor 队列引起的一次 [OOM]():[强如 Disruptor 也发生内存溢出?](https://crossoverjie.top/2018/08/29/java-senior/OOM-Disruptor/) @@ -40,7 +40,7 @@ tag: [http://fastthread.io/](http://fastthread.io/) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/cpu-percent-100-d6c9bc1c-9600-47f2-9ff1-d0c9bd8ef849.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/cpu-percent-100-d6c9bc1c-9600-47f2-9ff1-d0c9bd8ef849.jpg) 其中有一项菜单展示了所有消耗 CPU 的线程,我仔细看了下发现几乎都是和上面的堆栈一样。 @@ -62,7 +62,7 @@ tag: 代码如下: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/cpu-percent-100-49840c0d-2c10-4bcb-80c6-1df7553ddb6c.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/cpu-percent-100-49840c0d-2c10-4bcb-80c6-1df7553ddb6c.jpg) > 初步看来和这个等待策略有很大的关系。 @@ -70,44 +70,44 @@ tag: 为了验证,我在本地创建了 15 个 `Disruptor` 队列同时结合监控观察 CPU 的使用情况。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/cpu-percent-100-7f3b2fa6-6505-4b67-9f42-0170a236832b.jpg) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/cpu-percent-100-d597089d-54e0-49ef-a0f9-41798e84de48.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/cpu-percent-100-7f3b2fa6-6505-4b67-9f42-0170a236832b.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/cpu-percent-100-d597089d-54e0-49ef-a0f9-41798e84de48.jpg) 创建了 15 个 `Disruptor` 队列,同时每个队列都用线程池来往 `Disruptor队列` 里面发送 100W 条数据。 消费程序仅仅只是打印一下。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/cpu-percent-100-97b88b4d-2d81-47ab-9beb-830ac122c282.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/cpu-percent-100-97b88b4d-2d81-47ab-9beb-830ac122c282.jpg) 跑了一段时间发现 CPU 使用率确实很高。 --- -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/cpu-percent-100-c0ee1da2-29af-4581-b0d8-97f6250401e7.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/cpu-percent-100-c0ee1da2-29af-4581-b0d8-97f6250401e7.jpg) 同时 `dump` 线程发现和生产的现象也是一致的:消费线程都处于 `RUNNABLE` 状态,同时都在执行 `yield`。 通过查询 `Disruptor` 官方文档发现: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/cpu-percent-100-de904a90-8b59-4333-82f5-9ec94a6525a0.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/cpu-percent-100-de904a90-8b59-4333-82f5-9ec94a6525a0.jpg) > YieldingWaitStrategy 是一种充分压榨 CPU 的策略,使用`自旋 + yield`的方式来提高性能。 > 当消费线程(Event Handler threads)的数量小于 CPU 核心数时推荐使用该策略。 --- -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/cpu-percent-100-3faf6f7e-0d2c-4cfe-8e3a-07e15601485d.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/cpu-percent-100-3faf6f7e-0d2c-4cfe-8e3a-07e15601485d.jpg) 同时查阅到其他的等待策略 `BlockingWaitStrategy` (也是默认的策略),它使用的是锁的机制,对 CPU 的使用率不高。 于是在和之前同样的条件下将等待策略换为 `BlockingWaitStrategy`。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/cpu-percent-100-12912ce3-a702-4bb2-a19b-816c22f7d43a.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/cpu-percent-100-12912ce3-a702-4bb2-a19b-816c22f7d43a.jpg) --- -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/cpu-percent-100-b4aad83e-af9d-48fc-bcd0-ad2a42588179.jpg) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/cpu-percent-100-56dc1513-8f10-422f-bb2a-ae5dcfb8413f.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/cpu-percent-100-b4aad83e-af9d-48fc-bcd0-ad2a42588179.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/cpu-percent-100-56dc1513-8f10-422f-bb2a-ae5dcfb8413f.jpg) 和刚才的 CPU 对比会发现到后面使用率的会有明显的降低;同时 dump 线程后会发现大部分线程都处于 waiting 状态。 @@ -121,9 +121,9 @@ tag: 而现有的使用场景很明显消费线程数已经大大的超过了核心 CPU 数了,因为我的使用方式是一个 `Disruptor` 队列一个消费者,所以我将队列调整为只有 1 个再试试(策略依然是 `YieldingWaitStrategy`)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/cpu-percent-100-b1cbc2c2-828a-46e8-ba14-86cd0fa660c6.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/cpu-percent-100-b1cbc2c2-828a-46e8-ba14-86cd0fa660c6.jpg) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/cpu-percent-100-f8fb7682-a61a-407d-923c-890a16bce109.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/cpu-percent-100-f8fb7682-a61a-407d-923c-890a16bce109.jpg) 跑了一分钟,发现 CPU 的使用率一直都比较平稳而且不高。 diff --git a/docs/jvm/gc.md b/docs/jvm/gc.md index a777591cd..2d4dea30f 100644 --- a/docs/jvm/gc.md +++ b/docs/jvm/gc.md @@ -32,13 +32,13 @@ tag: 先创建一个字符串,这时候"jack"有一个引用,就是 m。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/gc-691109d2-bee4-4a79-8da6-87c5fd233f54.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/gc-691109d2-bee4-4a79-8da6-87c5fd233f54.jpg) 然后将 m 设置为 null,这时候"jack"的引用次数就等于0了,在引用计数算法中,意味着这块内容就需要被回收了。 **m = null;** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/gc-74865618-4576-4f8b-baf3-17d6a71125b9.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/gc-74865618-4576-4f8b-baf3-17d6a71125b9.jpg) 引用计数算法是将垃圾回收分摊到整个应用程序的运行当中了,而不是在进行垃圾收集时,要挂起整个应用的运行,直到对堆中所有对象的处理都结束。因此,采用引用计数的垃圾收集不属于严格意义上的"Stop-The-World"的垃圾收集机制。 @@ -69,7 +69,7 @@ b = null; **2\. 相互引用** **3\. 置空各自的声明引用** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/gc-fe980c00-3605-4b5d-a711-7edbfd2c80b0.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/gc-fe980c00-3605-4b5d-a711-7edbfd2c80b0.jpg) 我们可以看到,最后这2个对象已经不可能再被访问了,但由于他们相互引用着对方,导致它们的引用计数永远都不会为0,通过引用计数算法,也就永远无法通知GC收集器回收它们。 @@ -77,7 +77,7 @@ b = null; 可达性分析算法(Reachability Analysis)的基本思路是,通过一些被称为引用链(GC Roots)的对象作为起点,从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时(即从 GC Roots 节点到该节点不可达),则证明该对象是不可用的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/gc-1636ce77-77b3-4b10-b75a-c0c2d28912c5.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/gc-1636ce77-77b3-4b10-b75a-c0c2d28912c5.jpg) 通过可达性算法,成功解决了引用计数所无法解决的问题-“循环依赖”,只要你无法与 GC Root 建立直接或间接的连接,系统就会判定你为可回收对象。那这样就引申出了另一个问题,哪些属于 GC Root。 @@ -90,7 +90,7 @@ b = null; * 方法区中常量引用的对象 * 本地方法栈中 JNI(即一般说的 Native 方法)引用的对象 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/gc-6abf9f50-dc53-4e8f-a7f6-3e74df8803d6.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/gc-6abf9f50-dc53-4e8f-a7f6-3e74df8803d6.jpg) 1、虚拟机栈(栈帧中的本地变量表)中引用的对象 此时的 s,即为 GC Root,当s置空时,localParameter 对象也断掉了与 GC Root 的引用链,将被回收。 @@ -142,7 +142,7 @@ s = null; 4、本地方法栈中引用的对象 任何 native 接口都会使用某种本地方法栈,实现的本地方法接口是使用 C 连接模型的话,那么它的本地方法栈就是 C 栈。当线程调用 Java 方法时,虚拟机会创建一个新的栈帧并压入 Java 栈。然而当它调用的是本地方法时,虚拟机会保持 Java 栈不变,不再在线程的 Java 栈中压入新的帧,虚拟机只是简单地动态连接并直接调用指定的本地方法。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/gc-a138a4b4-56cb-4d6f-a65a-7f4259977476.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/gc-a138a4b4-56cb-4d6f-a65a-7f4259977476.jpg) ### 怎么回收垃圾 @@ -150,7 +150,7 @@ s = null; **标记 --- 清除算法** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/gc-2001e224-0f34-4429-bc89-a8fbe8ab271c.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/gc-2001e224-0f34-4429-bc89-a8fbe8ab271c.jpg) 标记清除算法(Mark-Sweep)是最基础的一种垃圾回收算法,它分为2部分,先把内存区域中的这些对象进行标记,哪些属于可回收标记出来,然后把这些垃圾拎出来清理掉。就像上图一样,清理掉的垃圾就变成未使用的内存区域,等待被再次使用。 @@ -160,7 +160,7 @@ s = null; **复制算法** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/gc-a2b15e6f-6921-4710-bf76-77858df38c27.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/gc-a2b15e6f-6921-4710-bf76-77858df38c27.jpg) 复制算法(Copying)是在标记清除算法上演化而来,解决标记清除算法的内存碎片问题。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。保证了内存的连续可用,内存分配时也就不用考虑内存碎片等复杂情况,逻辑清晰,运行高效。 @@ -168,7 +168,7 @@ s = null; **标记整理算法** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/gc-2d47a225-ad9d-4f15-9b4d-7dce9a693adf.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/gc-2d47a225-ad9d-4f15-9b4d-7dce9a693adf.jpg) 标记整理算法(Mark-Compact)标记过程仍然与标记 --- 清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,再清理掉端边界以外的内存区域。 @@ -178,7 +178,7 @@ s = null; ### 内存模型与回收策略 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/gc-59dddea1-b6bc-4fd4-bb79-d81adbdc7bed.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/gc-59dddea1-b6bc-4fd4-bb79-d81adbdc7bed.jpg) Java 堆(Java Heap)是JVM所管理的内存中最大的一块,堆又是垃圾收集器管理的主要区域,这里我们主要分析一下 Java 堆的结构。 diff --git a/docs/jvm/how-jvm-run-zijiema-zhiling.md b/docs/jvm/how-jvm-run-zijiema-zhiling.md index 989c3274a..4d7c98130 100644 --- a/docs/jvm/how-jvm-run-zijiema-zhiling.md +++ b/docs/jvm/how-jvm-run-zijiema-zhiling.md @@ -21,7 +21,7 @@ tag: 一个线程中的方法调用链可能会很长,很多方法都处于执行状态。对于执行引擎来说,在活动线程中,只有位于栈顶的栈帧才是有效的,称为当前栈帧(Current Stack Frame),与这个栈帧相关联的方法成为当前方法。执行引擎运行的所有字节码指令对当前栈帧进行操作,在概念模型上,典型的栈帧结构如下图: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/how-jvm-run-zijiema-zhiling-a58ee82e-c0b0-4c06-9606-f7a0f0df0de9) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/how-jvm-run-zijiema-zhiling-a58ee82e-c0b0-4c06-9606-f7a0f0df0de9) #### 局部变量表 @@ -41,7 +41,7 @@ public class LocalVaraiablesTable { 然后用 Intellij IDEA 的 jclasslib 查看一下编译后的字节码文件 LocalVaraiablesTable.class。可以看到 `write()` 方法的 Code 属性中,Maximum local variables(局部变量表的最大容量)的值为 3。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/how-jvm-run-zijiema-zhiling-70ab6bf6-4fbb-4722-99b4-a93d5061630c.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/how-jvm-run-zijiema-zhiling-70ab6bf6-4fbb-4722-99b4-a93d5061630c.png) 按理说,局部变量表的最大容量应该为 2 才对,一个 age,一个 name,为什么是 3 呢? @@ -49,7 +49,7 @@ public class LocalVaraiablesTable { 点开 Code 属性,查看 LocalVaraiableTable 就可以看到详细的信息了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/how-jvm-run-zijiema-zhiling-e5e6037c-9be1-472f-8ab3-2754466e7828.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/how-jvm-run-zijiema-zhiling-e5e6037c-9be1-472f-8ab3-2754466e7828.png) 第 0 个是 this,类型为 LocalVaraiablesTable 对象;第 1 个是方法参数 age,类型为整型 int;第 2 个是方法内部的局部变量 name,类型为字符串 String。 @@ -98,15 +98,15 @@ public void solt() { 用 jclasslib 可以查看到,`solt()` 方法的 Maximum local variables 的值为 4。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/how-jvm-run-zijiema-zhiling-6734774b-376c-49bf-a915-508c7e829557.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/how-jvm-run-zijiema-zhiling-6734774b-376c-49bf-a915-508c7e829557.png) 为什么等于 4 呢?带上 this 也就 3 个呀? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/how-jvm-run-zijiema-zhiling-91ad04f8-1620-44c9-83d1-6fbd7860701a.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/how-jvm-run-zijiema-zhiling-91ad04f8-1620-44c9-83d1-6fbd7860701a.png) 查看 LocalVaraiableTable 就明白了,变量 i 的下标为 3,也就意味着变量 d 占了两个槽。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/how-jvm-run-zijiema-zhiling-630b50e3-fc37-4748-8d20-852d5358f87a.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/how-jvm-run-zijiema-zhiling-630b50e3-fc37-4748-8d20-852d5358f87a.png) #### 操作数栈 @@ -128,11 +128,11 @@ public class OperandStack { OperandStack 类共有 2 个方法,`test()` 方法中调用了 `add()` 方法,传递了 2 个参数。用 jclasslib 可以看到,`test()` 方法的 maximum stack size 的值为 3。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/how-jvm-run-zijiema-zhiling-f790aa0f-d742-465b-91bf-5f143ee098c1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/how-jvm-run-zijiema-zhiling-f790aa0f-d742-465b-91bf-5f143ee098c1.png) 这是因为调用成员方法的时候会将 this 和所有参数压入栈中,调用完毕后 this 和参数都会一一出栈。通过 「Bytecode」 面板可以查看到对应的字节码指令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/how-jvm-run-zijiema-zhiling-c37add5c-a74b-4bd6-8c8e-9085d9e6d374.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/how-jvm-run-zijiema-zhiling-c37add5c-a74b-4bd6-8c8e-9085d9e6d374.png) - aload_0 用于将局部变量表中下标为 0 的引用类型的变量,也就是 this 加载到操作数栈中; - iconst_1 用于将整数 1 加载到操作数栈中; @@ -143,14 +143,14 @@ OperandStack 类共有 2 个方法,`test()` 方法中调用了 `add()` 方法 再来看一下 `add()` 方法的字节码指令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/how-jvm-run-zijiema-zhiling-49e3f396-7ea8-49f5-81d4-093b9bdfa453.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/how-jvm-run-zijiema-zhiling-49e3f396-7ea8-49f5-81d4-093b9bdfa453.png) - iload_1 用于将局部变量表中下标为 1 的 int 类型变量加载到操作数栈上(下标为 0 的是 this); - iload_2 用于将局部变量表中下标为 2 的 int 类型变量加载到操作数栈上; - iadd 用于 int 类型的加法运算; - ireturn 为返回值为 int 的方法返回指令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/how-jvm-run-zijiema-zhiling-3ffdbe03-c0e4-49de-97a6-76666964a087.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/how-jvm-run-zijiema-zhiling-3ffdbe03-c0e4-49de-97a6-76666964a087.png) 操作数中的数据类型必须与字节码指令匹配,以上面的 iadd 指令为例,该指令只能用于整型数据的加法运算,它在执行的时候,栈顶的两个数据必须是 int 类型的,不能出现一个 long 型和一个 double 型的数据进行 iadd 命令相加的情况。 @@ -205,7 +205,7 @@ public class DynamicLinking { 用 jclasslib 看一下 main 方法的字节码指令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/how-jvm-run-zijiema-zhiling-93a21aaf-ff67-445d-8ddb-ac6f72fd9b25.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/how-jvm-run-zijiema-zhiling-93a21aaf-ff67-445d-8ddb-ac6f72fd9b25.png) - 第 1 行:new 指令创建了一个 Man 对象,并将对象的内存地址压入栈中。 - 第 2 行:dup 指令将栈顶的值复制一份并压入栈顶。因为接下来的指令 invokespecial 会消耗掉一个当前类的引用,所以需要复制一份。 @@ -560,7 +560,7 @@ Java 语言常被人们定义成「解释执行」的语言,但随着 JIT 以 无论是解释执行还是编译执行,无论是物理机还是虚拟机,对于应用程序,机器都不可能像人一样阅读、理解,然后获得执行能力。大部分的程序代码到物理机的目标代码或者虚拟机执行的指令之前,都需要经过下图中的各个步骤。下图中最下面的那条分支,就是传统编译原理中程序代码到目标机器代码的生成过程;中间那条分支,则是解释执行的过程。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/how-jvm-run-zijiema-zhiling-3c8a0865-2a77-464e-8dd6-5616fd6a72d7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/how-jvm-run-zijiema-zhiling-3c8a0865-2a77-464e-8dd6-5616fd6a72d7.png) 如今,基于物理机、Java 虚拟机或者非 Java 的其它高级语言虚拟机的语言,大多都会遵循这种基于现代编译原理的思路,在执行前先对程序源代码进行词法分析和语法分析处理,把源代码转化为抽象语法树。对于一门具体语言的实现来说,词法分析、语法分析以至后面的优化器和目标代码生成器都可以选择独立于执行引擎,形成一个完整意义的编译器去实现,这类代表是 C/C++。也可以为一个半独立的编译器,这类代表是 Java。又或者把这些步骤和执行全部封装在一个封闭的黑匣子中,如大多数的 JavaScript 执行器。 diff --git a/docs/jvm/how-run-java-code.md b/docs/jvm/how-run-java-code.md index 7a6e3af19..27fd394d3 100644 --- a/docs/jvm/how-run-java-code.md +++ b/docs/jvm/how-run-java-code.md @@ -28,17 +28,17 @@ public class HelloWorld { 点击 IDEA 工具栏中的锤子按钮(Build Project,编译整个项目),如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/five-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/five-01.png) 这时候,就可以在 src 的同级目录 target 下找到一个名为 HelloWorld.class 的文件。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/five-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/five-02.png) 如果找不到的话,在目录上右键选择「Reload from Disk,从磁盘上重新加载」,如下图所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/five-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/five-03.png) 可以双击打开它。 @@ -103,35 +103,35 @@ public class com/itwanger/five/HelloWorld { 那这个字节码文件是怎么看到的呢?可以通过 IDEA 菜单栏中的「View」→「Show Bytecode」查看,如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/five-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/five-04.png) PS:字节码并不是机器码,操作系统无法直接识别,需要在操作系统上安装不同版本的 Java 虚拟机(JVM)来识别。通常情况下,我们只需要安装不同版本的 JDK(Java Development Kit,Java 开发工具包)就行了,它里面包含了 JRE(Java Runtime Environment,Java 运行时环境),而 JRE 又包含了 JVM。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/five-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/five-05.png) Windows、Linux、MacOS 等操作系统都有相应的 JDK,只要安装好了 JDK 就有了 Java 语言的运行时环境,就可以把一份字节码文件在不同的平台上运行了。可以在 [Oracle 官网](https://www.oracle.com/java/technologies/javase-jdk11-downloads.html)上下载不同版本的 JDK。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/five-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/five-06.png) PPS:为什么要查看字节码呢?查看字节码文件更容易让我们搞懂 Java 代码背后的原理,比如搞懂 Java 中的各种语法糖的本质。 相比于 IDEA 自带的「Show Bytecode」功能,我更推荐 `jclasslib` 这款插件,可以在插件市场中安装(我已经安装过了)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/five-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/five-07.png) 安装完成之后,点击 View -> Show Bytecode With jclasslib 即可通过 jclasslib 查看字节码文件了(点击之前,光标要停留在对应的类文件上),如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/five-08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/five-08.png) 使用 jclasslib 不仅可以直观地查看类对应的字节码文件,还可以查看类的基本信息、常量池、接口、字段、方法等信息,如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/five-09.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/five-09.png) 也就是说,在编译阶段,Java 会将 Java 源代码文件编译为字节码文件。在这个阶段,编译器会进行一些检查工作,比如说,某个关键字是不是写错了,语法上是不是符合预期了,不能有很明显的错误,否则带到运行时再检查出来就会比较麻烦了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/five-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/five-10.png) Java 字节码是沟通 JVM 与 Java 代码的桥梁,下面使用 javap 来稍微看一下字节码到底长什么样子。 @@ -173,6 +173,6 @@ JVM 就是靠解析这些 opcode 和操作数来完成程序的执行的。当 而其他大块数据,是存放在堆上的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/how-run-java-code-91dac706-1c4e-4775-bc4e-b2104283aa04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/how-run-java-code-91dac706-1c4e-4775-bc4e-b2104283aa04.png) diff --git a/docs/jvm/hsdb.md b/docs/jvm/hsdb.md index 09198e28c..a98ab6eb9 100644 --- a/docs/jvm/hsdb.md +++ b/docs/jvm/hsdb.md @@ -72,33 +72,33 @@ $ jps ### 可视化线程栈 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-a606c6ac-1cfc-44c3-8fdf-e0eeeabbf05a.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-a606c6ac-1cfc-44c3-8fdf-e0eeeabbf05a.png) ### 对象直方图 `Tools -> Object Histogram`,我们可以通过对象直方图快速定位某个类型的对象的地址以供我们进一步分析 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-cf39737a-7d6a-42de-b843-123cba1f96aa.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-cf39737a-7d6a-42de-b843-123cba1f96aa.png) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-974d211c-e627-40d6-af13-9560ebae0bfa.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-974d211c-e627-40d6-af13-9560ebae0bfa.png) ### OOP信息 我们可以根据对象地址在 `Tools -> Inspector` 获取对象的在 JVM 层的实例 `instanceOopDesc` 对象,它包括对象头 `_mark` 和 `_metadata` 以及实例信息 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-026fb881-59a2-4e0f-ac4a-a2a7b505a707.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-026fb881-59a2-4e0f-ac4a-a2a7b505a707.png) ### 堆信息 我们可以通过 `Tools -> Heap Parameters` 获取堆信息,可以结合对象地址判断对象位置 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-3baabaf0-1681-4443-b8db-ae08128744d6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-3baabaf0-1681-4443-b8db-ae08128744d6.png) ### 加载类列表 我们可以通过 `Tools -> Class Browser` 来获取所有加载类列表 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-7e4ebd1f-ba9c-4862-b4c3-574de5c30d6b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-7e4ebd1f-ba9c-4862-b4c3-574de5c30d6b.png) ### 元数据区 @@ -108,18 +108,18 @@ HotSpot VM 里有一套对象专门用来存放元数据,它们包括:  * `ConstantPool/ConstantPoolCache` 对象:每个 `InstanceKlass` 关联着一个 `ConstantPool`,作为该类型的运行时常量池。这个常量池的结构跟 Class 文件里的常量池基本上是对应的 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-07d80d18-be4e-4861-bea3-291eea0ff262.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-07d80d18-be4e-4861-bea3-291eea0ff262.png) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-b7b0ebed-cd38-42b7-bebc-41090409a1db.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-b7b0ebed-cd38-42b7-bebc-41090409a1db.png) * `Method` 对象,用来描述 Java 方法的总体信息,如方法入口地址、调用/循环计数器等等 * `ConstMethod` 对象,记录着 Java 方法的不变的描述信息,包括方法名、方法的访问修饰符、**字节码**、行号表、局部变量表等等。**注意,字节码指令被分配在 `constMethodOop` 对象的内存区域的末尾** * `MethodData` 对象,记录着 Java 方法执行时的 profile 信息,例如某方法里的某个字节码之类是否从来没遇到过 null,某个条件跳转是否总是走同一个分支,等等。这些信息在解释器(多层编译模式下也在低层的编译生成的代码里)收集,然后供给 HotSpot Server Compiler 用于做激进优化。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-59231922-9ce3-4107-ab1a-b33818cbab96.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-59231922-9ce3-4107-ab1a-b33818cbab96.png) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-85c6fca7-2d1f-4194-bc07-fd1e2ab18632.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-85c6fca7-2d1f-4194-bc07-fd1e2ab18632.png) * `Symbol` 对象,对应 Class 文件常量池里的 `JVM_CONSTANT_Utf8` 类型的常量。有一个 VM 全局的 `SymbolTable` 管理着所有 `Symbol`。`Symbol` 由所有 Java 类所共享。 @@ -159,23 +159,23 @@ class VMShow { 首先查看对象直方图可以找到三个 VMShow 对象 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-e5f0f200-83fd-4529-b5d6-416f9a6f626b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-e5f0f200-83fd-4529-b5d6-416f9a6f626b.png) 那么如何确定这三个地址分别属于哪些变量呢?首先找静态变量,它在 JDK8 中是在 Class 对象中的,因此我们可以找它们的反向指针,如果是`java.lang.Class` 的那么就是静态变量 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-301ebfc3-c2c4-49de-946a-5d2f1660e669.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-301ebfc3-c2c4-49de-946a-5d2f1660e669.png) 我们可以从 ObjTest 的 `instanceKlass` 中的镜像找到 class 对象来验证是否是该对象的 class -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-f22359ab-b066-405f-a754-197ccbd36884.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-f22359ab-b066-405f-a754-197ccbd36884.png) 那么成员变量和局部变量如何区分呢?成员变量会被类实例引用,而局部变量地址则在会被被放在栈区 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-51cb7321-fb7a-42b0-9321-edd410e3d328.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-51cb7321-fb7a-42b0-9321-edd410e3d328.png) 那么局部变量的反向指针都是 null,怎么确定它就被栈区所引用呢?我们可以看可视化线程栈 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-51fb09f4-06d7-4518-8038-5dd69d765862.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-51fb09f4-06d7-4518-8038-5dd69d765862.png) ### 分析字符串字面量存储区域 @@ -198,7 +198,7 @@ public class StringTest { 2. 打开对象直方图发现只有 1 个 `a` 的字符串对象 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-b5631e70-be3a-48de-99be-4468632d23e0.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-b5631e70-be3a-48de-99be-4468632d23e0.png) 3. 查找 StringTable 中 `a` 的对象地址 @@ -208,7 +208,7 @@ jseval "st = sa.vm.stringTable;st.stringsDo(function (s) { if (sapkg.oops.OopUti 可以根据需要改变 `matches` 中的值来匹配 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-76084b8d-6134-4866-8891-c24a43a3b836.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-76084b8d-6134-4866-8891-c24a43a3b836.png) 可以看到这个对象地址就是 StringTable 中引用的地址 @@ -216,7 +216,7 @@ jseval "st = sa.vm.stringTable;st.stringsDo(function (s) { if (sapkg.oops.OopUti 5. 重新使用对象直方图查看 String 值 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-f3c35027-2fe1-4b18-8a1c-b7243a9b5149.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-f3c35027-2fe1-4b18-8a1c-b7243a9b5149.png) 这里有5个值,`ab` 有3个: @@ -231,13 +231,13 @@ jseval "st = sa.vm.stringTable;st.stringsDo(function (s) { if (sapkg.oops.OopUti ``` -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-f07a9fc8-5744-4859-9df9-c3a1016e936a.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-f07a9fc8-5744-4859-9df9-c3a1016e936a.png) 那么运行时常量池中存放的是哪些呢?实际上它和 StringTable 一样是这些对象的引用,只不过 StringTable 是全局共享的,而运行时常量池只有该类的一些字面量。我们通过加载类列表可以查看 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-25fa5e06-0250-424b-8b05-aea770e80963.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-25fa5e06-0250-424b-8b05-aea770e80963.png) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-88e8c10d-e09c-4a74-95e6-e5b21577459f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-88e8c10d-e09c-4a74-95e6-e5b21577459f.png) ### 分析String.intern @@ -328,21 +328,21 @@ public class StringInternTest { jseval "st = sa.vm.stringTable;st.stringsDo(function (s) { if (sapkg.oops.OopUtilities.stringOopToString(s).matches('^(he|llo|hello|1|2|12)')) {print(s + ': ');s.printValueOn(java.lang.System.out); println('')}})" ``` -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-ca5d06a0-1690-4a8f-98cb-aa6bd7800afe.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-ca5d06a0-1690-4a8f-98cb-aa6bd7800afe.png) 但是 `hello` 对象还是存在的(new) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-85253e10-d0c2-442b-a26b-91b1b2588f1c.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-85253e10-d0c2-442b-a26b-91b1b2588f1c.png) 接着执行 s1.intern 会将 `hello` 对象的地址放入 StringTable -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-801d35f2-0c8a-4699-afbf-057c1e6cac6c.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-801d35f2-0c8a-4699-afbf-057c1e6cac6c.png) 再执行 `String s2="hello";` 会发现 `hello` 对象仍然只有一个,都指向同一个。 而继续在 6 打断点,即执行完 `String s4 = "12";`,因为 `12` 不在字符串常量池,那么会新建一个 `12`的实例,并让字符串常量池引用它,这样会发现就有两个 `12` 了 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/hsdb-47a42bfa-7645-4c9b-bcd8-aeabea1ae44f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/hsdb-47a42bfa-7645-4c9b-bcd8-aeabea1ae44f.png) --- diff --git a/docs/jvm/jit.md b/docs/jvm/jit.md index a3a00cd69..ed100ea46 100644 --- a/docs/jvm/jit.md +++ b/docs/jvm/jit.md @@ -23,7 +23,7 @@ Java的执行过程整体可以分为两个部分,第一步由javac将源码 怎么样才会被认为是热点代码呢?JVM中会设置一个阈值,当方法或者代码块的在一定时间内的调用次数超过这个阈值时就会被编译,存入codeCache中。当下次执行时,再遇到这段代码,就会从codeCache中读取机器码,直接执行,以此来提升程序运行的性能。整体的执行过程大致如下图所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/jit-9a62fc02-1a6a-451e-bb2b-19fc086d5be0.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/jit-9a62fc02-1a6a-451e-bb2b-19fc086d5be0.png) ### 1\. JVM中的编译器 @@ -51,7 +51,7 @@ Ideal Graph的构建是在解析字节码的时候,根据字节码中的指令 无论是否进行全局优化,Ideal Graph都会被转化为一种更接近机器层面的MachNode Graph,最后编译的机器码就是从MachNode Graph中得的,生成机器码前还会有一些包括寄存器分配、窥孔优化等操作。关于Ideal Graph和各种全局的优化手段会在后面的章节详细介绍。Server Compiler编译优化的过程如下图所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/jit-f4d1b763-be02-4bb2-ab0e-45b1f0eb9550.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/jit-f4d1b763-be02-4bb2-ab0e-45b1f0eb9550.png) **Graal Compiler** @@ -77,7 +77,7 @@ profiling就是收集能够反映程序执行状态的数据。其中最基本 通常情况下,C2代码的执行效率要比C1代码的高出30%以上。C1层执行的代码,按执行效率排序从高至低则是1层>2层>3层。这5个层次中,1层和4层都是终止状态,当一个方法到达终止状态后,只要编译后的代码并没有失效,那么JVM就不会再次发出该方法的编译请求的。服务实际运行时,JVM会根据服务运行情况,从解释执行开始,选择不同的编译路径,直到到达终止状态。下图中就列举了几种常见的编译路径: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/jit-a6cebc82-ed4d-4b6d-892a-c5b245d227ab.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/jit-a6cebc82-ed4d-4b6d-892a-c5b245d227ab.png) * 图中第①条路径,代表编译的一般情况,热点方法从解释执行到被3层的C1编译,最后被4层的C2编译。 * 如果方法比较小(比如Java服务中常见的getter/setter方法),3层的profiling没有收集到有价值的数据,JVM就会断定该方法对于C1代码和C2代码的执行效率相同,就会执行图中第②条路径。在这种情况下,JVM会在3层编译之后,放弃进入C2编译,直接选择用1层的C1编译运行。 @@ -240,7 +240,7 @@ public void DeadCodeElimination{ HIR是由很多基本块(Basic Block)组成的控制流图结构,每个块包含很多SSA形式的指令。基本块的结构如下图所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/jit-037b406d-1040-4bf8-976c-abf14a92402d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/jit-037b406d-1040-4bf8-976c-abf14a92402d.png) 其中,predecessors表示前驱基本块(由于前驱可能是多个,所以是BlockList结构,是多个BlockBegin组成的可扩容数组)。同样,successors表示多个后继基本块BlockEnd。除了这两部分就是主体块,里面包含程序执行的指令和一个next指针,指向下一个执行的主体块。 @@ -281,7 +281,7 @@ public static int foo(int count) { 对应的IR图如下所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/jit-f96da42a-568b-45ba-bed1-f4238ac32e14.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/jit-f96da42a-568b-45ba-bed1-f4238ac32e14.png) 图中若干个顺序执行的节点将被包含在同一个基本块之中,如图中的B0、B1等。B0基本块中0号Start节点是方法入口,B3中21号Return节点是方法出口。红色加粗线条为控制流,蓝色线条为数据流,而其他颜色的线条则是特殊的控制流或数据流。被控制流边所连接的是固定节点,其他的则是浮动节点(浮动节点指只要能满足数据依赖关系,可以放在不同位置的节点,浮动节点变动的这个过程称为Schedule)。 @@ -310,7 +310,7 @@ int a = 0; 为了解决这个问题,就引入一个Phi Nodes的概念,能够根据不同的执行路径选择不同的值。于是,上面这段代码可以表示为下面这张图: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/jit-fb8b2bac-a7b9-45eb-bd28-05e35cf043ae.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/jit-fb8b2bac-a7b9-45eb-bd28-05e35cf043ae.png) Phi Nodes中保存不同路径上包含的所有值,Region Nodes根据不同路径的判断条件,从Phi Nodes取得当前执行路径中变量应该赋予的值,带有Phi节点的SSA形式的伪代码如下: @@ -381,11 +381,11 @@ public static int bar(boolean flag) { bar方法的IR图: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/jit-04ca4a7e-46e7-4782-bb43-333aea31ed57.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/jit-04ca4a7e-46e7-4782-bb43-333aea31ed57.png) 内联后的IR图: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/jit-4bf4d190-7fd2-4542-b948-0c85ee6963d2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/jit-4bf4d190-7fd2-4542-b948-0c85ee6963d2.png) 内联不仅将被调用方法的IR图节点复制到调用者方法的IR图中,还要完成其他操作。 @@ -401,7 +401,7 @@ bar方法的IR图: 可以通过虚拟机参数-XX:MaxInlineLevel调整内联的层数,以及1层的直接递归调用(可以通过虚拟机参数-XX:MaxRecursiveInlineLevel调整)。一些常见的内联相关的参数如下表所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/jit-48e4ff65-07ec-487e-8b08-2f8fed1e56bd.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/jit-48e4ff65-07ec-487e-8b08-2f8fed1e56bd.png) **虚函数内联** @@ -709,13 +709,13 @@ y1=x1*3 经过强度削减后得到 y1=(x1<<1)+x1 通过增加-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining -XX:+PrintCodeCache -XX:+PrintCodeCacheOnCompilation -XX:+TraceClassLoading -XX:+LogCompilation -XX:LogFile=LogPath参数可以输出编译、内联、codeCache信息到文件。但是打印的编译日志多且复杂很难直接从其中得到信息,可以使用JITwatch的工具来分析编译日志。JITwatch首页的Open Log选中日志文件,点击Start就可以开始分析日志。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/jit-82ee887c-af7d-48d7-88a0-28960e564d4a.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/jit-82ee887c-af7d-48d7-88a0-28960e564d4a.png) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/jit-6158d832-9a0d-4af0-96ff-bf216a9cd5c6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/jit-6158d832-9a0d-4af0-96ff-bf216a9cd5c6.png) 如上图所示,区域1中是整个项目Java Class包括引入的第三方依赖;区域2是功能区Timeline以图形的形式展示JIT编译的时间轴,Histo是直方图展示一些信息,TopList里面是编译中产生的一些对象和数据的排序,Cache是空闲codeCache空间,NMethod是Native方法,Threads是JIT编译的线程;区域3是JITwatch对日志分析结果的展示,其中Suggestions中会给出一些代码优化的建议,举个例子,如下图中: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/jit-04b2d9ea-7add-4ee5-bf72-61a6bbaa58cf.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/jit-04b2d9ea-7add-4ee5-bf72-61a6bbaa58cf.png) 我们可以看到在调用ZipInputStream的read方法时,因为该方法没有被标记为热点方法,同时又“太大了”,导致无法被内联到。使用-XX:CompileCommand中inline指令可以强制方法进行内联,不过还是建议谨慎使用,除非确定某个方法内联会带来不少的性能提升,否则不建议使用,并且过多使用对编译线程和codeCache都会带来不小的压力。 diff --git a/docs/jvm/neicun-jiegou.md b/docs/jvm/neicun-jiegou.md index caced0769..8d2ed5865 100644 --- a/docs/jvm/neicun-jiegou.md +++ b/docs/jvm/neicun-jiegou.md @@ -11,7 +11,7 @@ tag: 在谈 JVM 内存区域划分之前,我们先来看一下 Java 程序的具体执行过程,我画了一幅图。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/neicun-jiegou-dac0f4c1-8a7e-4309-a599-5664cdaf5016.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/neicun-jiegou-dac0f4c1-8a7e-4309-a599-5664cdaf5016.png) Java 源代码文件经过编译器编译后生成字节码文件,然后交给 JVM 的类加载器,加载完毕后,交给执行引擎执行。在整个执行的过程中,JVM 会用一块空间来存储程序执行期间需要用到的数据,这块空间一般被称为运行时数据区,也就是常说的 JVM 内存。 @@ -29,7 +29,7 @@ Java 源代码文件经过编译器编译后生成字节码文件,然后交给 根据第二章 Java 虚拟机结构中的规定,运行时数据区可以分为以下几个部分,见下图。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/neicun-jiegou-e33179f3-275b-44c9-87f6-802198f8f360.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/neicun-jiegou-e33179f3-275b-44c9-87f6-802198f8f360.png) ### 01、程序计数器 @@ -52,7 +52,7 @@ Java 虚拟机栈中是一个个栈帧,每个栈帧对应一个被调用的方 栈帧包含以下 5 个部分,见下图。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/neicun-jiegou-4ea2a60a-05df-4ed1-8109-99ae23acefd1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/neicun-jiegou-4ea2a60a-05df-4ed1-8109-99ae23acefd1.png) [Java 虚拟机栈](https://tobebetterjavaer.com/jvm/how-jvm-run-zijiema-zhiling.md) diff --git a/docs/jvm/oom.md b/docs/jvm/oom.md index e8f0b7ef4..811016b3d 100644 --- a/docs/jvm/oom.md +++ b/docs/jvm/oom.md @@ -32,13 +32,13 @@ tag: 于是我们想根据运维之前收集到的内存数据、GC 日志尝试判断哪里出现问题。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/oom-81051388-0c35-4de6-a3d9-4f546ef4bfec.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/oom-81051388-0c35-4de6-a3d9-4f546ef4bfec.jpg) 结果发现老年代的内存使用就算是发生 GC 也一直居高不下,而且随着时间推移也越来越高。 结合 jstat 的日志发现就算是发生了 FGC 老年代也已经回收不了,内存已经到顶。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/oom-e79d4da0-fbb1-4918-a8d8-e29d2d64323b.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/oom-e79d4da0-fbb1-4918-a8d8-e29d2d64323b.jpg) 甚至有几台应用 FGC 达到了上百次,时间也高的可怕。 @@ -61,7 +61,7 @@ tag: 结果跑了 10 几分钟内存使用并没有什么问题。根据图中可以看出,每产生一次 GC 内存都能有效的回收,所以这样并没有复现问题。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/oom-4cf05af0-924f-406b-a8a4-5aa885e38cea.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/oom-4cf05af0-924f-406b-a8a4-5aa885e38cea.jpg) 没法复现问题就很难定位了。于是我们 review 代码,发现生产的逻辑和我们用 while 循环 Mock 数据还不太一样。 @@ -72,7 +72,7 @@ tag: 果然不出意外只跑了一分多钟内存就顶不住了,观察左图发现 GC 的频次非常高,但是内存的回收却是相形见拙。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/oom-a6d6c9cd-e79c-4a76-ba97-032cfefefd5f.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/oom-a6d6c9cd-e79c-4a76-ba97-032cfefefd5f.jpg) 同时后台也开始打印内存溢出了,这样便复现出问题。 @@ -82,7 +82,7 @@ tag: 于是便想看看到底是什么对象占用了这么多的内存,利用 VisualVM 的 HeapDump 功能可以立即 dump 出当前应用的内存情况。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/oom-49b47ca3-b3e2-49f7-85c9-23f7a3ef6f93.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/oom-49b47ca3-b3e2-49f7-85c9-23f7a3ef6f93.jpg) 结果发现 `com.lmax.disruptor.RingBuffer` 类型的对象占用了将近 50% 的内存。 @@ -98,7 +98,7 @@ tag: 我也做了一个实验,证明确实如此。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/oom-dee49da6-905a-4085-b82e-41e136d422e8.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/oom-dee49da6-905a-4085-b82e-41e136d422e8.jpg) 我设置队列大小为 8 ,从 0~9 往里面写 10 条数据,当写到 8 的时候就会把之前 0 的位置覆盖掉,后面的以此类推(类似于 HashMap 的取模定位)。 @@ -112,7 +112,7 @@ tag: 同样的 128M 内存,也是通过 Kafka 一直源源不断的取出数据。通过监控如下: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/oom-5529781f-1f68-47a7-a3d2-04eba9e9d52e.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/oom-5529781f-1f68-47a7-a3d2-04eba9e9d52e.jpg) 跑了 20 几分钟系统一切正常,每当一次 GC 都能回收大部分内存,最终呈现锯齿状。 diff --git a/docs/jvm/problem-tools.md b/docs/jvm/problem-tools.md index d67dbbf68..7e3ac9ad0 100644 --- a/docs/jvm/problem-tools.md +++ b/docs/jvm/problem-tools.md @@ -13,7 +13,7 @@ tag: 在JDK的bin目录下有很多命令行工具: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-547b1b2c-9fb4-4d1d-9c72-013ec210f6a5.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-547b1b2c-9fb4-4d1d-9c72-013ec210f6a5.jpg)   我们可以看到各个工具的大小基本上都稳定在27kb左右,这个不是JDK开发团队刻意为之的,而是因为这些工具大多数是 `jdk\lib\tools.jar` 类库的一层薄包装而已,他们的主要功能代码是在tools类库中实现的。 @@ -23,7 +23,7 @@ tag: 这里主要介绍如下几个工具: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-01.png) 1、jps:查看本机java进程信息 @@ -53,21 +53,21 @@ JAVA Dump就是虚拟机运行时的快照,将虚拟机运行时的状态和 显示当前所有java进程pid的命令,我们可以通过这个命令来查看到底启动了几个java进程(因为每一个java程序都会独占一个java虚拟机实例),不过jps有个缺点是只能显示当前用户的进程id,要显示其他用户的还只能用linux的ps命令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-2017daf6-832a-4673-b776-ad3380e47402.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-2017daf6-832a-4673-b776-ad3380e47402.png) 执行jps命令,会列出所有正在运行的java进程,其中jps命令也是一个java程序。前面的数字就是进程的id,这个id的作用非常大,后面会有相关介绍。 **jps -help:** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-031be661-e47e-44f0-9e33-34368b187662.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-031be661-e47e-44f0-9e33-34368b187662.png) **jps -l** 输出应用程序main.class的完整package名或者应用程序jar文件完整路径名 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-0ccc96dc-8053-4222-9824-b116f02776a4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-0ccc96dc-8053-4222-9824-b116f02776a4.png) **jps -v** 输出传递给JVM的参数 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-059a3285-4a01-4f7a-a6ed-1cc5dcbf3f18.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-059a3285-4a01-4f7a-a6ed-1cc5dcbf3f18.png) **jps失效** @@ -90,7 +90,7 @@ java程序启动后,会在目录/tmp/hsperfdata_{userName}/下生成几个文 主要用于生成指定进程当前时刻的线程快照,线程快照是当前java虚拟机每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是用于定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致长时间等待。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-e80d0925-2dcf-4204-b46d-47312df2a673.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-e80d0925-2dcf-4204-b46d-47312df2a673.png) **3、jmap** @@ -100,21 +100,21 @@ java程序启动后,会在目录/tmp/hsperfdata_{userName}/下生成几个文 jmap的用法摘要: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-96a70bab-5cee-4068-8ccb-1d35124abeea.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-96a70bab-5cee-4068-8ccb-1d35124abeea.png) **1、`jmap pid`** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-38d5c9da-e433-43d2-b1bc-3f3634e05497.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-38d5c9da-e433-43d2-b1bc-3f3634e05497.png) 打印的信息分别为:共享对象的起始地址、映射大小、共享对象路径的全程。 **2、`jmap -heap pid`:查看堆使用情况** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-75acf4c8-393d-43d1-b208-04de1f0ba6bd.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-75acf4c8-393d-43d1-b208-04de1f0ba6bd.png) **3、`jmap -histo pid`:查看堆中对象数量和大小** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-5e42fe47-e1e6-4649-acb5-e17bd277a771.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-5e42fe47-e1e6-4649-acb5-e17bd277a771.png) 打印的信息分别是:序列号、对象的数量、这些对象的内存占用大小、这些对象所属的类的全限定名 @@ -144,7 +144,7 @@ count:打印次数 **1、jstat -gc PID 5000 20** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-3f71397d-3ff6-430d-adf4-ff5ab9f111d5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-3f71397d-3ff6-430d-adf4-ff5ab9f111d5.png) S0C:年轻代第一个survivor的容量(字节) @@ -176,7 +176,7 @@ FGCT:从应用程序启动到采样时老年代中GC所使用的时间(单 **2、jstat -gcutil PID 5000 20** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-c2a84c1d-e853-482a-88a5-27ef39da66a0.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-c2a84c1d-e853-482a-88a5-27ef39da66a0.png) s0:年轻代中第一个survivor已使用的占当前容量百分比 @@ -196,7 +196,7 @@ P:永久代中已使用的占当前容量百分比 **jhat heapdump** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-fd76ac30-53a5-4549-8206-18283f330758.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-fd76ac30-53a5-4549-8206-18283f330758.png) 这个命令将heapdump文件转换成html格式,并且启动一个http服务,默认端口为7000。 @@ -204,7 +204,7 @@ P:永久代中已使用的占当前容量百分比 下面我们来访问下:ip:port -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-059e61f1-8263-4ee0-b36b-f117ecaf0a07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-059e61f1-8263-4ee0-b36b-f117ecaf0a07.png) ## 6、jinfo @@ -222,15 +222,15 @@ jinfo可以用来查看正在运行的java运用程序的扩展参数,甚至 下面的命令显示了新生代对象晋升到老年代对象的最大年龄。在运行程序运行时并没有指定这个参数,但是通过jinfo,可以查看这个参数的当前的值。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-f37517b7-20b4-4243-ae03-d41126ae43e5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-f37517b7-20b4-4243-ae03-d41126ae43e5.png) 下面的命令显示是否打印gc详细信息: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-86c5ace2-7377-4d5a-a780-0a194e14c9a0.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-86c5ace2-7377-4d5a-a780-0a194e14c9a0.png) 下面的命令在运用程序运行时动态打开打印详细gc信息开关: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-d258d260-65eb-48f9-8585-6bed74de5a47.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-d258d260-65eb-48f9-8585-6bed74de5a47.png) 注意事项:jinfo虽然可以在java程序运行时动态地修改虚拟机参数,但并不是所有的参数都支持动态修改。 @@ -239,11 +239,11 @@ jinfo可以用来查看正在运行的java运用程序的扩展参数,甚至 在JDK 1.7之后,新增了一个命令行工具jcmd。它是一个多功能工具,可以用来导出堆,查看java进程,导出线程信息,执行GC等。jcmd拥有jmap的大部分功能,Oracle官方建议使用jcmd代替jmap。 使用 jcmd -l 命令列出当前运行的所有虚拟机,示例: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-4fa6915b-d39c-4d6d-a6e7-edc989cac76f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-4fa6915b-d39c-4d6d-a6e7-edc989cac76f.png) 针对每一个虚拟机,可以使用help命令列出该虚拟机支持的所有命令,示例: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-219b7cac-c9a9-4d47-8ecf-93a4a04fc1db.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-219b7cac-c9a9-4d47-8ecf-93a4a04fc1db.png) 子命令含义: @@ -267,7 +267,7 @@ jinfo可以用来查看正在运行的java运用程序的扩展参数,甚至 示例: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-b0742677-4ad0-4fd3-b985-054238af8865.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-b0742677-4ad0-4fd3-b985-054238af8865.png) ## 8、可视化监控工具(JConsole、JVisualVM) @@ -285,12 +285,12 @@ JConsole(Java Monitoring and Management Console)是一款基于 JMX(Java M 打开位于 bin 目录下的 `jconsole` 程序后,它会自动扫描当前主机上的所有 JVM 进程: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-6b614bd9-5e75-48e0-b51e-50cbd33669a5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-6b614bd9-5e75-48e0-b51e-50cbd33669a5.png) 选中需要监控的进程后,点击连接,即可进入监控界面。监控界面包含了 *概览*、*内存*、*线程*、*类*、*VM 概要*、*MBean* 六个选项卡。其中概览界面显示的是 *内存*、*线程*、*类* 等三个选项卡界面的概览信息,如下所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-10f3df05-e209-4bca-a8dc-99668a2d8e07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-10f3df05-e209-4bca-a8dc-99668a2d8e07.png) @@ -299,7 +299,7 @@ JConsole(Java Monitoring and Management Console)是一款基于 JMX(Java M -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-ddabe66e-18ac-4cb6-9e9e-f446645a4501.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-ddabe66e-18ac-4cb6-9e9e-f446645a4501.png) @@ -308,14 +308,14 @@ JConsole(Java Monitoring and Management Console)是一款基于 JMX(Java M -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-a97902be-6084-4009-81b2-cbe08d60a617.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-a97902be-6084-4009-81b2-cbe08d60a617.png) 点击死锁选项卡则可以看到造成死锁的线程: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-a76f6714-0efd-4208-a203-9264bc9963d9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-a76f6714-0efd-4208-a203-9264bc9963d9.png) @@ -324,7 +324,7 @@ JConsole(Java Monitoring and Management Console)是一款基于 JMX(Java M -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-519acfd8-943e-4005-b1af-9de1e4187971.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-519acfd8-943e-4005-b1af-9de1e4187971.png) @@ -345,7 +345,7 @@ VisualVM(All-in-One Java Troubleshooting Tool)是 Oracle 提供的功能最 打开位于 bin 目录下的 `jvisualvm` 程序, 它会自动扫描当前主机上的所有 JVM 进程: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-221c1e6e-bcfd-4bf3-be85-6172a3f72962.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-221c1e6e-bcfd-4bf3-be85-6172a3f72962.png) @@ -354,7 +354,7 @@ VisualVM(All-in-One Java Troubleshooting Tool)是 Oracle 提供的功能最 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-0e0a833a-d13c-4b70-b7ee-c58651a58185.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-0e0a833a-d13c-4b70-b7ee-c58651a58185.png) @@ -365,7 +365,7 @@ VisualVM(All-in-One Java Troubleshooting Tool)是 Oracle 提供的功能最 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-afaf433c-6ae7-4c4b-b686-48504cd4c3e9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-afaf433c-6ae7-4c4b-b686-48504cd4c3e9.png) @@ -374,7 +374,7 @@ VisualVM(All-in-One Java Troubleshooting Tool)是 Oracle 提供的功能最 在线程界面可以查看所有线程的状态,如果出现死锁,该界面还会进行提示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-9dbc6b53-c9e6-4051-845f-ef2d848b5d60.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-9dbc6b53-c9e6-4051-845f-ef2d848b5d60.png) @@ -383,7 +383,7 @@ VisualVM(All-in-One Java Troubleshooting Tool)是 Oracle 提供的功能最 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-74941c88-d009-4d7f-8264-efc7d94c94ee.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-74941c88-d009-4d7f-8264-efc7d94c94ee.png) @@ -392,7 +392,7 @@ VisualVM(All-in-One Java Troubleshooting Tool)是 Oracle 提供的功能最 在 Profiler 界面,可以进行 CPU 和 内存的性能分析。要开始性能分析,需要先选择 **CPU** 或 **内存** 按钮中的一个,VisualVM 将会开始记录应用程序执行过的所有方法:如果是进行的是 CPU 执行时间分析,将会统计每个方法的执行次数、执行耗时;如果是内存分析,则会统计每个方法关联的对象数以及这些对象所占的空间。想要结束性能分析,点击停止按钮即可: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-946d7f3e-9519-4a0b-8905-0bf2c1d83fcb.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-946d7f3e-9519-4a0b-8905-0bf2c1d83fcb.png) @@ -401,7 +401,7 @@ VisualVM(All-in-One Java Troubleshooting Tool)是 Oracle 提供的功能最 Visual GC 面板默认是不显示的,需要通过插件进行扩展。它会实时监控虚拟机的状态,在功能上类似于 jstat 命令: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-b6f23234-8b1d-44df-8b12-e723dc0d1903.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-b6f23234-8b1d-44df-8b12-e723dc0d1903.png) @@ -410,7 +410,7 @@ Visual GC 面板默认是不显示的,需要通过插件进行扩展。它会 在主界面,点击 **工具 => 插件** ,可以打开插件面板。右击插件选项或者点击安装按钮即可完成对应插件的安装: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-4518bc2f-ef1f-4ed6-8da9-47a0fdc03338.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-4518bc2f-ef1f-4ed6-8da9-47a0fdc03338.png) @@ -419,7 +419,7 @@ Visual GC 面板默认是不显示的,需要通过插件进行扩展。它会 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-146f715c-a902-4725-9101-07d608a04770.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-146f715c-a902-4725-9101-07d608a04770.png) @@ -428,7 +428,7 @@ Visual GC 面板默认是不显示的,需要通过插件进行扩展。它会 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-55919d48-88f4-4ee5-842f-3ed20b9f7cd6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-55919d48-88f4-4ee5-842f-3ed20b9f7cd6.png) @@ -486,7 +486,7 @@ chown root:root /usr/local/jmxremote.password 之后在使用 VisualVM 进行远程连接时,配置如下: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-f84f1b0a-3ff7-444f-8285-709a234ce670.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-f84f1b0a-3ff7-444f-8285-709a234ce670.png) 需要注意的是这里的端口号是配置的 `Dcom.sun.management.jmxremote.port` 的值,而不是 Java 程序的端口号。连接完成后,即可查看到对应进程的监控状态。 @@ -536,7 +536,7 @@ strace:跟踪程序运行过程发起的系统调用 https://fastthread.io:线程栈分析的网站 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/problem-tools-6d57b323-9665-4453-9fee-ea3111ad8629.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/problem-tools-6d57b323-9665-4453-9fee-ea3111ad8629.png) ## 上问题排查思路(八股) diff --git a/docs/jvm/tujie-gc.md b/docs/jvm/tujie-gc.md index ee5b3c73a..c49f92c15 100644 --- a/docs/jvm/tujie-gc.md +++ b/docs/jvm/tujie-gc.md @@ -17,27 +17,27 @@ Java 语言出来之前,大家都在拼命的写 C 或者 C++ 的程序,此 垃圾回收的第一步是标记。垃圾回收器此时会找出内存哪些在使用中,哪些不是。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/tujie-gc-9858785a-c6aa-4d6d-a6cd-640d24dd27d0.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/tujie-gc-9858785a-c6aa-4d6d-a6cd-640d24dd27d0.png) 上图中,蓝色表示已引用对象,橙色表示未引用对象。垃圾回收器要检查完所有的对象,才能知道哪些有被引用,哪些没。如果系统里所有的对象都要检查,那这一步可能会相当耗时间。 垃圾回收的第二步是清除,这一步会删掉标记出的未引用对象。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/tujie-gc-768f5a2c-6c81-4f76-b847-a41cc8413228.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/tujie-gc-768f5a2c-6c81-4f76-b847-a41cc8413228.png) 内存分配器会保留指向可用内存中的引用,以分配给新的对象。 垃圾回收的第三步是压缩,为了提升性能,删除了未引用对象后,还可以将剩下的已引用对象放在一起(压缩),这样就能更简单快捷地分配新对象了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/tujie-gc-989889b6-adb4-4277-8c67-73d76658f744.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/tujie-gc-989889b6-adb4-4277-8c67-73d76658f744.png) 之前提到过,逐一标记和压缩  Java 虚拟机中的所有对象非常低效:分配的对象越多,垃圾回收需要的时间就越久。不过,根据统计,大部分的对象,其实用没多久就不用了。 来看个例子吧。下图中,竖轴代表已分配的字节,而横轴代表程序的运行时间。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/tujie-gc-24b154be-4ad0-4cc7-87e9-a3035bc9e3c5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/tujie-gc-24b154be-4ad0-4cc7-87e9-a3035bc9e3c5.png) 可见,存活(没被释放)的对象随着运行时间越来越少。图中左侧的峰值,也表明了大部分对象其实都挺短命的。 @@ -48,7 +48,7 @@ Java 语言出来之前,大家都在拼命的写 C 或者 C++ 的程序,此 根据之前的规律,就可以用来提升 JVM 的效率了。方法是,把堆分成几个部分(就是所谓的分代),分别是新生代、老年代,以及永生代。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/tujie-gc-590c5011-48c4-4543-bd26-6f14c2b8614b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/tujie-gc-590c5011-48c4-4543-bd26-6f14c2b8614b.png) 新对象会被分配在新生代内存。一旦新生代内存满了,就会开始对死掉的对象,进行所谓的小型垃圾回收(Minor GC)过程。一片新生代内存里,死掉的越多,回收过程就越快;至于那些还活着的对象,此时就会老化,并最终老到进入老年代内存。 @@ -64,37 +64,37 @@ Major GC 也会触发STW(Stop the World)。通常,Major GC会慢很多, 首先,将任何新对象分配给 eden 空间。 两个 survivor 空间都是空的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/tujie-gc-efe9657b-c7a6-48a8-9037-0e709b1d236c) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/tujie-gc-efe9657b-c7a6-48a8-9037-0e709b1d236c) 当 eden 空间填满时,会触发轻微的垃圾收集。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/tujie-gc-2497947b-92b5-4a7c-9399-1909a3153660) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/tujie-gc-2497947b-92b5-4a7c-9399-1909a3153660) 引用的对象被移动到第一个 survivor 空间。 清除 eden 空间时,将删除未引用的对象。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/tujie-gc-2b431315-26fa-4ea0-843a-c63ca568f960) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/tujie-gc-2b431315-26fa-4ea0-843a-c63ca568f960) 在下一次Minor GC中,Eden区也会做同样的操作。删除未被引用的对象,并将被引用的对象移动到Survivor区。然而,这里,他们被移动到了第二个Survivor区(S1)。 此外,第一个Survivor区(S0)中,在上一次Minor GC幸存的对象,会增加年龄,并被移动到S1中。待所有幸存对象都被移动到S1后,S0和Eden区都会被清空。注意,Survivor区中有了不同年龄的对象。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/tujie-gc-e2560f59-9b24-4d16-88db-b6ac4d0b6ffe) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/tujie-gc-e2560f59-9b24-4d16-88db-b6ac4d0b6ffe) 在下一次Minor GC中,会重复同样的操作。不过,这一次Survivor区会交换。被引用的对象移动到S0,。幸存的对象增加年龄。Eden区和S1被清空。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/tujie-gc-aa9f883a-12db-4c8b-8391-3c289b53d804) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/tujie-gc-aa9f883a-12db-4c8b-8391-3c289b53d804)  此幻灯片演示了 promotion。 在较小的GC之后,当老化的物体达到一定的年龄阈值(在该示例中为8)时,它们从年轻一代晋升到老一代。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/tujie-gc-dec96816-2912-4127-aaaa-a4d987123f52) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/tujie-gc-dec96816-2912-4127-aaaa-a4d987123f52) 随着较小的GC持续发生,物体将继续被推广到老一代空间。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/tujie-gc-6cb31f8a-2eac-489c-88bd-fc643996ab49) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/tujie-gc-6cb31f8a-2eac-489c-88bd-fc643996ab49) 所以这几乎涵盖了年轻一代的整个过程。 最终,将主要对老一代进行GC,清理并最终压缩该空间。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/tujie-gc-df98a004-e233-4fb5-a31a-f422033ecfa7) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/tujie-gc-df98a004-e233-4fb5-a31a-f422033ecfa7) -------- @@ -102,7 +102,7 @@ Major GC 也会触发STW(Stop the World)。通常,Major GC会慢很多, Java 堆(Java Heap)是 JVM 所管理的内存中最大的一块,堆又是垃圾收集器管理的主要区域,这里我们主要分析一下 Java 堆的结构。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/tujie-gc-294701a5-1c50-4112-94a1-96a8bab80e34.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/tujie-gc-294701a5-1c50-4112-94a1-96a8bab80e34.png) Java 堆主要分为 2 个区域-年轻代与老年代,其中年轻代又分 Eden 区和 Survivor 区,其中 Survivor 区又分 From 和 To 2 个区。可能这时候大家会有疑问,为什么需要 Survivor 区,为什么 Survivor 还要分 2 个区。 diff --git a/docs/jvm/what-happen-when-javac.md b/docs/jvm/what-happen-when-javac.md index 2a126eddf..d7215d165 100644 --- a/docs/jvm/what-happen-when-javac.md +++ b/docs/jvm/what-happen-when-javac.md @@ -28,17 +28,17 @@ public class HelloWorld { 点击 IDEA 工具栏中的锤子按钮(Build Project,编译整个项目),如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/five-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/five-01.png) 这时候,就可以在 src 的同级目录 target 下找到一个名为 HelloWorld.class 的文件。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/five-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/five-02.png) 如果找不到的话,在目录上右键选择「Reload from Disk,从磁盘上重新加载」,如下图所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/five-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/five-03.png) 可以双击打开它。 @@ -103,34 +103,34 @@ public class com/itwanger/five/HelloWorld { 那这个字节码文件是怎么看到的呢?可以通过 IDEA 菜单栏中的「View」→「Show Bytecode」查看,如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/five-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/five-04.png) PS:字节码并不是机器码,操作系统无法直接识别,需要在操作系统上安装不同版本的 Java 虚拟机(JVM)来识别。通常情况下,我们只需要安装不同版本的 JDK(Java Development Kit,Java 开发工具包)就行了,它里面包含了 JRE(Java Runtime Environment,Java 运行时环境),而 JRE 又包含了 JVM。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/five-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/five-05.png) Windows、Linux、MacOS 等操作系统都有相应的 JDK,只要安装好了 JDK 就有了 Java 语言的运行时环境,就可以把一份字节码文件在不同的平台上运行了。可以在 [Oracle 官网](https://www.oracle.com/java/technologies/javase-jdk11-downloads.html)上下载不同版本的 JDK。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/five-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/five-06.png) PPS:为什么要查看字节码呢?查看字节码文件更容易让我们搞懂 Java 代码背后的原理,比如搞懂 Java 中的各种语法糖的本质。 相比于 IDEA 自带的「Show Bytecode」功能,我更推荐 `jclasslib` 这款插件,可以在插件市场中安装(我已经安装过了)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/five-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/five-07.png) 安装完成之后,点击 View -> Show Bytecode With jclasslib 即可通过 jclasslib 查看字节码文件了(点击之前,光标要停留在对应的类文件上),如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/five-08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/five-08.png) 使用 jclasslib 不仅可以直观地查看类对应的字节码文件,还可以查看类的基本信息、常量池、接口、字段、方法等信息,如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/five-09.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/five-09.png) 也就是说,在编译阶段,Java 会将 Java 源代码文件编译为字节码文件。在这个阶段,编译器会进行一些检查工作,比如说,某个关键字是不是写错了,语法上是不是符合预期了,不能有很明显的错误,否则带到运行时再检查出来就会比较麻烦了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/five-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/five-10.png) \ No newline at end of file diff --git a/docs/jvm/what-is-jvm.md b/docs/jvm/what-is-jvm.md index 16917075a..b4d87d91d 100644 --- a/docs/jvm/what-is-jvm.md +++ b/docs/jvm/what-is-jvm.md @@ -17,7 +17,7 @@ tag: 一开始,项目组打算使用 C++,但 C++ 无法达到跨平台的要求,比如在 Windows 系统下编译的 Hello.exe 无法直接拿到 Linux 环境下执行。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/seven-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/seven-01.png) 在当时,C++ 已经非常流行了,但无法跨平台,只能忍痛割爱了。 @@ -25,7 +25,7 @@ tag: 三妹不知道有没有听过直译器(解释器)这玩意?(估计你没听过)就是每跑一行代码就生成机器码,然后执行,比如说 Python 和 Ruby 用的就是直译器。在每个操作系统上装一个直译器就好了,跨平台的目的就达到了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/seven-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/seven-02.png) 但直译器有个缺点,就是没法像编译器那样对一些热点代码进行优化,从而让机器码跑得更快一些。 @@ -33,7 +33,7 @@ tag: 来个结合体呗,编译器和直译器一块上! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/seven-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/seven-03.png) 编译器负责把 Java 源代码编译成字节码(不清楚的小伙伴可以点击链接查看[上一节](https://mp.weixin.qq.com/s/GYDFndO0Q1Nqzcc_Te61gw)),Java 虚拟机(Java Virtual Machine,简称 JVM) 负责把字节码转换成机器码。转换的时候,可以做一些压缩或者优化,这样的机器码跑起来就快多了。 @@ -49,7 +49,7 @@ tag: 说到这,三妹是不是想问,“都有哪些 Java 虚拟机呢?”来看下面这张思维导图: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/seven-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/seven-04.png) 除了我们经常看到,经常听到的 Hotspot VM,还有很多,下面我来简单介绍一下。 @@ -85,7 +85,7 @@ HotSpot 的技术优势就在于热点代码探测技术(名字就从这来) 关键是,1997 年 10 月,Sun 公司因为这事把微软告了,最后微软赔给了 Sun 公司 2000 万美金,并且终止了在 Java 虚拟机方面的发展。如果,我是说如果,如果微软保持着对 Java 的热情,后面还有 .Net 什么事? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/seven-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/seven-05.png) 解释了这么多 Java 虚拟机后,三妹是不是想问,“Java 虚拟机长什么样子呢?” @@ -95,7 +95,7 @@ Java 虚拟机虽然是虚拟的,但它的内部是可以划分为: - 运行时数据区(Runtime Data Areas) - 执行引擎(Excution Engine) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/seven-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/seven-06.png) **1)类加载器** @@ -143,7 +143,7 @@ jdk.internal.loader.ClassLoaders$PlatformClassLoader@2d209079 来看下面这张图: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/seven-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/seven-07.png) - PC寄存器(PC Register),也叫程序计数器(Program Counter Register),是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的信号指示器。 diff --git a/docs/jvm/whereis-the-object.md b/docs/jvm/whereis-the-object.md index 36c2904e1..8deb10e06 100644 --- a/docs/jvm/whereis-the-object.md +++ b/docs/jvm/whereis-the-object.md @@ -18,28 +18,28 @@ tag: 大部分我们创建的对象,都属于生命周期比较短的,所以会存放在新生代。新生代又细分 Eden 空间、From Survivor 空间、To Survivor 空间,我们创建的对象优先在 Eden 分配。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/whereis-the-object-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/whereis-the-object-1.png) 随着对象的创建,Eden 剩余内存空间越来越少,就会触发 Minor GC,于是 Eden 的存活对象会放入 From Survivor 空间。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/whereis-the-object-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/whereis-the-object-2.png) Minor GC 后,新对象依然会往 Eden 分配。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/whereis-the-object-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/whereis-the-object-3.png) Eden 剩余内存空间越来越少,又会触发 Minor GC,于是 Eden 和 From Survivor 的存活对象会放入 To Survivor 空间。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/whereis-the-object-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/whereis-the-object-4.png) ### 二、大对象直接进入老年代 在上面的流程中,如果一个对象很大,一直在 Survivor 空间复制来复制去,那很费性能,所以这些大对象直接进入老年代。 可以用 `XX:PretenureSizeThreshold` 来设置这些大对象的阈值。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/whereis-the-object-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/whereis-the-object-5.png) ### 三、长期存活的对象将进入老年代 @@ -48,7 +48,7 @@ Eden 剩余内存空间越来越少,又会触发 Minor GC,于是 Eden 和 Fr 虚拟机为了给对象计算他到底经历了几次 Minor GC,会给每个对象定义了一个对象年龄计数器。如果对象在 Eden 中经过第一次 Minor GC 后仍然存活,移动到 Survivor 空间年龄加 1,在 Survivor 区中每经历过 Minor GC 后仍然存活年龄再加 1。年龄到了 15,就到了老年代。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/whereis-the-object-6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/whereis-the-object-6.png) @@ -57,7 +57,7 @@ Eden 剩余内存空间越来越少,又会触发 Minor GC,于是 Eden 和 Fr 比如 Survivor 是 100M,Hello1 和 Hello2 都是 3 岁,且总和超过了 50M,Hello3 是 4 岁,这个时候,这三个对象都将到老年代。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/whereis-the-object-7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/whereis-the-object-7.png) ### 五、空间分配担保 上面的流程提过,存活的对象都会放入另外一个 Survivor 空间,如果这些存活的对象比 Survivor 空间还大呢?整个流程如下: @@ -68,7 +68,7 @@ Eden 剩余内存空间越来越少,又会触发 Minor GC,于是 Eden 和 Fr - 如果大于,发起 Minor GC。Minor GC 后,看 Survivor 空间是否足够存放存活对象,如果不够,就放入老年代,如果够放,就直接存放 Survivor 空间。如果老年代都不够放存活对象,担保失败(Handle Promotion Failure),发起 full gc。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/whereis-the-object-8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/whereis-the-object-8.png) 好了,今天就分享到这儿吧,我是二哥呀,我们下期见~~ diff --git a/docs/jvm/zijiema-zhiling.md b/docs/jvm/zijiema-zhiling.md index 4f48a6b44..cbe54062f 100644 --- a/docs/jvm/zijiema-zhiling.md +++ b/docs/jvm/zijiema-zhiling.md @@ -39,7 +39,7 @@ Java 字节码由操作码和操作数组成。 x 为操作码助记符,表明是哪一种数据类型。见下表所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zijiema-zhiling-879da2f2-fb72-48a9-985e-5a28a9fc8814.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zijiema-zhiling-879da2f2-fb72-48a9-985e-5a28a9fc8814.png) 像 arraylength 指令,没有操作码助记符,它没有代表数据类型的特殊字符,但操作数只能是一个数组类型的对象。 @@ -55,7 +55,7 @@ private void load(int age, String name, long birthday, boolean sex) { 通过 jclasslib 看一下 `load()` 方法(4 个参数)的字节码指令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zijiema-zhiling-05bfae95-2a33-402c-9041-570093729c42.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zijiema-zhiling-05bfae95-2a33-402c-9041-570093729c42.png) - iload_1:将局部变量表中下标为 1 的 int 变量压入操作数栈中。 - aload_2:将局部变量表中下标为 2 的引用数据类型变量(此时为 String)压入操作数栈中。 @@ -64,7 +64,7 @@ private void load(int age, String name, long birthday, boolean sex) { 通过查看局部变量表就能关联上了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zijiema-zhiling-79d74946-ce9e-41d4-b889-bda861f847bc.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zijiema-zhiling-79d74946-ce9e-41d4-b889-bda861f847bc.png) **2)将常量池中的常量压入操作数栈中** @@ -73,7 +73,7 @@ private void load(int age, String name, long birthday, boolean sex) { **const 系列**,用于特殊的常量入栈,要入栈的常量隐含在指令本身。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zijiema-zhiling-270c314d-872b-43b0-861f-417eafc046fd.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zijiema-zhiling-270c314d-872b-43b0-861f-417eafc046fd.png) **push 系列**,主要包括 bipush 和 sipush,前者接收 8 位整数作为参数,后者接收 16 位整数。 @@ -101,7 +101,7 @@ public void pushConstLdc() { 通过 jclasslib 看一下 `pushConstLdc()` 方法的字节码指令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zijiema-zhiling-b34fc802-18bb-46a1-8d24-de2087c9b6bf.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zijiema-zhiling-b34fc802-18bb-46a1-8d24-de2087c9b6bf.png) - iconst_m1:将 -1 入栈。范围 [-1,5]。 - bipush 127:将 127 入栈。范围 [-128,127]。 @@ -136,14 +136,14 @@ public void store(int age, String name) { 通过 jclasslib 看一下 `store()` 方法的字节码指令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zijiema-zhiling-d955468c-d07d-47cd-b82b-c03ecea8753d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zijiema-zhiling-d955468c-d07d-47cd-b82b-c03ecea8753d.png) - istore_3:从操作数中弹出一个整数,并把它赋值给局部变量表中索引为 3 的变量。 - astore 4:从操作数中弹出一个引用数据类型,并把它赋值给局部变量表中索引为 4 的变量。 通过查看局部变量表就能关联上了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zijiema-zhiling-a08c20cb-c148-47c9-91e2-df37e68989a9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zijiema-zhiling-a08c20cb-c148-47c9-91e2-df37e68989a9.png) ### 02、算术指令 @@ -200,7 +200,7 @@ public void calculate(int age) { 通过 jclasslib 看一下 `calculate()` 方法的字节码指令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zijiema-zhiling-598e4204-fd77-425b-b536-1e001cda8e13.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zijiema-zhiling-598e4204-fd77-425b-b536-1e001cda8e13.png) - iadd,加法 - isub,减法 @@ -238,7 +238,7 @@ public void updown() { 通过 jclasslib 看一下 `updown()` 方法的字节码指令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zijiema-zhiling-0c3e47c6-1e25-4926-a838-20cf146a8993.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zijiema-zhiling-0c3e47c6-1e25-4926-a838-20cf146a8993.png) - i2d,int 宽化为 double - f2l, float 窄化为 long @@ -269,7 +269,7 @@ public void newObject() { 通过 jclasslib 看一下 `newObject()` 方法的字节码指令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zijiema-zhiling-8125da3d-876c-43fe-8347-cb2341408088.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zijiema-zhiling-8125da3d-876c-43fe-8347-cb2341408088.png) - `new #13 `,创建一个 String 对象。 @@ -304,7 +304,7 @@ public class Writer { 通过 jclasslib 看一下 `main()` 方法的字节码指令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zijiema-zhiling-70441cfc-7c6e-4a5e-b0dd-818fc3fa1a67.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zijiema-zhiling-70441cfc-7c6e-4a5e-b0dd-818fc3fa1a67.png) - `getstatic #2 `,访问静态变量 mark - `getfield #6 `,访问成员变量 name @@ -428,7 +428,7 @@ invokestatic #11 // Method print:()V 方法返回指令根据方法的返回值类型进行区分,常见的返回指令见下图。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zijiema-zhiling-37513fa2-fdba-45db-adfc-c18225c6ff8b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zijiema-zhiling-37513fa2-fdba-45db-adfc-c18225c6ff8b.png) ### 06、操作数栈管理指令 @@ -453,7 +453,7 @@ public class Dup { 通过 jclasslib 看一下 `incAndGet()` 方法的字节码指令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zijiema-zhiling-642ca54e-5808-428d-9840-ebf478e95c17.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zijiema-zhiling-642ca54e-5808-428d-9840-ebf478e95c17.png) - aload_0:将 this 入栈。 - dup:复制栈顶的 this。 @@ -490,13 +490,13 @@ public void lcmp(long a, long b) { 通过 jclasslib 看一下 `lcmp()` 方法的字节码指令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zijiema-zhiling-e8fa6685-b3d4-4f42-8fc5-8a4d8a9efe7b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zijiema-zhiling-e8fa6685-b3d4-4f42-8fc5-8a4d8a9efe7b.png) lcmp 用于两个 long 型的数据进行比较。 **2)条件跳转指令** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zijiema-zhiling-5de34f26-52ad-4e07-a20d-91ea92038984.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zijiema-zhiling-5de34f26-52ad-4e07-a20d-91ea92038984.png) 这些指令都会接收两个字节的操作数,它们的统一含义是,弹出栈顶元素,测试它是否满足某一条件,满足的话,跳转到对应位置。 @@ -519,13 +519,13 @@ public void fi() { 通过 jclasslib 看一下 `fi()` 方法的字节码指令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zijiema-zhiling-d0561d5c-ae21-48e7-9e7c-4aae87d02f56.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zijiema-zhiling-d0561d5c-ae21-48e7-9e7c-4aae87d02f56.png) `3 ifne 12 (+9)` 的意思是,如果栈顶的元素不等于 0,跳转到第 12(3+9)行 `12 bipush 20`。 **3)比较条件转指令** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zijiema-zhiling-bfab6edd-d63f-45a7-8838-997e7630fa2a.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zijiema-zhiling-bfab6edd-d63f-45a7-8838-997e7630fa2a.png) 前缀“if_”后,以字符“i”开头的指令针对 int 型整数进行操作,以字符“a”开头的指令表示对象的比较。 @@ -541,7 +541,7 @@ public void compare() { 通过 jclasslib 看一下 `compare()` 方法的字节码指令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zijiema-zhiling-d4f9a680-1364-4af9-9474-c0763c9bc6f7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zijiema-zhiling-d4f9a680-1364-4af9-9474-c0763c9bc6f7.png) `11 if_icmple 18 (+7)` 的意思是,如果栈顶的两个 int 类型的数值比较的话,如果前者小于后者时跳转到第 18 行(11+7)。 @@ -571,7 +571,7 @@ public void switchTest(int select) { 通过 jclasslib 看一下 `switchTest()` 方法的字节码指令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zijiema-zhiling-04e166ae-13c7-4025-804a-be88e2923a50.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zijiema-zhiling-04e166ae-13c7-4025-804a-be88e2923a50.png) case 2 的时候没有 break,所以 case 2 和 case 3 是连续的,用的是 tableswitch。如果等于 1,跳转到 28 行;如果等于 2 和 3,跳转到 34 行,如果是 default,跳转到 40 行。 diff --git a/docs/jvm/zongjie.md b/docs/jvm/zongjie.md index ac440424d..43bb8340c 100644 --- a/docs/jvm/zongjie.md +++ b/docs/jvm/zongjie.md @@ -45,7 +45,7 @@ Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode) # 使用的是H ## 二、Java 内存区域 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zongjie-1d779fca-b6a4-4982-b746-2a8db7805645.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zongjie-1d779fca-b6a4-4982-b746-2a8db7805645.png) ### 2.1 程序计数器 @@ -89,7 +89,7 @@ Java 堆(Java Heap)是虚拟机所管理的最大一块的内存空间,它 + **指针碰撞**:假设 Java 堆中内存是绝对规整的,所有使用的内存放在一边,所有未被使用的内存放在另外一边,中间以指针作为分界点指示器。此时内存分配只是将指针向空闲方向偏移出对象大小的空间即可,这种方式被称为指针碰撞。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zongjie-afb11e2b-f457-4a19-bb21-f659756061ec.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zongjie-afb11e2b-f457-4a19-bb21-f659756061ec.png) + **空闲列表**:如果 Java 堆不是规整的,此时虚拟机需要维护一个列表,记录哪些内存块是可用的,哪些是不可用的。在进行内存分配时,只需要从该列表中选取出一块足够的内存空间划分给对象实例即可。 @@ -137,12 +137,12 @@ Java 堆(Java Heap)是虚拟机所管理的最大一块的内存空间,它 通过句柄访问对象: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zongjie-f6b5eb22-a5af-40c0-8c80-00fdd6d16b1d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zongjie-f6b5eb22-a5af-40c0-8c80-00fdd6d16b1d.png) 通过直接指针访问对象: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zongjie-f696f4a8-af51-4e28-9d72-c2f6b1e5b3db.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zongjie-f696f4a8-af51-4e28-9d72-c2f6b1e5b3db.png) 句柄访问的优点在于对象移动时(垃圾收集时移动对象是非常普遍的行为)只需要改变句柄中实例数据的指针,而 `reference` 本生并不需要修改;指针访问则反之,由于其 `reference` 中存储的直接就是对象地址,所以当对象移动时, `reference` 需要被修改。但针对只需要访问对象本身的场景,指针访问则可以减少一次定位开销。由于对象访问是一项非常频繁的操作,所以这类减少的效果会非常显著,基于这个原因,HotSpot 主要使用的是指针访问的方式。 @@ -173,7 +173,7 @@ System.gc(); 上面的代码在大多数虚拟机中都能被正确的回收,因为大多数主流的虚拟机都是采用的可达性分析方法来判断对象是否死亡。可达性分析是通过一系列被称为 `GC Roots` 的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径被称为引用链(Reference Chain),如果某个对象到 `GC Roots` 间没有任何引用链相连,这代表 `GC Roots` 到该对象不可达, 此时证明此该对象不可能再被使用。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zongjie-f1325549-5689-4398-a39c-c0a6836f6077.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zongjie-f1325549-5689-4398-a39c-c0a6836f6077.png) 在 Java 语言中,固定可作为 `GC Roots` 的对象包括以下几种: @@ -234,7 +234,7 @@ System.gc(); 它是最基础的垃圾收集算法,收集过程分为两个阶段:首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象;也可以反过来,标记存活对象,统一回收所有未被标记的对象。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zongjie-7d489254-f1e0-4feb-bd4a-af129767a787.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zongjie-7d489254-f1e0-4feb-bd4a-af129767a787.png) 它主要有以下两个缺点: @@ -249,7 +249,7 @@ System.gc(); + 如果内存中多数对象都是存活的,这种算法将产生大量的复制开销; + 浪费内存空间,内存空间变为了原有的一半。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zongjie-f4572b93-f7f3-41cc-9901-93816e79c789.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zongjie-f4572b93-f7f3-41cc-9901-93816e79c789.png) 基于新生代 “朝生夕灭” 的特点,大多数虚拟机都不会按照 1:1 的比例来进行内存划分,例如 HotSpot 虚拟机会将内存空间划分为一块较大的 `Eden` 和 两块较小的 `Survivor` 空间,它们之间的比例是 8:1:1 。 每次分配时只会使用 `Eden` 和其中的一块 `Survivor` ,发生垃圾回收时,只需要将存活的对象一次性复制到另外一块 `Survivor` 上,这样只有 10% 的内存空间会被浪费掉。当 `Survivor` 空间不足以容纳一次 `Minor GC` 时,此时由其他内存区域(通常是老年代)来进行分配担保。 @@ -258,7 +258,7 @@ System.gc(); 标记-整理算法是在标记完成后,让所有存活对象都向内存的一端移动,然后直接清理掉边界以外的内存。其优点在于可以避免内存空间碎片化的问题,也可以充分利用内存空间;其缺点在于根据所使用的收集器的不同,在移动存活对象时可能要全程暂停用户程序: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zongjie-e674c49f-c55b-4eba-95ea-34be62d55a78.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zongjie-e674c49f-c55b-4eba-95ea-34be62d55a78.png) ## 五、经典垃圾收集器 @@ -271,7 +271,7 @@ System.gc(); HotSpot 虚拟机中一共存在七款经典的垃圾收集器: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zongjie-1fa20f99-d203-42d6-982c-f1bd66a0c929.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zongjie-1fa20f99-d203-42d6-982c-f1bd66a0c929.png) > 注:收集器之间存在连线,则代表它们可以搭配使用。 @@ -279,14 +279,14 @@ HotSpot 虚拟机中一共存在七款经典的垃圾收集器: Serial 收集器是最基础、历史最悠久的收集器,它是一个单线程收集器,在进行垃圾回收时,必须暂停其他所有的工作线程,直到收集结束,这是其主要缺点。它的优点在于单线程避免了多线程复杂的上下文切换,因此在单线程环境下收集效率非常高,由于这个优点,迄今为止,其仍然是 HotSpot 虚拟机在客户端模式下默认的新生代收集器: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zongjie-ec6ec994-6fe4-4d5b-890c-7f31b5a607a0.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zongjie-ec6ec994-6fe4-4d5b-890c-7f31b5a607a0.png) ### 5.2 ParNew 收集器 他是 Serial 收集器的多线程版本,可以使用多条线程进行垃圾回收: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zongjie-ae8c47c7-538b-426a-85e4-d422d1c37683.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zongjie-ae8c47c7-538b-426a-85e4-d422d1c37683.png) ### 5.3 Parallel Scavenge 收集器 @@ -305,14 +305,14 @@ Parallel Scavenge 收集器提供两个参数用于精确控制吞吐量: 从名字也可以看出来,它是 Serial 收集器的老年代版本,同样是一个单线程收集器,采用 标记-整理 算法,主要用于给客户端模式下的 HotSpot 虚拟机使用: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zongjie-b199ece2-8de2-4f24-b50a-ea0c0d16bd7b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zongjie-b199ece2-8de2-4f24-b50a-ea0c0d16bd7b.png) ### 5.5 Paralled Old 收集器 Paralled Old 是 Parallel Scavenge 收集器的老年代版本,支持多线程并发收集,采用 标记-整理 算法实现: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zongjie-30dd34d5-27df-4a6f-b391-5a7928dfb3ab.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zongjie-30dd34d5-27df-4a6f-b391-5a7928dfb3ab.png) ### 5.6 CMS 收集器 @@ -324,7 +324,7 @@ CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时 3. **重新标记 (remark)**:采用增量更新算法,对并发标记阶段因为用户线程运行而产生变动的那部分对象进行重新标记,耗时比初始标记稍长且需要暂停用户线程; 4. **并发清除 (inital sweep)**:并发清除掉已经死亡的对象,耗时长但不需要暂停用户线程。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zongjie-7ad8d755-53f2-422a-9cea-c792b0579d8b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zongjie-7ad8d755-53f2-422a-9cea-c792b0579d8b.png) 其优点在于耗时长的 并发标记 和 并发清除 阶段都不需要暂停用户线程,因此其停顿时间较短,其主要缺点如下: @@ -337,7 +337,7 @@ CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时 Garbage First(简称 G1)是一款面向服务端的垃圾收集器,也是 JDK 9 服务端模式下默认的垃圾收集器,它的诞生具有里程碑式的意义。G1 虽然也遵循分代收集理论,但不再以固定大小和固定数量来划分分代区域,而是把连续的 Java 堆划分为多个大小相等的独立区域(Region)。每一个 Region 都可以根据不同的需求来扮演新生代的 `Eden` 空间、`Survivor` 空间或者老年代空间,收集器会根据其扮演角色的不同而采用不同的收集策略。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zongjie-e0f5da26-6e46-4f9d-bfcc-0842cc7079e7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zongjie-e0f5da26-6e46-4f9d-bfcc-0842cc7079e7.png) 上面还有一些 Region 使用 H 进行标注,它代表 Humongous,表示这些 Region 用于存储大对象(humongous object,H-obj),即大小大于等于 region 一半的对象。G1 收集器的运行大致可以分为以下四个步骤: @@ -347,7 +347,7 @@ Garbage First(简称 G1)是一款面向服务端的垃圾收集器,也是 3. **最终标记 (Final Marking)**:对用户线程做一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的少量的 STAB 记录。虽然并发标记阶段会处理 SATB 记录,但由于处理时用户线程依然是运行中的,因此依然会有少量的变动,所以需要最终标记来处理; 4. **筛选回收 (Live Data Counting and Evacuation)**:负责更新 Regin 统计数据,按照各个 Regin 的回收价值和成本进行排序,在根据用户期望的停顿时间进行来指定回收计划,可以选择任意多个 Regin 构成回收集。然后将回收集中 Regin 的存活对象复制到空的 Regin 中,再清理掉整个旧的 Regin 。此时因为涉及到存活对象的移动,所以需要暂停用户线程,并由多个收集线程并行执行。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zongjie-3cf7a78a-d541-49af-929a-4bf8f4f0edd9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zongjie-3cf7a78a-d541-49af-929a-4bf8f4f0edd9.png) ### 5.8 内存分配原则 @@ -380,7 +380,7 @@ Java 虚拟机把描述类的数据从 Class 文件加载到内存,并对数 一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载、验证、准备、卸载、解析、初始化、使用、卸载七个阶段,其中验证、准备、解析三个部分统称为连接: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zongjie-e154964a-9a0a-46de-bf37-75b483956d6c.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zongjie-e154964a-9a0a-46de-bf37-75b483956d6c.png) 《Java 虚拟机规范》严格规定了有且只有六种情况必须立即对类进行初始化: @@ -459,7 +459,7 @@ Java 虚拟机把描述类的数据从 Class 文件加载到内存,并对数 JDK 9 之前的 Java 应用都是由这三种类加载器相互配合来完成加载: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zongjie-c1fcdc37-4e5a-4ed3-94b1-ad4afa2dba7c.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zongjie-c1fcdc37-4e5a-4ed3-94b1-ad4afa2dba7c.png) 上图所示的各种类加载器之间的层次关系被称为类加载器的 “双亲委派模型”,“双亲委派模型” 要求除了顶层的启动类加载器外,其余的类加载器都应该有自己的父类加载器,需要注意的是这里的加载器之间的父子关系一般不是以继承关系来实现的,而是使用组合关系来复用父类加载器的代码。 @@ -474,7 +474,7 @@ JDK 9 之后为了适应模块化的发展,类加载器做了如下变化: + 当平台及应用程序类加载器收到类加载请求时,要首先判断该类是否能够归属到某一个系统模块中,如果可以找到这样的归属关系,就要优先委派给负责那个模块的加载器完成加载; + 启动类加载器、平台类加载器、应用程序类加载器全部继承自 `java.internal.loader.BuiltinClassLoader` ,BuiltinClassLoader 中实现了新的模块化架构下类如何从模块中加载的逻辑,以及模块中资源可访问性的处理。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zongjie-04b18ddb-3457-4e46-ba53-78237d234e37.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zongjie-04b18ddb-3457-4e46-ba53-78237d234e37.png) ## 七、程序编译 @@ -507,7 +507,7 @@ JDK 9 之后为了适应模块化的发展,类加载器做了如下变化: 以上层次并不是固定不变的,根据不同的运行参数和版本,虚拟机可以调整分层的数量。各层次编译之间的交互转换关系如下图所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/jvm/zongjie-2188c350-fdb8-4fee-b2f0-5311795f386b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/jvm/zongjie-2188c350-fdb8-4fee-b2f0-5311795f386b.png) 实施分层编译后,解释器、客户端编译器和服务端编译器就会同时工作,可以用客户端编译器获取更高的编译速度、用服务端编译器来获取更好的编译质量。 diff --git a/docs/kaiyuan/auto-generator.md b/docs/kaiyuan/auto-generator.md index 81aead928..d37046e2b 100644 --- a/docs/kaiyuan/auto-generator.md +++ b/docs/kaiyuan/auto-generator.md @@ -14,7 +14,7 @@ Mybatis Generator 是 MyBatis 官方提供的一个代码生成工具,完全 来个 GIF 感受一下 AutoGenerator 生成代码的快感吧。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/kaiyuan/auto-generator-1.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/kaiyuan/auto-generator-1.gif) ### 一、使用 Mybatis Generator @@ -40,12 +40,12 @@ Mybatis Generator 是 MyBatis 官方提供的一个代码生成工具,完全 添加完成后,一定要执行一次 Maven 重载(见下图),确保 MyBatis 的依赖加载完毕后再执行第二步。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/kaiyuan/auto-generator-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/kaiyuan/auto-generator-2.png) 否则下一步可能不通过,但又得不到任何错误提示。不要问我为什么,踩过坑后痛苦的领悟。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/kaiyuan/auto-generator-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/kaiyuan/auto-generator-3.png) 添加完成后,可以通过 Maven 插件来生成代码,也可以通过 Java 代码来生成代码,这里以 Maven 插件的形式来演示。Java 代码的形式可参照 Mybatis Generator: @@ -53,7 +53,7 @@ Mybatis Generator 是 MyBatis 官方提供的一个代码生成工具,完全 第二步,在 pom.xml 的 MyBatis Generator 插件,先来看一下整体的结构图,注意是在 build→plugins 下节点下添加。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/kaiyuan/auto-generator-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/kaiyuan/auto-generator-4.png) 首先是 MyBatis Generator 插件,目前最新版是 1.4.0,我们采用上一个稳定版本 1.3.7,稳一点。 @@ -69,7 +69,7 @@ Mybatis Generator 是 MyBatis 官方提供的一个代码生成工具,完全 src/main/resources/mybatis-generator-config.xml ``` -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/kaiyuan/auto-generator-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/kaiyuan/auto-generator-5.png) 来看一下 mybatis-generator-config.xml 的内容。 @@ -173,13 +173,13 @@ Mybatis Generator 需要链接数据库,所以还需要添加数据库驱动 配置完成后可以双击运行 Maven 的插件 Mybatis Generator,没有问题的话,可以看到生成后的文件。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/kaiyuan/auto-generator-6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/kaiyuan/auto-generator-6.png) ### 二、使用 MyBatis-Plus 的 AutoGenerator MyBatis-Plus(简写 MP)是 MyBatis 的增强工具,官方宣称 MP 和 MyBatis 的关系就好像魂斗罗中的 1P 和 2P,可谓好基友,天下走。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/kaiyuan/auto-generator-7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/kaiyuan/auto-generator-7.png) AutoGenerator 是 MyBatis-Plus 推出的代码生成器,可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,比 Mybatis Generator 更强大,开发效率更高。 @@ -266,7 +266,7 @@ public class CodeGenerator { 再来看一下运行后的效果,可以看到数据库表对应的 controller、service、entity、mapper 等等全有了——爽歪歪: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/kaiyuan/auto-generator-1.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/kaiyuan/auto-generator-1.gif) ### 三、总结对比 @@ -274,7 +274,7 @@ public class CodeGenerator { **MyBatis-Plus 的确配得上 Plus 啊,确实优秀**。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/kaiyuan/auto-generator-9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/kaiyuan/auto-generator-9.png) 想要完整示例的话,可以到 GitHub 上查看: diff --git a/docs/kaiyuan/yuneban-wangyiyunyinyue.md b/docs/kaiyuan/yuneban-wangyiyunyinyue.md index 29ade9ee5..7492988dc 100644 --- a/docs/kaiyuan/yuneban-wangyiyunyinyue.md +++ b/docs/kaiyuan/yuneban-wangyiyunyinyue.md @@ -11,7 +11,7 @@ tag: 前段时间,有个读者私信我说,**刚学完 Spring Boot,想找点练手项目,准备找实习了。** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/kaiyuan/yuneban-wangyiyunyinyue-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/kaiyuan/yuneban-wangyiyunyinyue-01.png) 二哥这么贴心,对于读者的请求,一向是有求必应,有问必答。那自然得花心思去淘 2 个像样的 Java 练手项目出来了,关键是还要基础,不能太难😆。 @@ -23,7 +23,7 @@ tag: 这是一个带大家从 0 搭建一个 Spring Boot+ Vue 的前后端分离的 Java 项目,前 P71 讲前端,P72 到 P131 讲后端。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/kaiyuan/yuneban-wangyiyunyinyue-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/kaiyuan/yuneban-wangyiyunyinyue-02.png) 前端涉及到的技术有 Vue 全家桶、ElementUI;后端涉及到的技术有 Spring Boot、SpringMVC、MyBatisPlus、SpringSecurity、Swagger、Redis、EasyPOI、RabbitMQ、FasfDFS 等等。 @@ -31,7 +31,7 @@ tag: 为了验证 up 是不是一家培训机构,顺带替大家踩踩坑。我按照要求加了小助理的微信: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/kaiyuan/yuneban-wangyiyunyinyue-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/kaiyuan/yuneban-wangyiyunyinyue-03.png) 加好友时一看头像,和平常偷偷摸摸混进群的广告党差不多,哈哈哈。 @@ -47,11 +47,11 @@ tag: 这是读者提供的一个前后端分离项目,问我项目怎么样,我点开去一看,这不一个号主朋友的嘛。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/kaiyuan/yuneban-wangyiyunyinyue-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/kaiyuan/yuneban-wangyiyunyinyue-04.png) 网站的客户端和管理端使用 VUE 框架来实现的,服务端使用 Spring Boot + MyBatis 来实现,数据库使用了 MySQL。建议至少 1.5 倍速食用。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/kaiyuan/yuneban-wangyiyunyinyue-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/kaiyuan/yuneban-wangyiyunyinyue-05.png) 前后端整体的项目结构也挺清晰的,这是后端的。 @@ -94,9 +94,9 @@ up 也非常的良心,源码都开源到 GitHub 上了。 随便再展示两张项目的效果图吧。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/kaiyuan/yuneban-wangyiyunyinyue-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/kaiyuan/yuneban-wangyiyunyinyue-06.png) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/kaiyuan/yuneban-wangyiyunyinyue-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/kaiyuan/yuneban-wangyiyunyinyue-07.png) 项目的基本功能也很完善: diff --git a/docs/manongshenghuo/yanjiusuo-20wan.md b/docs/manongshenghuo/yanjiusuo-20wan.md index f1569c543..a681b6298 100644 --- a/docs/manongshenghuo/yanjiusuo-20wan.md +++ b/docs/manongshenghuo/yanjiusuo-20wan.md @@ -4,7 +4,7 @@ 之前在一篇[美团还是研究所,美团年包多二十万,怎么选](https://mp.weixin.qq.com/s/oc-6Um6y0LlpSQwDY4HAbw)的文章里做过一个投票:一线互联网大厂还是二三线城市的研究所? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/manongshenghuo/yanjiusuo-20wan-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/manongshenghuo/yanjiusuo-20wan-1.png) 1000 多人的投票结果中,选择研究所的竟然比选择互联网的多一些,看来向往研究所那种能兼顾生活和工作的小伙伴不在少数啊。 @@ -19,7 +19,7 @@ 先发个收入截图吧。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/manongshenghuo/yanjiusuo-20wan-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/manongshenghuo/yanjiusuo-20wan-2.png) 首先声明我这个收入是要低于跟我同年上大学,2014年硕士入所的同学的,我比较熟的技术部门的同学收入加公积金在30左右了,不是很多人想的博士才这么点硕士更少了。我要达到这个数字估计还得两年左右。 @@ -74,6 +74,6 @@ 分享结束,你的选择是互联网 or 研究所? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/manongshenghuo/yanjiusuo-20wan-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/manongshenghuo/yanjiusuo-20wan-3.png) *没有什么使我停留——除了目的,纵然岸旁有玫瑰、有绿荫、有宁静的港湾,我是不系之舟*。 diff --git a/docs/maven/maven.md b/docs/maven/maven.md index 2b504c7c2..8788a7023 100644 --- a/docs/maven/maven.md +++ b/docs/maven/maven.md @@ -20,7 +20,7 @@ tag: 由于 JDK 是 Maven 安装的前置条件,所以请使用 `java -version` 确认是否已经安装了 JDK: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/maven-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/maven-01.png) 我本人使用的是 macOS,所以可以有两种安装方式,**一种官网下载,手动安装;一种直接使用 brew 一键安装**。 @@ -34,7 +34,7 @@ tag: >官网地址:http://maven.apache.org/download.cgi -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/maven-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/maven-02.png) 很多初学者在官网下载的时候不知道选哪一个,这里做一下简单的介绍。 @@ -45,20 +45,20 @@ tag: 第二步,解压下载的安装包,复制该路径: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/maven-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/maven-03.png) - bin 目录:该包含了 Maven 运行的所有脚本,用来配置 Java 命令,准备执行环境,然后执行 Java 命令。 - boot 目录:该目录只包含了一个 plexus-classworlds-xxx-jar 文件,该文件是一个类加载器框架,相当于默认的 Java 类加载器,提供了更加丰富的语法以便配置,Maven 使用该加载器加载自己的类库。 - **conf 目录**:该目录包含了一个非常重要的文件 settings.xml。可以直接修改该文件,用来全局定制 Maven 的行为;也可以复制该文件到 `~/.m2/` 目录下(~表示用户目录),修改该文件可以在用户范围内定制 Maven 的行为。 - lib 目录:该目录包含了Maven运行时所需要的 Java 类库,包括Maven 依赖的第三方类库,比如 slf4j-api.jar。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/maven-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/maven-04.png) 第三步,配置环境变量 打开终端,输入 `vim ~/.bash_profile` 命令打开 bash_profile 文件: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/maven-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/maven-05.png) bash_profile 文件用于配置环境变量和启动程序,详细介绍可参照: @@ -71,17 +71,17 @@ export M2_HOME=/Users/maweiqing/cmower/save/apache-maven-3.8.3 export PATH=${PATH}:${M2_HOME}/bin ``` -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/maven-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/maven-06.png) 保存后退出,可以执行 `source ~/.bash_profile` 使配置生效: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/maven-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/maven-07.png) 第四步,查看配置是否生效 输入 `mvn -v` 命令,如果输出以下内容,表示配置成功: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/maven-08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/maven-08.png) 如未生效,可再开一个终端窗口尝试 `mvn -v` 命令。 @@ -156,19 +156,19 @@ groupId、artifactId和version这三个元素定义了一个项目的基本坐 - provided,表示打包的时候可以不用包进去,别的容器会提供。和 compile 相当,但是在打包阶段做了排除的动作。 - system,从参与程度上来说,和 provided 类似,但不通过 Maven 仓库解析,可能会造成构建的不可移植,要谨慎使用。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/maven-09.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/maven-09.png) 关于**传递性依赖**: 比如一个account-email项目为例,account-email有一个compile范围的spring-code依赖,spring-code有一个compile范围的commons-logging依赖,那么commons-logging就会成为account-email的compile的范围依赖,commons-logging是account-email的一个传递性依赖: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/maven-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/maven-10.png) 有了传递性依赖机制,在使用Spring Framework的时候就不用去考虑它依赖了什么,也不用担心引入多余的依赖。Maven会解析各个直接依赖的POM,将那些必要的间接依赖,以传递性依赖的形式引入到当前的项目中。 关于**依赖可选**: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/maven-11.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/maven-11.png) 项目中A依赖B,B依赖于X和Y,如果所有这三个的范围都是compile的话,那么X和Y就是A的compile范围的传递性依赖,但是如果我想X、Y不作为A的传递性依赖,不给它用的话,可以按照下面的方式配置可选依赖: @@ -237,11 +237,11 @@ groupId、artifactId和version这三个元素定义了一个项目的基本坐 那么它对应的仓库路径是这样的: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/maven-12.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/maven-12.png) 仓库可以以下几种: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/maven-13.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/maven-13.png) **1)本地仓库** @@ -390,7 +390,7 @@ groupId、artifactId和version这三个元素定义了一个项目的基本坐 **3)Intellij IDEA 配置 Maven** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/maven-14.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/maven-14.png) **4)Maven 常用插件** @@ -414,7 +414,7 @@ groupId、artifactId和version这三个元素定义了一个项目的基本坐 在 GitHub 上闲逛的时候,发现了一个新的项目:**maven-mvnd**,持续霸占 GitHub trending 榜单好几天了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/mvnd-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/mvnd-01.png) maven-mvnd,可以读作 Maven Daemon,译作 Maven 守护版,旨在为 Maven 提供更快的构建速度,灵感借鉴了 Gradle 和 Takari(Maven 生命周期优化器)。 @@ -425,7 +425,7 @@ maven-mvnd,可以读作 Maven Daemon,译作 Maven 守护版,旨在为 Mave Maven 和 Gradle 可以说是项目构建工具中的绝代双骄,我自己的观点是:**Maven 不比 Gradle 好,Gradle 也不比 Maven 好**。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/mvnd-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/mvnd-02.png) 瞧我这该死的观点,足够的圆滑。 @@ -464,28 +464,28 @@ brew install mvndaemon/homebrew-mvnd/mvnd >https://github.com/apache/maven-mvnd/releases -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/mvnd-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/mvnd-03.png) 下载完成后解压,然后把 bin 目录添加到 PATH 路径下。 在终端执行 `mvnd -v` 就可以查看到 mvnd 的配置信息了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/mvnd-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/mvnd-04.png) 如果出现类似下面这样的错误,未找到 JAVA_HOME,可以按照提示在对应的文件中追加 java.home 属性,也就是 JDK 的安装路径。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/mvnd-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/mvnd-05.png) 刚好之前搭建了一个Spring Boot 项目,我们可以拿 Maven 和 mvnd 来对比一下构建速度。 先执行 `mvn clean package` 命令,一共花费的时间是 5.318 秒。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/mvnd-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/mvnd-06.png) 再执行 `mvnd clean package` 命令,一共花费的时间是 3.225 秒。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/mvnd-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/mvnd-07.png) 反复多测试几次,发现 mvnd 确实比 Maven 要快上许多!Maven 维持在 5 秒多,mvnd 维持在 3 秒左右。 @@ -493,7 +493,7 @@ brew install mvndaemon/homebrew-mvnd/mvnd 感受一下 mvnd 在一个 24 核电脑上执行的样子吧,简直就是效率神器! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/maven/mvnd-08.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/maven/mvnd-08.gif) ---- diff --git a/docs/mianjing/redis12question.md b/docs/mianjing/redis12question.md index 4848e562e..9f82c4fa9 100644 --- a/docs/mianjing/redis12question.md +++ b/docs/mianjing/redis12question.md @@ -56,7 +56,7 @@ AOF 采用的是写后日志的方式,Redis 先执行命令把数据写入内 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mianjing/redis12question-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mianjing/redis12question-1.png) RDB 采用的是内存快照的方式,它记录的是某一时刻的数据,而不是操作,所以采用 RDB 方法做故障恢复时只需要直接把 RDB 文件读入内存即可,实现快速恢复。 @@ -89,7 +89,7 @@ RDB 采用的是内存快照的方式,它记录的是某一时刻的数据, 小二:额,这个我不太清楚... -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mianjing/redis12question-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mianjing/redis12question-2.png) 面试官: @@ -100,7 +100,7 @@ RDB 采用的是内存快照的方式,它记录的是某一时刻的数据, - 如果主线程执行写操作,则被修改的数据会复制一份副本,然后 bgsave 子进程会把该副本数据写入 RDB 文件,在这个过程中,主线程仍然可以直接修改原来的数据。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mianjing/redis12question-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mianjing/redis12question-3.png) 要注意,Redis 对 RDB 的执行频率非常重要,因为这会影响快照数据的完整性以及 Redis 的稳定性,所以在 Redis 4.0 后,增加了 AOF 和 RDB 混合的数据持久化机制: 把数据以 RDB 的方式写入文件,再将后续的操作命令以 AOF 的格式存入文件,既保证了 Redis 重启速度,又降低数据丢失风险。 @@ -115,21 +115,21 @@ RDB 采用的是内存快照的方式,它记录的是某一时刻的数据, 将从前的一台 Redis 服务器,同步数据到多台从 Redis 服务器上,即一主多从的模式,这个跟 MySQL 主从复制的原理一样。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mianjing/redis12question-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mianjing/redis12question-4.png) **2)哨兵模式** 使用 Redis 主从服务的时候,会有一个问题,就是当 Redis 的主从服务器出现故障宕机时,需要手动进行恢复,为了解决这个问题,Redis 增加了哨兵模式(因为哨兵模式做到了可以监控主从服务器,并且提供自动容灾恢复的功能)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mianjing/redis12question-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mianjing/redis12question-5.png) **3)Redis Cluster(集群)** Redis Cluster 是一种分布式去中心化的运行模式,是在 Redis 3.0 版本中推出的 Redis 集群方案,它将数据分布在不同的服务器上,以此来降低系统对单主节点的依赖,从而提高 Redis 服务的读写性能。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mianjing/redis12question-6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mianjing/redis12question-6.png) 面试官:使用哨兵模式在数据上有副本数据做保证,在可用性上又有哨兵监控,一旦 master 宕机会选举 salve 节点为 master 节点,这种已经满足了我们的生产环境需要,**那为什么还需要使用集群模式呢**? @@ -140,7 +140,7 @@ Redis Cluster 是一种分布式去中心化的运行模式,是在 Redis 3.0 小二:这应该是使用了某种 hash 算法,但是我不太清楚。。。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mianjing/redis12question-7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mianjing/redis12question-7.png) 面试官:那好,今天的面试就到这里吧,你先回去等我们的面试通知。 diff --git a/docs/mianjing/shanganaliyun.md b/docs/mianjing/shanganaliyun.md index 21c95cefa..eb1017846 100644 --- a/docs/mianjing/shanganaliyun.md +++ b/docs/mianjing/shanganaliyun.md @@ -23,7 +23,7 @@ tag: ----- -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mianjing/shanganaliyun-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mianjing/shanganaliyun-1.png) 我的昵称是风,来自浙江舟山,南航读研,武大本科,专业是计算机。 @@ -74,7 +74,7 @@ Guide 哥 : 目前,虽然有各种编程语言异军突起,但是 Java 庞 进入研究生阶段后,在实验室,学生俱乐部,各种活动比赛,公司的宣讲会等,都能学到很多东西,认识很多朋友。我认为有志同道合的朋友一起学习,互相查漏补缺,帮忙解决问题,相对我之前的自学,效率上要提高不少。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mianjing/shanganaliyun-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mianjing/shanganaliyun-2.png) 在了解到项目在面试中占据比较重要的位置,我会在学习完理论知识后,尽量用代码实践。在自己学习项目视频教程,并跟着手打过后,我参与到学校老师手下的一个上线项目,真正用技术解决实际问题。 diff --git a/docs/mongodb/rumen.md b/docs/mongodb/rumen.md index 80c71058f..67c8af56f 100644 --- a/docs/mongodb/rumen.md +++ b/docs/mongodb/rumen.md @@ -12,7 +12,7 @@ tag: 有时候不得不感慨一下,系统升级真的是好处多多,不仅让我有机会重构了之前的烂代码,也满足了我积极好学的虚荣心。你看,[Redis 入门](https://mp.weixin.qq.com/s/NPJkMy5RppyFk9QhzHxhrw)了、[Elasticsearch 入门](https://mp.weixin.qq.com/s/ZjsZxle7m_dfmVwVkq2ayg)了,这次又要入门 MongoDB,感觉自己变秃的同时,也变强大了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mongodb/rumen-a1f2d203-e586-4ca1-8556-e1a94c6b411e) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mongodb/rumen-a1f2d203-e586-4ca1-8556-e1a94c6b411e) 小伙伴们在继续阅读之前,我必须要声明一点,我对 MongoDB 并没有进行很深入的研究,仅仅是因为要用,就学一下。但作为一名负责任的技术博主,我是花了心思的,这篇入门教程,小伙伴们读完后绝对会感到满意,忍不住点赞。 @@ -33,7 +33,7 @@ MongoDB 提供了企业版(功能更强大)和社区版,对于我们开发 MongoDB 针对不同的操作系统有不同的安装包,我们这篇入门的文章就以 Windows 为例吧。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mongodb/rumen-557ab648-e2c4-4309-8695-aeb7fbcba25b) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mongodb/rumen-557ab648-e2c4-4309-8695-aeb7fbcba25b) 官网下载地址如下: @@ -43,19 +43,19 @@ MongoDB 针对不同的操作系统有不同的安装包,我们这篇入门的 建议选择「Custom」自定义安装,如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mongodb/rumen-42330ff2-598a-40bf-b252-480704c6a531) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mongodb/rumen-42330ff2-598a-40bf-b252-480704c6a531) 以服务模式运行,并配置好数据和日志目录,如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mongodb/rumen-c71a31d4-5a99-4c12-93a9-348aa79bc086) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mongodb/rumen-c71a31d4-5a99-4c12-93a9-348aa79bc086) 建议取消勾选安装 MongoDB 的图形化客户端工具,否则安装速度慢到你想要去扣会手机。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mongodb/rumen-84ea8b51-6b53-4500-86fa-0dcfa61b84cd) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mongodb/rumen-84ea8b51-6b53-4500-86fa-0dcfa61b84cd) 安装完成后进入到 bin 目录下,双击 mongo.exe 文件就可以连接到 MongoDB 服务了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mongodb/rumen-bcf34981-0c2b-4f86-94e0-711dfb307371) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mongodb/rumen-bcf34981-0c2b-4f86-94e0-711dfb307371) 1)MongoDB 的默认端口号为 27017。 @@ -64,11 +64,11 @@ MongoDB 针对不同的操作系统有不同的安装包,我们这篇入门的 默认会连接到 test 文档(相当于数据),可以通过 db 命令查询。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mongodb/rumen-ded1c30c-9943-4061-8fa3-4562387b72cd) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mongodb/rumen-ded1c30c-9943-4061-8fa3-4562387b72cd) 还可以运行一些简单的算术运算: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mongodb/rumen-22c1d414-6ba4-47dd-beb8-f1003f21083e) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mongodb/rumen-22c1d414-6ba4-47dd-beb8-f1003f21083e) 那如何停止服务呢?可以直接点击右上角的 X 号——粗暴、壁咚。 @@ -82,20 +82,20 @@ Robo 3T 提供了对 MongoDB 和 SCRAM-SHA-256(升级的 mongo shell)的支 最新的版本是 1.3,选择 zip 格式进行下载,23M 左右。下载完成后,解压就行了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mongodb/rumen-b178e02c-23b6-4b54-92ec-3a170a8499d0) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mongodb/rumen-b178e02c-23b6-4b54-92ec-3a170a8499d0) 包目录不再一一解释了,进入 bin 目录下,双击运行 robo3t.exe 文件,启动 Robo 3T 客户端。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mongodb/rumen-99717a12-c90d-4a03-941d-f3857f503d44) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mongodb/rumen-99717a12-c90d-4a03-941d-f3857f503d44) 点击「Create」创建一个 MongoDB 的连接。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mongodb/rumen-11866209-6a60-4a6a-9ca9-fcd9279796f6) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mongodb/rumen-11866209-6a60-4a6a-9ca9-fcd9279796f6) 连接成功后,就可以操作 MongoDB 了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mongodb/rumen-753dfcd7-1557-4837-9a7e-94b2ff9db72a) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mongodb/rumen-753dfcd7-1557-4837-9a7e-94b2ff9db72a) (不过,小伙伴们这时候也不太知道该怎么操作,毕竟 MongoDB 的一些相关概念还不清楚,无从下手啊) @@ -117,7 +117,7 @@ MongoDB 命名源于英文单词 hu**mongo**us,意思是「巨大无比」, 在进行下一步之前,需要先来理解 MongoDB 中的几个关键概念,比如说什么是集合,什么是文档,什么是字段等等。MongoDB 虽然是非关系型数据库,但和关系型数据库非常相似。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mongodb/rumen-814acc38-8934-47f9-991c-666466601bd7) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mongodb/rumen-814acc38-8934-47f9-991c-666466601bd7) 看完上面这幅图(图片来源于好朋友 macrozheng 的文章),是不是瞬间就清晰了? @@ -201,7 +201,7 @@ Document 对象来源于 org.bson 包下,可以在实例化该对象之后通 也可以通过 Robo 3T 查看“mydb”数据库,结果如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mongodb/rumen-95d49578-b1ef-43cc-91da-c7b3a7a7517e) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mongodb/rumen-95d49578-b1ef-43cc-91da-c7b3a7a7517e) ### 06、鸣谢 diff --git a/docs/mq/100-budiushi.md b/docs/mq/100-budiushi.md index cb2a09b07..9200651e8 100644 --- a/docs/mq/100-budiushi.md +++ b/docs/mq/100-budiushi.md @@ -17,7 +17,7 @@ tag: 以京东系统为例,用户在购买商品时,通常会选择用京豆抵扣一部分的金额,在这个过程中,交易服务和京豆服务通过 MQ 消息队列进行通信。在下单时,交易服务发送“扣减账户 X 100 个京豆”的消息给 MQ 消息队列,而京豆服务则在消费端消费这条命令,实现真正的扣减操作。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mq/100-budiushi-a5cbe077-5f38-44c6-9ed7-496fe1702cca.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mq/100-budiushi-a5cbe077-5f38-44c6-9ed7-496fe1702cca.png) 那在这个过程中你会遇到什么问题呢? @@ -50,7 +50,7 @@ tag: 我们首先来看消息丢失的环节,一条消息从生产到消费完成这个过程,可以划分三个阶段,分别为消息生产阶段,消息存储阶段和消息消费阶段。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mq/100-budiushi-1d1962fb-cb0f-41d0-8d6d-077ba33b6125.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mq/100-budiushi-1d1962fb-cb0f-41d0-8d6d-077ba33b6125.png) **消息生产阶段**: 从消息被生产出来,然后提交给 MQ 的过程中,只要能正常收到 MQ Broker 的 ack 确认响应,就表示发送成功,所以只要处理好返回值和异常,这个阶段是不会出现消息丢失的。 @@ -77,7 +77,7 @@ tag: 我们还是来看扣减京豆的例子,将账户 X 的金豆个数扣减 100 个,在这个例子中,我们可以通过改造业务逻辑,让它具备幂等性。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mq/100-budiushi-9d864624-2136-4770-942b-9a5f70c2aaf6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mq/100-budiushi-9d864624-2136-4770-942b-9a5f70c2aaf6.png) 最简单的实现方案,就是在数据库中建一张消息日志表, 这个表有两个字段:消息 ID 和消息执行状态。这样,我们消费消息的逻辑可以变为:在消息日志表中增加一条消息记录,然后再根据消息记录,异步操作更新用户京豆余额。 @@ -88,7 +88,7 @@ tag: 在分布式系统中,全局唯一 ID 生成的实现方法有数据库自增主键、UUID、Redis,Twitter-Snowflake 算法,我总结了几种方案的特点,你可以参考下。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mq/100-budiushi-0622a500-83ef-4b8f-96d3-68c649d17311.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mq/100-budiushi-0622a500-83ef-4b8f-96d3-68c649d17311.png) 我提醒你注意,无论哪种方法,如果你想同时满足简单、高可用和高性能,就要有取舍,所以你要站在实际的业务中,说明你的选型所考虑的平衡点是什么。我个人在业务中比较倾向于选择 Snowflake 算法,在项目中也进行了一定的改造,主要是让算法中的 ID 生成规则更加符合业务特点,以及优化诸如时钟回拨等问题。 diff --git a/docs/mq/rabbitmq-rumen.md b/docs/mq/rabbitmq-rumen.md index 75f6554e0..431f7e473 100644 --- a/docs/mq/rabbitmq-rumen.md +++ b/docs/mq/rabbitmq-rumen.md @@ -11,7 +11,7 @@ tag: 在 RabbitMQ 入门之前,我已经入门了 [Redis](https://mp.weixin.qq.com/s/NPJkMy5RppyFk9QhzHxhrw)、[Elasticsearch](https://mp.weixin.qq.com/s/ZjsZxle7m_dfmVwVkq2ayg) 和 [MongoDB](https://mp.weixin.qq.com/s/qz0sNOFeS0GTW-H9cdnbJg),这让我感觉自己富有极客精神,非常良好。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mq/rabbitmq-rumen-d416cab5-69bd-46a6-b65c-8cf3b6667136) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mq/rabbitmq-rumen-d416cab5-69bd-46a6-b65c-8cf3b6667136) 小伙伴们在继续阅读之前,我必须要声明一点,我对 RabbitMQ 并没有进行很深入的研究,仅仅是因为要用,就学一下。但作为一名负责任的技术博主,我是动了心的,这篇入门教程,小伙伴们读完后绝对会感到满意,忍不住无情地点赞,以及赤裸裸地转发。 @@ -28,7 +28,7 @@ RabbitMQ 的主要特点在于健壮性好、易于使用、高性能、高并 下图是 RabbitMQ 的消息模型图(来源于网络,侵删),小伙伴们来感受下。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mq/rabbitmq-rumen-0e8d83f8-fdf0-4755-9131-1f5c775ca010) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mq/rabbitmq-rumen-0e8d83f8-fdf0-4755-9131-1f5c775ca010) 1)P 是 Producer,代表生产者,也就是消息的发送者,可以将消息发送到 X @@ -44,7 +44,7 @@ RabbitMQ 的主要特点在于健壮性好、易于使用、高性能、高并 咦,怎么不是安装 RabbitMQ 啊?先来看看官方的解释。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mq/rabbitmq-rumen-9012f7d7-01bf-437a-ac98-f6a71390105e) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mq/rabbitmq-rumen-9012f7d7-01bf-437a-ac98-f6a71390105e) 英文看不太懂,没关系,我来补充两句人话。RabbitMQ 服务器是用 Erlang 语言编写的,它的安装包里并没有集成 Erlang 的环境,因此需要先安装 Erlang。小伙伴们不要担心,Erlang 安装起来没有任何难度。 @@ -55,7 +55,7 @@ Erlang 下载地址如下: 最新的版本是 23.0.1,我选择的是 64 位的版本,104M 左右。下载完就可以双击运行安装,傻瓜式的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mq/rabbitmq-rumen-644fafa4-a4bc-45a2-831a-12deda958122) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mq/rabbitmq-rumen-644fafa4-a4bc-45a2-831a-12deda958122) 需要注意的是,我安装的过程中,电脑重启了一次,好像要安装一个什么库,重启之前忘记保存图片了(sorry)。重启后,重新双击运行 otp_win64_23.0.1.exe 文件完成 Erlang 安装。 @@ -67,19 +67,19 @@ Erlang 安装成功后,就可以安装 RabbitMQ 了。下载地址如下所示 找到下图中的位置,选择红色框中的文件进行下载。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mq/rabbitmq-rumen-43268557-1240-4ed4-9883-de93668f1f04) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mq/rabbitmq-rumen-43268557-1240-4ed4-9883-de93668f1f04) 安装包只有 16.5M 大小,还是非常轻量级的。下载完后直接双击运行 exe 文件就可以傻瓜式地安装了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mq/rabbitmq-rumen-b3343075-f9a8-441f-b266-0750df82a1c6) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mq/rabbitmq-rumen-b3343075-f9a8-441f-b266-0750df82a1c6) 安装成功后,就可以将 RabbitMQ 作为 Windows 服务启动,可以从“开始”菜单管理 RabbitMQ Windows 服务。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mq/rabbitmq-rumen-6f185cbd-b3ab-432a-85df-be3a93450533) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mq/rabbitmq-rumen-6f185cbd-b3ab-432a-85df-be3a93450533) 点击「RabbitMQ Command Prompt (sbin dir)」,进入命令行,输入 `rabbitmqctl.bat status` 可确认 RabbitMQ 的启动状态。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mq/rabbitmq-rumen-d9407f52-3585-4c3c-af2f-7b7c4ec45461) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mq/rabbitmq-rumen-d9407f52-3585-4c3c-af2f-7b7c4ec45461) 可以看到 RabbitMQ 一些状态信息: @@ -96,11 +96,11 @@ rabbitmq-plugins enable rabbitmq_management 看到以下信息就可以确认插件启用成功了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mq/rabbitmq-rumen-3479f2b1-5089-4d73-a6fa-f673989766b7) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mq/rabbitmq-rumen-3479f2b1-5089-4d73-a6fa-f673989766b7) 在浏览器地址栏输入 [http://localhost:15672/](http://localhost:15672/) 可以进入管理端界面,如下图所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mq/rabbitmq-rumen-cbad5128-e675-4da7-a5dd-fe9bb303f5f0) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mq/rabbitmq-rumen-cbad5128-e675-4da7-a5dd-fe9bb303f5f0) ### 04、在 Java 中使用 RabbitMQ @@ -119,7 +119,7 @@ rabbitmq-plugins enable rabbitmq_management 第二步,我们来模拟一个最简单的场景,一个生产者发送消息到队列中,一个消费者从队列中读取消息并打印。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mq/rabbitmq-rumen-3ce8cf20-76f0-4192-88d4-f40d9a23233e) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mq/rabbitmq-rumen-3ce8cf20-76f0-4192-88d4-f40d9a23233e) 官方对 RabbitMQ 有一个很好的解释,我就“拿来主义”的用一下。在我上高中的年代,同学们之间最流行的交流方式不是 QQ、微信,甚至短信这些,而是书信。因为那时候还没有智能手机,况且上学期间学校也是命令禁用手机的,所以书信是情感表达的最好方式。好怀念啊。 @@ -232,6 +232,6 @@ DeliverCallback deliverCallback = (consumerTag, delivery) -> { 在消息发送的过程中,也可以使用 RabbitMQ 的管理面板查看到消息的走势图,如下所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mq/rabbitmq-rumen-0dcb67b3-521f-4d91-9e46-590e93631b35) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mq/rabbitmq-rumen-0dcb67b3-521f-4d91-9e46-590e93631b35) diff --git a/docs/mysql/deletedb-binlog-weiguanjishu.md b/docs/mysql/deletedb-binlog-weiguanjishu.md index 701c318dc..03afc73d4 100644 --- a/docs/mysql/deletedb-binlog-weiguanjishu.md +++ b/docs/mysql/deletedb-binlog-weiguanjishu.md @@ -3,19 +3,19 @@ 网上也经常看到一些段子,某公司程序员对工作不满,删库跑路,老板损失惨重,欲哭无泪。这不前几天又爆出一例,**某程序员离职当天删库跑路**! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mysql/deletedb-binlog-weiguanjishu-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mysql/deletedb-binlog-weiguanjishu-1.png) 那么有没有什么解决方案?即使数据库真的被删了,也有备份数据,能快速恢复。甚至可以做到实时热备,即使内部炸掉外部用户也感知不到,一片风平浪静。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mysql/deletedb-binlog-weiguanjishu-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mysql/deletedb-binlog-weiguanjishu-2.png) MySQL 作为当下流行数据库,在数据备份、高可用方面非常有竞争力,今天,我们就重点聊一聊数据备份的杀手锏 binlog。 ### 一、MySQL 主备是什么? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mysql/deletedb-binlog-weiguanjishu-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mysql/deletedb-binlog-weiguanjishu-3.png) 情况一: @@ -33,7 +33,7 @@ MySQL 作为当下流行数据库,在数据备份、高可用方面非常有 ### 二、主从同步 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mysql/deletedb-binlog-weiguanjishu-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mysql/deletedb-binlog-weiguanjishu-4.png) 1、在备库执行 `change master` 命令 ,绑定主库的信息 @@ -99,12 +99,12 @@ insert into person values(80,800,800); 查看binlog模式: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mysql/deletedb-binlog-weiguanjishu-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mysql/deletedb-binlog-weiguanjishu-5.png) 查看当前正在写入的binlog文件: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mysql/deletedb-binlog-weiguanjishu-6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mysql/deletedb-binlog-weiguanjishu-6.png) 查看 binlog 中的内容,我们先来看下 row 模式 @@ -113,7 +113,7 @@ show binlog events in 'mysql-bin.000001'; ``` -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mysql/deletedb-binlog-weiguanjishu-7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mysql/deletedb-binlog-weiguanjishu-7.png) 说明: @@ -137,7 +137,7 @@ mysqlbinlog -vv mysql-bin.000001 --start-position=2986; ``` -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mysql/deletedb-binlog-weiguanjishu-8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mysql/deletedb-binlog-weiguanjishu-8.png) 红框中的内容表示执行了插入命令,insert into person values(80,800,800); @@ -155,7 +155,7 @@ show binlog events in 'mysql-bin.000001'; ``` -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mysql/deletedb-binlog-weiguanjishu-9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mysql/deletedb-binlog-weiguanjishu-9.png) 从上图中我们可以看出,当 binlog_format=statement 时,binlog 里面记录的就是 SQL 语句的原文。 @@ -172,7 +172,7 @@ statement 与 row 对比: statement 格式的binlog记录的是sql语句;row 格式的binlog记录的是event(Table_map,Write_rows,Delete_rows) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mysql/deletedb-binlog-weiguanjishu-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mysql/deletedb-binlog-weiguanjishu-10.png) 当 binlog 在 statement 格式下,记录的是sql语句,在主库执行时可能使用的是索引 A;但是同步给备库执行时,可能用了 索引B。 @@ -184,10 +184,10 @@ statement 格式的binlog记录的是sql语句;row 格式的binlog记录的是 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mysql/deletedb-binlog-weiguanjishu-11.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mysql/deletedb-binlog-weiguanjishu-11.png) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mysql/deletedb-binlog-weiguanjishu-12.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mysql/deletedb-binlog-weiguanjishu-12.png) **mixed 格式 的binlog 是个啥**? @@ -246,6 +246,6 @@ OK,搞定,再也不怕删库跑路了。 转载链接:https://mp.weixin.qq.com/s/oD3Anvz3XCsrahn6WdeeNw -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/mysql/deletedb-binlog-weiguanjishu-13.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/mysql/deletedb-binlog-weiguanjishu-13.png) *没有什么使我停留——除了目的,纵然岸旁有玫瑰、有绿荫、有宁静的港湾,我是不系之舟*。 diff --git a/docs/nginx/40-interview.md b/docs/nginx/40-interview.md index 402ba4c36..fa2ecd9d1 100644 --- a/docs/nginx/40-interview.md +++ b/docs/nginx/40-interview.md @@ -50,7 +50,7 @@ tag: * 生产中如何设置worker进程的数量呢? * nginx状态码 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/40-interview-63553177-7359-4f68-8673-5b44285cb701.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/40-interview-63553177-7359-4f68-8673-5b44285cb701.png) @@ -233,7 +233,7 @@ session: 最核心的区别在于apache是同步多进程模型,一个连接对应一个进程,nginx是异步的,多个连接可以对应一个进程。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/40-interview-8cd652f4-5a93-4e12-a020-0d90b9379bf2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/40-interview-8cd652f4-5a93-4e12-a020-0d90b9379bf2.png) ## 什么是动态资源、静态资源分离? @@ -434,7 +434,7 @@ location的语法能说出来吗? > 注意:~ 代表自己输入的英文字母 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/40-interview-8b8c7eeb-5542-43ab-9acc-3c7e51b5a4ac.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/40-interview-8b8c7eeb-5542-43ab-9acc-3c7e51b5a4ac.png) #### Location正则案例 @@ -555,7 +555,7 @@ server { 漏桶算法思路很简单,我们把水比作是请求,漏桶比作是系统处理能力极限,水先进入到漏桶里,漏桶里的水按一定速率流出,当流出的速率小于流入的速率时,由于漏桶容量有限,后续进入的水直接溢出(拒绝请求),以此实现限流。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/40-interview-57f54ac6-82fe-403c-af37-728d707d5eca.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/40-interview-57f54ac6-82fe-403c-af37-728d707d5eca.png) #### 令牌桶算法 @@ -564,7 +564,7 @@ server { 系统会维护一个令牌(token)桶,以一个恒定的速度往桶里放入令牌(token),这时如果有请求进来想要被处理,则需要先从桶里获取一个令牌(token),当桶里没有令牌(token)可取时,则该请求将被拒绝服务。令牌桶算法通过控制桶的容量、发放令牌的速率,来达到对请求的限制。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/40-interview-4bebeaf2-3853-4bb3-a34d-10316a230854.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/40-interview-4bebeaf2-3853-4bb3-a34d-10316a230854.png) ## Nginx配置高可用性怎么配置? @@ -691,17 +691,17 @@ http { 我们可以先来对比下,如果我们没有开启zip压缩之前,我们的对应的文件大小,如下所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/40-interview-fb46fd48-e596-48ac-8759-0663d29593af.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/40-interview-fb46fd48-e596-48ac-8759-0663d29593af.png) 现在我们开启了gzip进行压缩后的文件的大小,可以看到如下所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/40-interview-1ee18fd5-1cc1-478a-9fca-d15f85f0f5c7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/40-interview-1ee18fd5-1cc1-478a-9fca-d15f85f0f5c7.png) 并且我们查看响应头会看到gzip这样的压缩,如下所示 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/40-interview-c3c95717-45ee-4f56-b606-d56d2fc3cc57.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/40-interview-c3c95717-45ee-4f56-b606-d56d2fc3cc57.png) gzip压缩前后效果对比:jquery原大小90kb,压缩后只有30kb。 diff --git a/docs/nginx/nginx.md b/docs/nginx/nginx.md index dd05b6a2d..ce9c7452a 100644 --- a/docs/nginx/nginx.md +++ b/docs/nginx/nginx.md @@ -13,7 +13,7 @@ tag: 作为开发者,相信大家都知道 Nginx 的重要性。Nginx 是一个高性能的 HTTP 和反向代理 Web 服务器,由俄罗斯的伊戈尔·赛索耶夫开发,第一个版本发布于 2004 年 10 月 4 日。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/nginx-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/nginx-01.png) Nginx 的特点是: @@ -31,13 +31,13 @@ Nginx 的特点是: **反向代理**是 Nginx 作为 Web 服务器最常用的功能之一。什么是反向代理呢?很多初学者在第一次遇到这个名词的时候总免不了出现很多问号。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/nginx-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/nginx-02.png) 那要想搞明白什么是反向代理,就必须得搞明白什么是正向代理。 举个例子,小二的浏览器是无法直接访问谷哥的,但香港的代理服务器是可以访问谷哥的,于是小二访问了香港的代理服务器,也就间接地访问了谷哥。那这台代理服务器也就是**正向代理**。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/nginx-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/nginx-03.png) 总结一句就是,**正向代理是代理客户端的**,让你能正常访问目的服务器。 @@ -47,7 +47,7 @@ Nginx 的特点是: 这就需要一个反向代理服务器了,反向代理服务器让 1 万个小二访问服务器 A,1 万个小二访问服务器 B,1 个小二访问服务器 C,这样的话,每台服务器的压力就相应减小了,是不是很 nice? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/nginx-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/nginx-04.png) 那问题来了。每台服务器的能力可能不同,比如说服务器 A 的内存比较大一点,有 100 个 G;服务器 B 的内存小一点,有 10 个 G;服务器 C 的内存更小一点,只有 1 个 G。怎么才能让没台服务器承担起它能力范围内的访问呢? @@ -59,7 +59,7 @@ Nginx 的特点是: 我们可以根据一些规则,把动态资源和静态资源分开,然后通过 Nginx 把请求分开,静态资源的请求就不需要经过 Web 服务器处理了,从而提高整体上的资源的响应速度。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/nginx-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/nginx-05.png) ### 二、Nginx 的安装 @@ -71,17 +71,17 @@ Nginx 的特点是: 不过,如果在安装宝塔面板必备工具包的时候,如果选择了 phpmyadmin(MySQL 的管理工具),会覆盖掉 80 端口,就导致没办法直接通过默认配置的方式访问 Nginx 启动页面了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/nginx-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/nginx-06.png) 我这里以 macOS 环境为例,来演示一下。 第一步,通过 `brew info nginx` 命令查看 Nginx 是否安装。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/nginx-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/nginx-07.png) 第二步,通过 `brew install nginx` 命令安装 Nginx。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/nginx-08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/nginx-08.png) 从以上信息可以得出: @@ -91,11 +91,11 @@ Nginx 的特点是: 第三步,通过 `nginx` 命令启动 Nginx。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/nginx-09.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/nginx-09.png) 第四步,在浏览器地址栏通过 `localhost:8080` 访问,可以看到以下欢迎页面。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/nginx-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/nginx-10.png) ### 三、Nginx 常用命令 @@ -166,7 +166,7 @@ http { 好,现在我们登录宝塔面板,尝试把默认的 server 配置复制到 Linux 服务器中的 Nginx 配置里。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/nginx-11.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/nginx-11.png) 简单解释一下。 @@ -189,7 +189,7 @@ root 我指定了 `/home/www` 目录,首页文件为 index.html。这个文件 好,保存配置文件,并且 reload Nginx,我们在本地的浏览器中输入服务器的 IP 地址就可以看到效果了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/nginx-12.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/nginx-12.png) ### 五、Nginx 的学习资料 @@ -199,13 +199,13 @@ root 我指定了 `/home/www` 目录,首页文件为 index.html。这个文件 **1)狂神说的视频入门教程**,我个人觉得,狂神的入门教程还是非常舒适的,语速和内容都刚刚好。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/nginx-13.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/nginx-13.png) >https://www.bilibili.com/video/BV1F5411J7vK **2)黑马程序员Nginx教程**,总共 159 讲,基本上算是非常全面的 Nginx 的视频教程了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/nginx-14.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/nginx-14.png) >https://www.bilibili.com/video/BV1ov41187bq @@ -213,7 +213,7 @@ root 我指定了 `/home/www` 目录,首页文件为 index.html。这个文件 **4)Nginx 从入门到实践,万字详解**,图文版的,可以到掘金上看看这篇文章,内容基本上面面俱到了(可以看一下下面的目录),配合前面的视频课,拿下 Nginx 基本上是稳了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/nginx/nginx-15.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/nginx/nginx-15.png) >https://juejin.cn/post/6844904144235413512 diff --git a/docs/oo/abstract.md b/docs/oo/abstract.md index 7bafef1eb..21dddfc49 100644 --- a/docs/oo/abstract.md +++ b/docs/oo/abstract.md @@ -28,7 +28,7 @@ abstract class AbstractPlayer { 抽象类是不能实例化的,尝试通过 `new` 关键字实例化的话,编译器会报错,提示“类是抽象的,不能实例化”。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/object-class/abstract-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/object-class/abstract-01.png) 虽然抽象类不能实例化,但可以有子类。子类通过 `extends` 关键字来继承抽象类。就像下面这样。 @@ -41,11 +41,11 @@ public class BasketballPlayer extends AbstractPlayer { 当我们尝试在一个普通类中定义抽象方法的时候,编译器会有两处错误提示。第一处在类级别上,提示“这个类必须通过 `abstract` 关键字定义”,见下图。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/object-class/abstract-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/object-class/abstract-02.png) 第二处在尝试定义 abstract 的方法上,提示“抽象方法所在的类不是抽象的”,见下图。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/object-class/abstract-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/object-class/abstract-03.png) 抽象类中既可以定义抽象方法,也可以定义普通方法,就像下面这样: @@ -72,7 +72,7 @@ public class BasketballPlayer extends AbstractPlayer { 如果没有实现的话,编译器会提示“子类必须实现抽象方法”,见下图。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/object-class/abstract-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/object-class/abstract-04.png) “二哥,抽象方法我明白了,那什么时候使用抽象方法呢?能给我讲讲它的应用场景吗?”三妹及时的插话道。 @@ -228,7 +228,7 @@ public class FileReaderTest { 在项目的 resource 目录下建一个文本文件,名字叫 helloworld.txt,里面的内容就是“Hello World”。文件的具体位置如下图所示,我用的集成开发环境是 Intellij IDEA。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/object-class/abstract-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/object-class/abstract-05.png) 在 resource 目录下的文件可以通过 `ClassLoader.getResource()` 的方式获取到 URI 路径,然后就可以取到文本内容了。 diff --git a/docs/oo/code-init.md b/docs/oo/code-init.md index 9fcff3f4b..2ddecb09f 100644 --- a/docs/oo/code-init.md +++ b/docs/oo/code-init.md @@ -79,7 +79,7 @@ public class Car { 说完这句话,我打开 draw.io,使上了吃奶的劲,画出了下面这幅图。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/object-class/22-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/object-class/22-01.png) “哦,原来如此啊!”三妹仿佛发现了新大陆,意味深长地说。 diff --git a/docs/oo/construct.md b/docs/oo/construct.md index b775f65c0..c6b19195f 100644 --- a/docs/oo/construct.md +++ b/docs/oo/construct.md @@ -101,7 +101,7 @@ public class Bike { 通常情况下,无参构造方法是可以缺省的,我们开发者并不需要显式的声明无参构造方法,把这项工作交给编译器更轻松一些。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/object-class/18-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/object-class/18-01.png) “二哥,默认构造方法的目的是什么?它为什么是一个空的啊?”三妹疑惑地看着我,提出了这个尖锐的问题。 @@ -217,7 +217,7 @@ public class OverloadingConstrutorPerson { 构造方法和方法之间的区别还是蛮多的,比如说下面这些: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/object-class/18-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/object-class/18-02.png) diff --git a/docs/oo/final.md b/docs/oo/final.md index 15dd309b9..d3e54e465 100644 --- a/docs/oo/final.md +++ b/docs/oo/final.md @@ -30,7 +30,7 @@ final int age = 18; “当尝试将 age 的值修改为 30 的时候,编译器就生气了。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/keywords/23-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/keywords/23-01.png) “再来看这段代码。” @@ -58,7 +58,7 @@ final Pig pig = new Pig(); “如果尝试将 pig 重新赋值的话,编译器同样会生气。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/keywords/23-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/keywords/23-02.png) “但我们仍然可以去修改 pig 对象的 name。” @@ -70,7 +70,7 @@ System.out.println(pig.getName()); // 特立独行 “另外,final 修饰的成员变量必须有一个默认值,否则编译器将会提醒没有初始化。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/keywords/23-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/keywords/23-03.png) “final 和 static 一起修饰的成员变量叫做常量,常量名必须全部大写。” @@ -97,7 +97,7 @@ public class ArgFinalTest { “如果尝试去修改它的话,编译器会提示以下错误。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/keywords/23-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/keywords/23-04.png) ### 02、final 方法 @@ -124,7 +124,7 @@ public class Actor { “当我们想要重写该方法的话,就会出现编译错误。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/keywords/23-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/keywords/23-05.png) “如果一个类中的某些方法要被其他方法调用,则应考虑事被调用的方法称为 final 方法,否则,重写该方法会影响到调用方法的使用。” @@ -186,7 +186,7 @@ public final class Writer { “尝试去继承它,编译器会提示以下错误,Writer 类是 final 的,无法继承。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/keywords/23-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/keywords/23-06.png) “不过,类是 final 的,并不意味着该类的对象是不可变的。” diff --git a/docs/oo/inner-class.md b/docs/oo/inner-class.md index fd24e7c3d..fa2c59561 100644 --- a/docs/oo/inner-class.md +++ b/docs/oo/inner-class.md @@ -117,7 +117,7 @@ public class Wangsan { 局部内部类就好像一个局部变量一样,它是不能被权限修饰符修饰的,比如说 public、protected、private 和 static 等。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/oo/inner-class-26fc0242-134a-4588-a52d-7da962fc3fb9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/oo/inner-class-26fc0242-134a-4588-a52d-7da962fc3fb9.png) **3)匿名内部类** @@ -140,7 +140,7 @@ public class ThreadDemo { 匿名内部类就好像一个方法的参数一样,用完就没了,以至于我们都不需要为它专门写一个构造方法,它的名字也是由系统自动命名的。仔细观察编译后的字节码文件也可以发现,匿名内部类连名字都不配拥有,哈哈,直接借用的外部类,然后 `&1` 就搞定了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/oo/inner-class-c0b9bdf5-cb12-45fc-b362-cb14d5d44fdc.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/oo/inner-class-c0b9bdf5-cb12-45fc-b362-cb14d5d44fdc.png) 匿名内部类是唯一一种没有构造方法的类。就上面的写法来说,匿名内部类也不允许我们为其编写构造方法,因为它就像是直接通过 new 关键字创建出来的一个对象。 @@ -167,7 +167,7 @@ public class Wangsi { 由于 static 关键字的存在,静态内部类是不允许访问外部类中非 static 的变量和方法的,这一点也非常好理解:你一个静态的内部类访问我非静态的成员变量干嘛? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/oo/inner-class-69523196-37fe-43c6-a52e-5a8c94fdd2d8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/oo/inner-class-69523196-37fe-43c6-a52e-5a8c94fdd2d8.png) ### 总结 diff --git a/docs/oo/interface.md b/docs/oo/interface.md index a57e10169..eb34402d4 100644 --- a/docs/oo/interface.md +++ b/docs/oo/interface.md @@ -10,14 +10,14 @@ tag: “哥,我看你朋友圈说《Java 程序员进阶之路》专栏收到了第一笔赞赏呀,虽然只有一块钱,但我也替你感到开心。”三妹的脸上洋溢着自信的微笑,仿佛这钱是打给她的一样。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/object-class/interface-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/object-class/interface-01.png) “是啊,早上起来的时候看到这条信息,还真的是挺开心的,虽然只有一块钱,但是开源的第一笔,也是我人生当中的第一笔,真的非常感谢这个读者,值得纪念的一天。”我自己也掩饰不住内心的激动。 “有了这份鼓励,我相信你更新下去的动力更足了!”三妹今天说的话真的是特别令人喜欢。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/object-class/interface-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/object-class/interface-02.png) “是啊是啊,所以,今天要更新第 26 讲了——接口。”我接着说,“对于面向对象编程来说,抽象是一个极具魅力的特征。如果一个程序员的抽象思维很差,那他在编程中就会遇到很多困难,无法把业务变成具体的代码。在 Java 中,可以通过两种形式来达到抽象的目的,一种上一篇的主角——[抽象类](https://mp.weixin.qq.com/s/WSmGwdtlimIFVVDVKfvrWQ),另外一种就是今天的主角——接口。” @@ -93,7 +93,7 @@ Java 官方文档上有这样的声明: 4)接口中允许定义 `default` 方法也是从 Java 8 开始的,比如说上例中的 `printDescription()` 方法,它始终由一个代码块组成,为,实现该接口而不覆盖该方法的类提供默认实现。既然要提供默认实现,就要有方法体,换句话说,默认方法后面不能直接使用“;”号来结束——编译器会报错。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/object-class/interface-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/object-class/interface-03.png) “为什么要在接口中定义默认方法呢?”三妹好奇地问到。 @@ -110,7 +110,7 @@ Java 官方文档上有这样的声明: 1)接口不允许直接实例化,否则编译器会报错。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/object-class/interface-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/object-class/interface-04.png) 需要定义一个类去实现接口,见下例。 @@ -145,11 +145,11 @@ Serializable 接口用来为序列化的具体实现提供一个标记,也就 3)不要在定义接口的时候使用 final 关键字,否则会报编译错误,因为接口就是为了让子类实现的,而 final 阻止了这种行为。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/object-class/interface-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/object-class/interface-05.png) 4)接口的抽象方法不能是 private、protected 或者 final,否则编译器都会报错。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/object-class/interface-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/object-class/interface-06.png) 5)接口的变量是隐式 `public static final`(常量),所以其值无法改变。 @@ -204,7 +204,7 @@ Exception in thread "main" java.lang.CloneNotSupportedException: com.cmower.bael 如果有两个类共同继承(extends)一个父类,那么父类的方法就会被两个子类重写。然后,如果有一个新类同时继承了这两个子类,那么在调用重写方法的时候,编译器就不能识别要调用哪个类的方法了。这也正是著名的菱形问题,见下图。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/object-class/interface-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/object-class/interface-07.png) 简单解释下,ClassC 同时继承了 ClassA 和 ClassB,ClassC 的对象在调用 ClassA 和 ClassB 中重写的方法时,就不知道该调用 ClassA 的方法,还是 ClassB 的方法。 diff --git a/docs/oo/method.md b/docs/oo/method.md index 67c4fabcc..014196bc7 100644 --- a/docs/oo/method.md +++ b/docs/oo/method.md @@ -25,7 +25,7 @@ tag: 方法的声明反映了方法的一些信息,比如说可见性、返回类型、方法名和参数。如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/object-class/17-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/object-class/17-01.png) **访问权限**:它指定了方法的可见性。Java 提供了四种访问权限修饰符: @@ -77,7 +77,7 @@ public class PredefinedMethodDemo { 我们可以通过集成开发工具查看预先定义方法的方法签名,当我们把鼠标停留在 `println()` 方法上面时,就会显示下图中的内容: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/object-class/17-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/object-class/17-02.png) `println()` 方法的访问权限修饰符是 public,返回类型为 void,方法名为 println,参数为 `String x`,以及 Javadoc(方法是干嘛的)。 diff --git a/docs/oo/object-class.md b/docs/oo/object-class.md index 0f02b6893..917609b62 100644 --- a/docs/oo/object-class.md +++ b/docs/oo/object-class.md @@ -157,7 +157,7 @@ null 第二种:`main()` 方法不在 Person 类中,而在另外一个类中。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/object-class/16-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/object-class/16-01.png) 实际开发中,我们通常不在当前类中直接创建对象并使用它,而是放在使用对象的类中,比如说上图中的 PersonTest 类。 @@ -221,7 +221,7 @@ public class Person { person 被称为对象 Person 的引用变量,见下图: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/object-class/16-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/object-class/16-02.png) 通过对象的引用变量,可以直接对字段进行初始化(`person.name = "沉默王二"`),所以以上代码输出结果如下所示: diff --git a/docs/oo/static.md b/docs/oo/static.md index d5d3b2d1e..ebe4cf320 100644 --- a/docs/oo/static.md +++ b/docs/oo/static.md @@ -62,7 +62,7 @@ public class Student { “哦哦,别担心,三妹,画幅图你就全明白了。”说完我就打开 draw.io 这个网址,认真地画起了图。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/keywords/19-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/keywords/19-01.png) “现在,是不是一下子就明白了?”看着这幅漂亮的手绘图,我心里有点小开心。 @@ -91,7 +91,7 @@ public class Counter { 我在侃侃而谈,而三妹似乎有些不太明白。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/keywords/19-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/keywords/19-02.png) “没关系,三妹,你先盲猜一下,这段代码输出的结果是什么?” @@ -143,7 +143,7 @@ public class StaticCounter { “另外,需要注意的是,由于静态变量属于一个类,所以不要通过对象引用来访问,而应该直接通过类名来访问,否则编译器会发出警告。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/keywords/19-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/keywords/19-03.png) ### 02、静态方法 @@ -204,11 +204,11 @@ public class StaticMethodStudent { “先是在静态方法中访问非静态变量,编译器不允许。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/keywords/19-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/keywords/19-04.png) “然后在静态方法中访问非静态方法,编译器同样不允许。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/keywords/19-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/keywords/19-05.png) “关于静态方法的使用,这下清楚了吧,三妹?” @@ -220,7 +220,7 @@ public class StaticMethodStudent { “java.lang.Math 类的几乎所有方法都是静态的,可以直接通过类名来调用,不需要创建类的对象。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/keywords/19-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/keywords/19-06.png) ### 03、静态代码块 @@ -274,7 +274,7 @@ public class StaticBlockNoMain { “在命令行中执行 `java StaticBlockNoMain` 的时候,会抛出 NoClassDefFoundError 的错误。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/keywords/19-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/keywords/19-07.png) “三妹,来看下面这个例子。” @@ -339,7 +339,7 @@ public class Singleton { “三妹,你看,在 Singleton 类上加 static 后,编译器就提示错误了。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/keywords/19-08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/keywords/19-08.png) 三妹点了点头,所有所思。 diff --git a/docs/oo/this-super.md b/docs/oo/this-super.md index 3d4ba999c..adafe1966 100644 --- a/docs/oo/this-super.md +++ b/docs/oo/this-super.md @@ -218,7 +218,7 @@ hello “不过,需要注意的是,`this()` 必须放在构造方法的第一行,否则就报错了。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/keywords/20-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/keywords/20-01.png) ### 05、作为参数在方法中传递 diff --git a/docs/overview/hello-world.md b/docs/overview/hello-world.md index baffa6f75..d44ce131d 100644 --- a/docs/overview/hello-world.md +++ b/docs/overview/hello-world.md @@ -95,7 +95,7 @@ public class HelloWorld { IDEA 会自动保存,在代码编辑面板中右键,在弹出的菜单中选择「Run 'HelloWorld.main()'」,如下图所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/four-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/four-01.png) 等代码编译结束后,就可以在 Run 面板里看到下面的内容: @@ -129,15 +129,15 @@ JDK 是 Java Development Kit 的首字母缩写,是提供给 Java 程序员的 想要成为一名 Java 程序员,首先就需要在电脑上安装 JDK。当然了,新版的 Intellij IDEA(公认最好用的集成开发环境)已经支持直接下载 JDK 了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/six-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/six-01.png) 并且支持下载不同版本的 JDK,除了 Oracle 的 OpenJDK,还有社区维护版 AdoptOpenJDK,里面包含了目前使用范围最广的 HotSpot 虚拟机。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/six-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/six-02.png) 如果下载比较慢的话,可以直接在 AdoptOpenJDK 官网上下载。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/six-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/six-03.png) 如果还是比较慢的话,通过 Oracle 官网下载吧! @@ -170,7 +170,7 @@ Java 程序运行的正式环境一般会选择 Linux 服务器,因为更安 可以看到有这么一些(只列出 Java 11 的部分——最近一个 LTS 版本): -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/six-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/six-04.png) 其中 JRE 为 java-11-openjdk.x86_64,JDK 为 java-11-openjdk-devel.x86_64。 @@ -184,35 +184,35 @@ Java 程序运行的正式环境一般会选择 Linux 服务器,因为更安 如果出现以下结果,则表明安装成功: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/six-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/six-05.png) 由于 JRE 中不包含 javac,所以 `javac -version` 的结果如下所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/six-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/six-06.png) 那既然服务器上的 JRE 环境已经 OK 了,那我们就把之前的“Hello World”程序打成 jar 上传过去,让它跑起来。 第一步,Maven clean(对项目清理): -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/six-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/six-07.png) 第二步,Maven package(对项目打包): -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/six-08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/six-08.png) 可以在 Run 面板中看到以下信息: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/six-09.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/six-09.png) 说明项目打包成功了。 第三步,使用 FileZilla 工具将 jar 包上传到服务器指定目录。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/six-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/six-10.png) 第四步,使用 iTerm2 工具连接服务器。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/six-11.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/six-11.png) 第五步,执行以下命令: @@ -220,7 +220,7 @@ Java 程序运行的正式环境一般会选择 Linux 服务器,因为更安 可以看到以下结果: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/six-12.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/six-12.png) “搞定了,三妹,今天我们就学到这吧。”转动了一下僵硬的脖子后,我对三妹说,“开发环境需要安装 JDK,因为既需要编写源代码,还需要打包和测试;生产环境只需要安装 JRE,因为只需要运行编译打包好的 jar 包即可。” diff --git a/docs/overview/what-is-java.md b/docs/overview/what-is-java.md index 92c3e885b..f7e4f0bf1 100644 --- a/docs/overview/what-is-java.md +++ b/docs/overview/what-is-java.md @@ -25,7 +25,7 @@ tag: Java 是一门计算机编程语言,高级、健壮、面向对象,并且非常安全。它由 Sun 公司在 1995 年开发,主力开发叫 James Gosling,被称为 Java 之父,就是下图这位,头秃的厉害。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/one-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/one-01.png) “三妹啊,你要不要再考虑考虑?做程序员不容易啊” @@ -42,7 +42,7 @@ James Gosling 回忆说,“Java”是一个叫 Mark Opperman 的人提议的 使用十六进制编辑器打开由 Java 源代码编译出的二进制文件(.class 文件),就可以看得到,最前面的 8 个字符是 CA FE BA BE(定义文件类型的魔数),即词组“CAFE BABE”(咖啡屋宝贝)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/one-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/one-02.png) “二哥,能给我展示一段 Java 代码吗?我想感受一下。” @@ -97,7 +97,7 @@ Sun 公司为了抢占市场先机,在 1991 年成立了一个由詹姆斯· **Oak** 是“Java”的第二个名字,这次就有点意义了。Oak(橡树)是力量的象征,被美国、法国、德国等许多欧美国家选为国树。橡树长下面这样。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/two-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/two-01.png) 1992 年,Oak 的雏形有了,但项目组在向硬件生产商进行商演的时候,并没有获得认可,于是 Oak 就被搁置一旁了。 @@ -111,7 +111,7 @@ Sun 公司为了抢占市场先机,在 1991 年成立了一个由詹姆斯· “Java”是印度尼西亚爪哇岛的英文名,因生产咖啡而闻名,所以,小伙伴也看到了,Java 这个单词经常和一杯冒着热气的咖啡一起出现。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/two-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/two-02.png) 同年,Sun 公司在 SunWorld 大会上正式发布了 Java 1.0 版本,第一次提出了“Write Once, Run anywhere”的口号。《时代》杂志将 Java 评为 1995 年十大最佳产品之一。 @@ -159,7 +159,7 @@ Java 在云时代面临着以 Go 语言为主的容器(Docker 等技术)生 尽管 Java 已经 25 岁了,但仍然“宝刀未老”。在 Stack Overflow 2019 年流行编程语言调查报告中,Java 位居第 5 位,有 41% 的受调开发者认为 Java 仍然是一门受欢迎的编程语言。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/three-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/three-01.png) 很多大型的互联网公司都在使用 Java,国内最有名的当属阿里巴巴,国外最有名的当属谷歌。那为什么 Java 如此流行呢? @@ -213,7 +213,7 @@ Java 在多线程方面做得非常突出,只要操作系统支持,Java 中 **物联网(IoT)领域:** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/overview/three-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/overview/three-02.png) Oracle 表示,灵活性和流行度是 IoT 程序员选择 Java 的主要原因。Java 提供了大量的 API 库,可以很容易应用到嵌入式应用程序中。相比其他编程语言,比如 C 语言,Java 在切换平台时更加顺畅,不容易出错。 diff --git a/docs/redis/rumen.md b/docs/redis/rumen.md index 1ec681c00..33f27c847 100644 --- a/docs/redis/rumen.md +++ b/docs/redis/rumen.md @@ -11,7 +11,7 @@ tag: 作为一名富有责任心的技术博主,我觉得有必要把我入门 Redis 的过程分享出来,供一些小伙伴作为参考。要是我哪里写错了,别客气,过来给我一巴掌,就行了(温柔点,别打肿,影响颜值就不好了)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/redis/rumen-fe7d042b-efed-469c-9460-fb3bc1d4c041) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/redis/rumen-fe7d042b-efed-469c-9460-fb3bc1d4c041) ### 01、Redis 是什么 @@ -25,7 +25,7 @@ Redis 的作者是一名意大利人,网名 Antirez,长相还是过得去的 据说是手机键盘上“MERZ”的位置决定的,小伙伴们可以打开自己手机上九宫格键盘感受一下。“MERZ”是什么意思呢?据说是“愚蠢”的意思。这?是不是感觉程序员的生活中还是有蛮多神秘色彩的? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/redis/rumen-ced599df-4791-4777-970f-20ceeeb39e68) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/redis/rumen-ced599df-4791-4777-970f-20ceeeb39e68) ### 02、安装 Redis @@ -37,11 +37,11 @@ Redis 针对不同的操作系统有不同的安装方式,我们这篇入门 Windows 最新的版本是 3.2.100。从下图中可以看得出,Redis 的体积非常的轻量级,还不到 6 M。体积越小,让我感觉 Redis 越牛逼,你是不是也有这种感觉? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/redis/rumen-a6709cca-d3a3-4381-b110-0ff37d384f27) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/redis/rumen-a6709cca-d3a3-4381-b110-0ff37d384f27) 有两种安装方式,第一种是 msi 的方式,双击运行后安装;第二种是免安装,绿色版,只需要把 zip 包解压就可以了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/redis/rumen-f3fc9852-7505-45ed-8ce2-d16f1d888251) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/redis/rumen-f3fc9852-7505-45ed-8ce2-d16f1d888251) 里面有一份英文版的文档——Windows Service Documentation.docx,教我们如何安装 Redis 服务、如何启动、如何关闭,以及如何使用自定义端口启动服务。 @@ -53,7 +53,7 @@ redis-server redis.windows.conf 然后你就会看到 Redis 启动后的欢迎画面,左边这个盒子感觉好有艺术感啊!有小伙伴知道是怎么生成的吗? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/redis/rumen-69b11133-7aac-4f8a-aa1e-9e8af576ad32) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/redis/rumen-69b11133-7aac-4f8a-aa1e-9e8af576ad32) 还有一些其他的提示信息: @@ -74,7 +74,7 @@ String 结构使用非常广泛,比如说把用户的登陆信息转成 JSON 小伙伴们应该都知道,Java 的 String 是不可变的,无法修改。Redis 的 String 是动态的,可以修改的,两者不同哦。关于 Redis 的 String 结构,我觉得老钱的 Redis 教程上讲得非常明白,大家一起拜读下。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/redis/rumen-d9aca13e-053e-4aea-a8cb-d77b01e5035a) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/redis/rumen-d9aca13e-053e-4aea-a8cb-d77b01e5035a) >Redis 的 String 在内部结构实现上类似于 Java 的 ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配。如上图所示,当前字符串实际分配的空间为 capacity,一般高于实际的字符串长度 len。当字符串长度小于 1M 时,扩容是对现有空间的成倍增长;如果长度超过 1M 时,扩容一次只会多增加 1M 的空间。最大长度为 512M。 @@ -84,15 +84,15 @@ String 结构使用非常广泛,比如说把用户的登陆信息转成 JSON Redis 的解压目录下有一个名叫 redis-cli.exe 的文件,这是 Redis 自带的一个客户端工具,可以用来连接之前我们启动好的 Redis 服务。双击启动它。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/redis/rumen-4fd63521-b07e-41c6-bcbd-b7acb2a81344) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/redis/rumen-4fd63521-b07e-41c6-bcbd-b7acb2a81344) 这个客户端还是非常智能的,当键入命令的时候,会跳出对应的提示 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/redis/rumen-6ca5d00d-4b5d-4475-a49c-9937e22f97af) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/redis/rumen-6ca5d00d-4b5d-4475-a49c-9937e22f97af) 当按下空格跟进关键字的时候,对应位置上的提示会自动消失。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/redis/rumen-656ec70a-c053-44ab-b078-a5c77386bee6) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/redis/rumen-656ec70a-c053-44ab-b078-a5c77386bee6) 以下是完整的键值对测试命令,小伙伴们可以按照格式动手实操一把。 @@ -229,7 +229,7 @@ get:null 完全符合我们的预期,perfect! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/redis/rumen-7135d995-f563-4021-b364-411b1be07b5a) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/redis/rumen-7135d995-f563-4021-b364-411b1be07b5a) ### 06、鸣谢 diff --git a/docs/shigu/image-yasuo.md b/docs/shigu/image-yasuo.md index f455d702b..593d8407d 100644 --- a/docs/shigu/image-yasuo.md +++ b/docs/shigu/image-yasuo.md @@ -18,9 +18,9 @@ 图像压缩可以是有损数据压缩,也可以是无损数据压缩。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/shigu/image-yasuo-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/shigu/image-yasuo-1.png) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/shigu/image-yasuo-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/shigu/image-yasuo-2.png) 怎么样? @@ -38,13 +38,13 @@ 数字图像处理(Digital Image Processing)是通过计算机对图像进行去除噪声、增强、复原、分割、提取特征等处理的方法和技术。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/shigu/image-yasuo-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/shigu/image-yasuo-3.png) 输入的是图像信号,然后经过 DIP 进行有效的算法处理后,输出为数字信号。 为了压缩图像,我们需要读取图像并将其转换成 BufferedImage 对象,BufferedImage 是 Image 类的一个子类,描述了一个具有可访问的图像数据缓冲区,由 ColorModel 和 Raster 的图像数据组成。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/shigu/image-yasuo-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/shigu/image-yasuo-4.png) 废话我就不多说了,直接进入实战吧! @@ -52,7 +52,7 @@ 刚好我本地有一张之前用过的封面图,离 1M 只差 236 KB,可以拿来作为测试用。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/shigu/image-yasuo-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/shigu/image-yasuo-5.png) @@ -134,12 +134,12 @@ public class Demo { 执行压缩后,可以看到图片的大小压缩到了 19 KB: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/shigu/image-yasuo-6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/shigu/image-yasuo-6.png) 可以看得出,质量因子为 0.01f 的时候图片已经有些失真了,可以适当提高质量因子比如说 0.5f,再来看一下。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/shigu/image-yasuo-7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/shigu/image-yasuo-7.png) 图片质量明显提高了,但大小依然只有 64 KB,压缩效果还是值得信赖的。 @@ -149,19 +149,19 @@ public class Demo { 1)ImageJ,用 Java 编写的,可以编辑、分析、处理、保存和打印图像。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/shigu/image-yasuo-8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/shigu/image-yasuo-8.png) 2)Apache Commons Imaging,一个读取和写入各种图像格式的库,包括快速解析图像信息(如大小,颜色,空间,ICC配置文件等)和元数据。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/shigu/image-yasuo-9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/shigu/image-yasuo-9.png) 3)ImageMagick,可以读取和写入超过100种格式的图像,包括DPX、EXR、GIF、JPEG、JPEG-2000、PDF、PNG、Postscript、SVG和TIFF。还可以调整大小、翻转、镜像、旋转、扭曲、剪切和变换图像,调整图像颜色,应用各种特殊效果,包括绘制文本、线条、多边形、椭圆和贝塞尔曲线。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/shigu/image-yasuo-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/shigu/image-yasuo-10.png) 4)OpenCV,由BSD许可证发布,可以免费学习和商业使用,提供了包括 C/C++、Python 和 Java 等主流编程语言在内的接口。OpenCV 专为计算效率而设计,强调实时应用,可以充分发挥多核处理器的优势。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/shigu/image-yasuo-11.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/shigu/image-yasuo-11.png) 这里就以 OpenCV 为例,来演示一下图像压缩。当然了,OpenCV 用来压缩图像属于典型的大材小用。 @@ -198,13 +198,13 @@ MatOfInt 的构造参数是一个可变参数,第一个参数 IMWRITE_JPEG_QUA 执行代码后得到的图片如下所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/shigu/image-yasuo-12.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/shigu/image-yasuo-12.png) 借这个机会,来对比下 OpenCV 和 JDK 原生 API 在压缩图像时所使用的时间。 这是我本机的配置情况,早年买的顶配 iMac,也是我的主力机。一开始只有 16 G 内存,后来加了一个 16 G 内存条,不过最近半年电脑突然死机重启的频率明显提高了,不知道是不是 Big Sur 这个操作系统的问题还是电脑硬件老了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/shigu/image-yasuo-13.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/shigu/image-yasuo-13.png) 结果如下所示: @@ -215,7 +215,7 @@ jdkCompress压缩完成,所花时间:322 压缩后的图片大小差不多,都是 19 KB,并且质量因子都是最低值。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/shigu/image-yasuo-14.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/shigu/image-yasuo-14.png) ### 四、一点点心声 @@ -225,7 +225,7 @@ jdkCompress压缩完成,所花时间:322 要知道,我可是连续加班了两天两夜,不眠不休。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/shigu/image-yasuo-15.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/shigu/image-yasuo-15.png) 累到最后,我趴在电脑上都睡着了。 diff --git a/docs/shigu/log4j2.md b/docs/shigu/log4j2.md index 0e6858f81..e5beb8357 100644 --- a/docs/shigu/log4j2.md +++ b/docs/shigu/log4j2.md @@ -29,11 +29,11 @@ Java 后端开发的小伙伴应该都知道,Log4j、SLF4J、Logback 这 3 个 目前,Log4j2 的官网已经发布了 Log4j2 2.15.0 正式版,来解决此次漏洞。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/shigu/log4j2-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/shigu/log4j2-01.png) 那随着 Log4j2 2.15.0 正式版的发布,Spring Boot 的 GitHub 仓库提的这些关于 Log4j2 的 issue 都已经处于关闭状态了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/shigu/log4j2-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/shigu/log4j2-02.png) 看到这些消息后,老王紧张的情绪一下子就缓解了下来,就像吃了一颗定心丸,赶紧去通知小二不用再提心吊胆了,直接一行代码搞定。 @@ -49,7 +49,7 @@ Java 后端开发的小伙伴应该都知道,Log4j、SLF4J、Logback 这 3 个 Gradle 构建的项目也有解决方案。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/shigu/log4j2-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/shigu/log4j2-03.png) 问题是解决了,不过老王没闲着。他从 Log4j2 官网公布的最新消息中琢磨出,本次远程代码执行漏洞正是由于组件存在 Java JNDI 注入漏洞:**当程序将用户输入的数据记录到日志时,攻击者通过构造特殊请求,来触发 Apache Log4j2 中的远程代码执行漏洞,从而利用此漏洞在目标服务器上执行任意代码**。 @@ -89,15 +89,15 @@ public class VulnerableLog4jExampleHandler implements HttpHandler { 下图是程序猿阿朗画的简单的攻击链路步骤图。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/shigu/log4j2-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/shigu/log4j2-04.png) 感兴趣的小伙伴可以在本地复现一下,但**千万不要不当利用**哦! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/shigu/log4j2-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/shigu/log4j2-05.png) 再次提醒大家一下,排查自己的项目是否引入了 Apache log4j-core Jar 包。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/shigu/log4j2-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/shigu/log4j2-06.png) 如果存在依赖引入,且在受影响版本范围内,请升级到 Apache Log4j2 2.15.0 版本,目前已经 release。 diff --git a/docs/sidebar/herongwei/mysql.md b/docs/sidebar/herongwei/mysql.md index d4ecdd040..56520fe68 100644 --- a/docs/sidebar/herongwei/mysql.md +++ b/docs/sidebar/herongwei/mysql.md @@ -138,7 +138,7 @@ Server 层按顺序执行 SQL 的步骤为: ### 13、MySQL 的 redo log 和 binlog 区别? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/herongwei/mysql-a2b8e123-41cb-4717-9225-3a8b49197004.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/herongwei/mysql-a2b8e123-41cb-4717-9225-3a8b49197004.png) ### 14、为什么需要 redo log? @@ -176,7 +176,7 @@ redo log包括两部分内容,分别是内存中的**日志缓冲**(redo log b MySQL 每执行一条 DML 语句,会先把记录写入 **redo log buffer(用户空间)** ,再保存到内核空间的缓冲区 OS-buffer 中,后续某个时间点再一次性将多个操作记录写到 **redo log file(刷盘)** 。这种先写日志,再写磁盘的技术,就是**WAL**。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/herongwei/mysql-f901a97f-9d82-4d4e-a5be-559a64b3d9b8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/herongwei/mysql-f901a97f-9d82-4d4e-a5be-559a64b3d9b8.png) 可以发现,redo log buffer写入到redo log file,是经过OS buffer中转的。其实可以通过参数innodb_flush_log_at_trx_commit进行配置,参数值含义如下: @@ -193,7 +193,7 @@ MySQL 每执行一条 DML 语句,会先把记录写入 **redo log buffer(用 update T set a =1 where id =666 ``` -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/herongwei/mysql-43fe6587-0cb8-49aa-bd93-0119e46430d7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/herongwei/mysql-43fe6587-0cb8-49aa-bd93-0119e46430d7.png) 1. MySQL 客户端将请求语句 update T set a =1 where id =666,发往 MySQL Server 层。 @@ -215,7 +215,7 @@ update T set a =1 where id =666 MySQL 将 redo log 的写入拆成了两个步骤:prepare 和 commit,中间再穿插写入binlog,这就是"两阶段提交"。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/herongwei/mysql-11420486-f9d0-483a-ba2e-a742ec4c518d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/herongwei/mysql-11420486-f9d0-483a-ba2e-a742ec4c518d.png) 而两阶段提交就是让这两个状态保持逻辑上的一致。redolog 用于恢复主机故障时的未更新的物理数据,binlog 用于备份操作。两者本身就是两个独立的个体,要想保持一致,就必须使用分布式事务的解决方案来处理。 @@ -270,7 +270,7 @@ WAL,中文全称是 Write-Ahead Logging,它的关键点就是日志先写内 ### 24、redo log日志格式 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/herongwei/mysql-ee8a859f-d1e8-4ab6-94d1-9733373be825.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/herongwei/mysql-ee8a859f-d1e8-4ab6-94d1-9733373be825.png) redo log buffer (内存中)是由首尾相连的四个文件组成的,它们分别是:ib_logfile_1、ib_logfile_2、ib_logfile_3、ib_logfile_4。 diff --git a/docs/sidebar/sanfene/collection.md b/docs/sidebar/sanfene/collection.md index d458c6d78..72d2fb92c 100644 --- a/docs/sidebar/sanfene/collection.md +++ b/docs/sidebar/sanfene/collection.md @@ -15,7 +15,7 @@ tag: 集合相关类和接口都在java.util中,主要分为3种:List(列表)、Map(映射)、Set(集)。 -![Java集合主要关系](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-1.png) +![Java集合主要关系](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-1.png) 其中`Collection`是集合`List`、`Set`的父接口,它主要有两个子接口: @@ -35,7 +35,7 @@ List,也没啥好问的,但不排除面试官剑走偏锋,比如面试官 - ArrayList基于数组实现 - LinkedList基于双向链表实现 -![ArrayList和LinkedList的数据结构](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-2.png) +![ArrayList和LinkedList的数据结构](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-2.png) **(2)** 多数情况下,ArrayList更利于查找,LinkedList更利于增删 @@ -43,9 +43,9 @@ List,也没啥好问的,但不排除面试官剑走偏锋,比如面试官 - ArrayList增删如果是数组末尾的位置,直接插入或者删除就可以了,但是如果插入中间的位置,就需要把插入位置后的元素都向前或者向后移动,甚至还有可能触发扩容;双向链表的插入和删除只需要改变前驱节点、后继节点和插入节点的指向就行了,不需要移动元素。 -![ArrayList和LinkedList中间插入](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-3.png) +![ArrayList和LinkedList中间插入](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-3.png) -![ArrayList和LinkedList中间删除](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-4.png) +![ArrayList和LinkedList中间删除](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-4.png) > 注意,这个地方可能会出陷阱,LinkedList更利于增删更多是体现在平均步长上,不是体现在时间复杂度上,二者增删的时间复杂度都是O(n) @@ -65,7 +65,7 @@ ArrayList是基于数组的集合,数组的容量是在定义的时候确定 ArrayList的扩容是创建一个**1.5倍**的新数组,然后把原数组的值拷贝过去。 -![ArrayList扩容](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-5.png) +![ArrayList扩容](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-5.png) ### 4.ArrayList怎么序列化的知道吗? 为什么用transient修饰数组? @@ -79,7 +79,7 @@ ArrayList的序列化不太一样,它使用`transient`修饰存储元素的`el ArrayList通过两个方法**readObject、writeObject**自定义序列化和反序列化策略,实际直接使用两个流`ObjectOutputStream`和`ObjectInputStream`来进行序列化和反序列化。 -![ArrayList自定义序列化](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-6.png) +![ArrayList自定义序列化](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-6.png) ### 5.快速失败(fail-fast)和安全失败(fail-safe)了解吗? @@ -115,7 +115,7 @@ CopyOnWriteArrayList就是线程安全版本的ArrayList。 CopyOnWriteArrayList采用了一种读写分离的并发策略。CopyOnWriteArrayList容器允许并发读,读操作是无锁的,性能较高。至于写操作,比如向容器中添加一个元素,则首先将当前容器复制一份,然后在新副本上执行写操作,结束之后再将原容器的引用指向新容器。 -![CopyOnWriteArrayList原理](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-7.png) +![CopyOnWriteArrayList原理](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-7.png) @@ -133,7 +133,7 @@ JDK1.8的数据结构是`数组`+`链表`+`红黑树`。 数据结构示意图如下: -![jdk1.8 hashmap数据结构示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-8.png) +![jdk1.8 hashmap数据结构示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-8.png) 其中,桶数组是用来存储数据元素,链表是用来解决冲突,红黑树是为了提高查询的效率。 @@ -152,7 +152,7 @@ JDK1.8的数据结构是`数组`+`链表`+`红黑树`。 4. 每个红色节点的两个子节点一定都是黑色; 5. 从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点; -![红黑树](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-9.png) +![红黑树](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-9.png) > 之所以不用二叉树: @@ -168,19 +168,19 @@ JDK1.8的数据结构是`数组`+`链表`+`红黑树`。 - 旋转:旋转分为两种,左旋和右旋 -![左旋](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-10.png) +![左旋](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-10.png) -![右旋](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-11.png) +![右旋](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-11.png) - 染⾊: -![染色](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-12.png) +![染色](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-12.png) ### 11.HashMap的put流程知道吗? 先上个流程图吧: -![HashMap插入数据流程图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-13.jpg) +![HashMap插入数据流程图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-13.jpg) 1. 首先进行哈希值的扰动,获取一个新的哈希值。`(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);` @@ -204,7 +204,7 @@ JDK1.8的数据结构是`数组`+`链表`+`红黑树`。 先看流程图: -![HashMap查找流程图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-14.png) +![HashMap查找流程图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-14.png) @@ -250,13 +250,13 @@ static int indexFor(int h, int length) { 顺便说一下,这也正好解释了为什么 HashMap 的数组长度要取 2 的整数幂。因为这样(数组长度 - 1)正好相当于一个 “低位掩码”。`与` 操作的结果就是散列值的高位全部归零,只保留低位值,用来做数组下标访问。以初始长度 16 为例,16-1=15。2 进制表示是` 0000 0000 0000 0000 0000 0000 0000 1111`。和某个散列值做 `与` 操作如下,结果就是截取了最低的四位值。 -![哈希&运算](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-15.png) +![哈希&运算](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-15.png) 这样是要快捷一些,但是新的问题来了,就算散列值分布再松散,要是只取最后几位的话,碰撞也会很严重。如果散列本身做得不好,分布上成等差数列的漏洞,如果正好让最后几个低位呈现规律性重复,那就更难搞了。 这时候 `扰动函数` 的价值就体现出来了,看一下扰动函数的示意图: -![扰动函数示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-16.jpg) +![扰动函数示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-16.jpg) 右移 16 位,正好是 32bit 的一半,自己的高半区和低半区做异或,就是为了混合原始哈希码的高位和低位,以此来加大低位的随机性。而且混合后的低位掺杂了高位的部分特征,这样高位的信息也被变相保留下来。 @@ -272,7 +272,7 @@ HashMap的容量是2的n次幂时,(n-1)的2进制也就是1111111***111这样 我们可以简单看看HashMap的扩容机制,HashMap中的元素在超过`负载因子*HashMap`大小时就会产生扩容。 -![put中的扩容](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-17.png) +![put中的扩容](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-17.png) ### 16.如果初始化HashMap,传一个17的值`new HashMap<>`,它会怎么处理? @@ -307,7 +307,7 @@ static final int tableSizeFor(int cap) { 以17为例,看一下初始化计算table容量的过程: -![容量计算](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-18.png) +![容量计算](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-18.png) ### 17.你还知道哪些哈希函数的构造方法呢? @@ -333,7 +333,7 @@ HashMap里哈希构造函数的方法叫: 将`key`分割成位数相同的几段,然后把它们的叠加和作为映射的位置 -![散列函数构造](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-19.png) +![散列函数构造](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-19.png) ### 18.解决哈希冲突有哪些方法呢? @@ -351,7 +351,7 @@ HashMap里哈希构造函数的方法叫: - 平方探查法: 从冲突的位置x开始,第一次增加`1^2`个位置,第二次增加`2^2`…,直至找到空闲的位置 - …… -![开放定址法](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-20.png) +![开放定址法](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-20.png) - **再哈希法**:换种哈希函数,重新计算冲突元素的地址。 - **建立公共溢出区**:再建一个数组,把冲突的元素放进去。 @@ -362,7 +362,7 @@ HashMap里哈希构造函数的方法叫: 为什么是8呢?源码的注释也给出了答案。 -![源码注释](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-21.png) +![源码注释](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-21.png) 红黑树节点的大小大概是普通节点大小的两倍,所以转红黑树,牺牲了空间换时间,更多的是一种兜底的策略,保证极端情况下的查找效率。 @@ -374,13 +374,13 @@ HashMap里哈希构造函数的方法叫: 为了减少哈希冲突发生的概率,当当前HashMap的元素个数达到一个临界值的时候,就会触发扩容,把所有元素rehash之后再放在扩容后的容器中,这是一个相当耗时的操作。 -![put时,扩容](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-22.png) +![put时,扩容](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-22.png) 而这个`临界值threshold`就是由加载因子和当前容器的容量大小来确定的,假如采用默认的构造方法: > 临界值(threshold )= 默认容量(DEFAULT_INITIAL_CAPACITY) * 默认扩容因子(DEFAULT_LOAD_FACTOR) -![threshold计算](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-23.png) +![threshold计算](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-23.png) 那就是大于`16x0.75=12`时,就会触发扩容操作。 @@ -390,7 +390,7 @@ HashMap里哈希构造函数的方法叫: 在HashMap中有这样一段注释: -![关于默认负载因子的注释](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-24.png) +![关于默认负载因子的注释](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-24.png) 我们都知道,HashMap的散列构造方式是Hash取余,负载因子决定元素个数达到多少时候扩容。 @@ -408,19 +408,19 @@ HashMap是基于数组+链表和红黑树实现的,但用于存放key值的桶 看下这张图,n为table的长度,图`a`表示扩容前的key1和key2两种key确定索引的位置,图`b`表示扩容后key1和key2两种key确定索引位置。 -![扩容之后的索引计算](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-25.png) +![扩容之后的索引计算](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-25.png) 元素在重新计算hash之后,因为n变为2倍,那么n-1的mask范围在高位多1bit(红色),因此新的index就会发生这样的变化: -![扩容位置变化](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-26.png) +![扩容位置变化](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-26.png) 所以在扩容时,只需要看原来的hash值新增的那一位是0还是1就行了,是0的话索引没变,是1的化变成`原索引+oldCap`,看看如16扩容为32的示意图: -![扩容节点迁移示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-27.png) +![扩容节点迁移示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-27.png) 扩容节点迁移主要逻辑: -![扩容主要逻辑](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-28.png) +![扩容主要逻辑](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-28.png) ### 22.jdk1.8对HashMap主要做了哪些优化呢?为什么? @@ -458,11 +458,11 @@ jdk1.8 的HashMap主要有五点优化: - 冲突解决:链地址法 - 扩容:节点重新hash获取位置 -![自定义HashMap整体结构](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-29.png) +![自定义HashMap整体结构](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-29.png) 完整代码: -![完整代码](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-30.png) +![完整代码](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-30.png) ### 24.HashMap 是线程安全的吗?多线程下会有什么问题? @@ -492,7 +492,7 @@ ConcurrentHashmap线程安全在jdk1.7版本是基于`分段锁`实现,在jdk1 实际上就是相当于每个Segment都是一个HashMap,默认的Segment长度是16,也就是支持16个线程的并发写,Segment之间相互不会受到影响。 -![1.7ConcurrentHashMap示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-31.png) +![1.7ConcurrentHashMap示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-31.png) **put流程** @@ -600,7 +600,7 @@ final Node[] helpTransfer(Node[] tab, Node f) { -![ConcurrentHashmap jdk1.8put流程](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-32.jpg) +![ConcurrentHashmap jdk1.8put流程](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-32.jpg) **get查询** @@ -614,11 +614,11 @@ HashMap是无序的,根据 hash 值随机插入。如果想使用有序的Map LinkedHashMap维护了一个双向链表,有头尾节点,同时 LinkedHashMap 节点 Entry 内部除了继承 HashMap 的 Node 属性,还有 before 和 after 用于标识前置节点和后置节点。 -![Entry节点](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-33.png) +![Entry节点](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-33.png) 可以实现按插入的顺序或访问顺序排序。 -![LinkedHashMap实现原理](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-34.png) +![LinkedHashMap实现原理](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-34.png) @@ -626,7 +626,7 @@ HashMap是无序的,根据 hash 值随机插入。如果想使用有序的Map TreeMap 是按照 Key 的自然顺序或者 Comprator 的顺序进行排序,内部是通过红黑树来实现。所以要么 key 所属的类实现 Comparable 接口,或者自定义一个实现了 Comparator 接口的比较器,传给 TreeMap 用于 key 的比较。 -![TreeMap](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-35.png) +![TreeMap](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-35.png) ## Set @@ -644,7 +644,7 @@ public boolean add(E e) { } ```` -![HashSet套娃](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/collection-36.png) +![HashSet套娃](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/collection-36.png) 而在HashMap的putVal方法中,进行了一系列判断,最后的结果是,只有在key在table数组中不存在的时候,才会返回插入的值。 diff --git a/docs/sidebar/sanfene/javase.md b/docs/sidebar/sanfene/javase.md index 6807691e9..b1472fc96 100644 --- a/docs/sidebar/sanfene/javase.md +++ b/docs/sidebar/sanfene/javase.md @@ -13,7 +13,7 @@ tag: ### 1.什么是 Java? -![下辈子还学Java](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-1.png) +![下辈子还学Java](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-1.png) PS:碎怂 Java,有啥好介绍的。哦,面试啊。 @@ -23,7 +23,7 @@ Java 是一门面向对象的编程语言,不仅吸收了 C++语言的各种 Java 语言有很多优秀(可吹)的特点,以下几个是比较突出的: -![Java语言特点](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-2.png) +![Java语言特点](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-2.png) - 面向对象(封装,继承,多态); - 平台无关性,平台无关性的具体表现在于,Java 是“一次编写,到处运行(Write Once,Run any Where)”的语言,因此采用 Java 语言编写的程序具有很好的可移植性,而保证这一点的正是 Java 的虚拟机机制。在引入虚拟机之后,Java 语言在不同的平台上运行不需要重新编译。 @@ -40,7 +40,7 @@ Java 语言有很多优秀(可吹)的特点,以下几个是比较突出的 简单来说,JDK 包含 JRE,JRE 包含 JVM。 -![JDK、JRE、JVM关系](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-3.png) +![JDK、JRE、JVM关系](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-3.png) ### 4.说说什么是跨平台性?原理是什么 @@ -58,7 +58,7 @@ Java 语言有很多优秀(可吹)的特点,以下几个是比较突出的 - **解释**:虚拟机执行 Java 字节码,将字节码翻译成机器能识别的机器码 - **执行**:对应的机器执行二进制机器码 -![Java程序执行过程](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-4.png) +![Java程序执行过程](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-4.png) 只需要把 Java 程序编译成 Java 虚拟机能识别的 Java 字节码,不同的平台安装对应的 Java 虚拟机,这样就可以可以实现 Java 语言的平台无关性。 @@ -72,7 +72,7 @@ Java 语言有很多优秀(可吹)的特点,以下几个是比较突出的 Java 语言既具有编译型语言的特征,也具有解释型语言的特征,因为 Java 程序要经过先编译,后解释两个步骤,由 Java 编写的程序需要先经过编译步骤,生成字节码(`\*.class` 文件),这种字节码必须再经过 JVM,解释成操作系统能识别的机器码,在由操作系统执行。因此,我们可以认为 Java 语言**编译**与**解释**并存。 -![编译与解释](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-5.png) +![编译与解释](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-5.png) @@ -84,7 +84,7 @@ Java 语言既具有编译型语言的特征,也具有解释型语言的特征 Java 语言数据类型分为两种:**基本数据类型**和**引用数据类型**。 -![Java数据类型](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-6.png) +![Java数据类型](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-6.png) 基本数据类型: @@ -117,7 +117,7 @@ Java 基本数据类型范围和默认值: Java 所有的数值型变量可以相互转换,当把一个表数范围小的数值或变量直接赋给另一个表数范围大的变量时,可以进行自动类型转换;反之,需要强制转换。 -![Java自动类型转换方向](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-7.png) +![Java自动类型转换方向](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-7.png) 这就好像,小杯里的水倒进大杯没问题,但大杯的水倒进小杯就不行了,可能会溢出。 @@ -138,7 +138,7 @@ Java 所有的数值型变量可以相互转换,当把一个表数范围小的 Java 可以自动对基本数据类型和它们的包装类进行装箱和拆箱。 -![装箱和拆箱](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-8.png) +![装箱和拆箱](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-8.png) 举例: @@ -173,7 +173,7 @@ Java5 以前 switch(expr)中,expr 只能是 byte、short、char、int。 - continue 跳出本次循环,继续执行下次循环(**结束正在执行的循环 进入下一个循环条件**) - return 程序返回,不再执行下面的代码(**结束当前的方法 直接返回**) -![break 、continue 、return](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-9.png) +![break 、continue 、return](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-9.png) ### 13.用最有效率的方法计算 2 乘以 8? @@ -250,11 +250,11 @@ PS:笔试面试可能会碰到的奇葩题,开发这么写,见一次吊一 用一个比喻:面向过程是编年体;面向对象是纪传体。 -![面向对象和面向过程的区别](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-10.png) +![面向对象和面向过程的区别](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-10.png) ### 16.面向对象有哪些特性 -![面向对象三大特征](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-11.png) +![面向对象三大特征](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-11.png) - 封装 @@ -301,7 +301,7 @@ Java 中,可以使用访问控制符来保护对类、变量、方法和构造 - **public** : 对所有类可见。可以修饰类、接口、变量、方法 - **protected** : 对同一包内的类和所有子类可见。可以修饰变量、方法。**注意:不能修饰类(外部类)**。 -![访问修饰符和可见性](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-12.png) +![访问修饰符和可见性](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-12.png) ### 19.this 关键字有什么作用? @@ -392,7 +392,7 @@ System.out.println(sb); //abcd 一张图说明: -![final修饰变量](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-13.png) +![final修饰变量](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-13.png) ### 25.final、finally、finalize 的区别? @@ -475,7 +475,7 @@ JVM 的内存分为堆和栈,其中栈中存储了基本数据类型和引用 而对象所占的空间是在堆中开辟的,所以传递的时候可以理解为把变量存储的对象地址给传递过去,因此引用类型也是值传递。 -![Java引用数据值传递示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-14.png) +![Java引用数据值传递示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-14.png) ### 29.深拷贝和浅拷贝? @@ -484,7 +484,7 @@ JVM 的内存分为堆和栈,其中栈中存储了基本数据类型和引用 例如现在有一个 order 对象,里面有一个 products 列表,它的浅拷贝和深拷贝的示意图: -![浅拷贝和深拷贝示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-15.png) +![浅拷贝和深拷贝示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-15.png) 因此深拷贝是安全的,浅拷贝的话如果有引用类型,那么拷贝后对象,引用类型变量修改,会影响原对象。 @@ -501,7 +501,7 @@ Object 类提供的 clone()方法可以非常简单地实现对象的浅拷贝 Java 中有以下四种创建对象的方式: -![Java创建对象的四种方式](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-16.png) +![Java创建对象的四种方式](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-16.png) - new 创建新对象 - 通过反射机制 @@ -536,7 +536,7 @@ String 是一个比较特殊的引用数据类型。 两个语句都会去字符串常量池中检查是否已经存在 “abc”,如果有则直接使用,如果没有则会在常量池中创建 “abc” 对象。 -![堆与常量池中的String](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-17.png) +![堆与常量池中的String](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-17.png) 但是不同的是,String str1 = new String("abc") 还会通过 new String() 在堆里创建一个 "abc" 字符串对象实例。所以后者可以理解为被前者包含。 @@ -565,7 +565,7 @@ String ab = a + b; 内存如下: -![jdk1.8之前的字符串拼接](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-18.png) +![jdk1.8之前的字符串拼接](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-18.png) 在**Java8 时**JDK 对“+”号拼接进行了优化,上面所写的拼接方式会被优化为基于 StringBuilder 的 append 方法进行处理。Java 会在编译期对“+”号进行处理。 @@ -659,7 +659,7 @@ Integer a= 127 这种赋值,是用到了 Integer 自动装箱的机制。自 实现的原理是 int 在自动装箱的时候会调用 Integer.valueOf,进而用到了 IntegerCache。 -![Integer.valueOf](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-19.png) +![Integer.valueOf](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-19.png) 很简单,就是判断下值是否在缓存范围之内,如果是的话去 IntegerCache 中取,不是的话就创建一个新的 Integer 对象。 @@ -722,7 +722,7 @@ public static int parseInt(String s, int radix) 去掉枝枝蔓蔓(当然这些枝枝蔓蔓可以去看看,源码 cover 了很多情况),其实剩下的就是一个简单的字符串遍历计算,不过计算方式有点反常规,是用负的值累减。 -![parseInt示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-20.png) +![parseInt示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-20.png) @@ -732,7 +732,7 @@ public static int parseInt(String s, int radix) Object 类是一个特殊的类,是所有类的父类,也就是说所有类都可以调用它的方法。它主要提供了以下 11 个方法,大概可以分为六类: -![Object类的方法](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-21.png) +![Object类的方法](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-21.png) **对象比较**: @@ -769,7 +769,7 @@ Object 类是一个特殊的类,是所有类的父类,也就是说所有类 Java 的异常体系是分为多层的。 -![Java异常体系](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-22.png) +![Java异常体系](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-22.png) `Throwable`是 Java 语言中所有错误或异常的基类。 Throwable 又分为`Error`和`Exception`,其中 Error 是系统内部错误,比如虚拟机异常,是程序无法处理的。`Exception`是程序问题导致的异常,又分为两种: @@ -780,7 +780,7 @@ Java 的异常体系是分为多层的。 针对异常的处理主要有两种方式: -![异常处理](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-23.png) +![异常处理](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-23.png) - **遇到异常不进行具体处理,而是继续抛给调用者 (throw,throws)** @@ -892,7 +892,7 @@ Java Io 流共涉及 40 多个类,看上去杂乱,其实都存在一定的 - **InputStream**/**Reader**: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。 - **OutputStream**/**Writer**: 所有输出流的基类,前者是字节输出流,后者是字符输出流。 -![IO-操作方式分类-图片来源参考[2]](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-24.jpeg) +![IO-操作方式分类-图片来源参考[2]](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-24.jpeg) > IO 流用到了什么设计模式? @@ -900,7 +900,7 @@ Java Io 流共涉及 40 多个类,看上去杂乱,其实都存在一定的 InputStream 相关的部分类图如下,篇幅有限,装饰器模式就不展开说了。 -![Java IO流用到装饰器模式](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-25.png) +![Java IO流用到装饰器模式](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-25.png) ### 43.既然有了字节流,为什么还要有字符流? @@ -910,11 +910,11 @@ InputStream 相关的部分类图如下,篇幅有限,装饰器模式就不 ### 44.BIO、NIO、AIO? -![BIO、NIO、AIO](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-26.png) +![BIO、NIO、AIO](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-26.png) **BIO**(blocking I/O) : 就是传统的 IO,同步阻塞,服务器实现模式为一个连接一个线程,即**客户端有连接请求时服务器端就需要启动一个线程进行处理**,如果这个连接不做任何事情会造成不必要的线程开销,可以通过连接池机制改善(实现多个客户连接服务器)。 -![BIO、NIO、AIO](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-27.png) +![BIO、NIO、AIO](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-27.png) BIO 方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4 以前的唯一选择,程序简单易理解。 @@ -922,13 +922,13 @@ BIO 方式适用于连接数目比较小且固定的架构,这种方式对服 NIO 是**同步非阻塞**的,服务器端用一个线程处理多个连接,客户端发送的连接请求会注册到多路复用器上,多路复用器轮询到连接有 IO 请求就进行处理: -![NIO线程](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-28.png) +![NIO线程](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-28.png) NIO 的数据是面向**缓冲区 Buffer**的,必须从 Buffer 中读取或写入。 所以完整的 NIO 示意图: -![NIO完整示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-29.png) +![NIO完整示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-29.png) 可以看出,NIO 的运行机制: @@ -948,7 +948,7 @@ NIO 的数据是面向**缓冲区 Buffer**的,必须从 Buffer 中读取或写 所以**反序列化就是把二进制流恢复成对象**。 -![序列化和反序列化](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-30.png) +![序列化和反序列化](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-30.png) 类比我们生活中一些大件物品的运输,运输的时候把它拆了打包,用的时候再拆包组装。 @@ -988,7 +988,7 @@ private static final long serialVersionUID = 1L; Java 序列化方式有很多,常见的有三种: -![Java常见序列化方式](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-31.png) +![Java常见序列化方式](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-31.png) - Java 对象流列化 :Java 原生序列化方法即通过 Java 原生流(InputStream 和 OutputStream 之间的转化)的方式进行转化,一般是对象输出流 `ObjectOutputStream`和对象输入流`ObjectI叩utStream`。 - Json 序列化:这个可能是我们最常用的序列化方式,Json 序列化的选择很多,一般会使用 jackson 包,通过 ObjectMapper 类来进行一些操作,比如将对象转化为 byte 数组或者将 json 串转化为对象。 @@ -1018,7 +1018,7 @@ System.out.println(list); 泛型一般有三种使用方式:**泛型类**、**泛型接口**、**泛型方法**。 -![泛型类、泛型接口、泛型方法](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-32.png) +![泛型类、泛型接口、泛型方法](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-32.png) **1.泛型类**: @@ -1132,7 +1132,7 @@ list.add(new Dog()); **Java 注解本质上是一个标记**,可以理解成生活中的一个人的一些小装扮,比如戴什么什么帽子,戴什么眼镜。 -![Java注解和帽子](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-33.png) +![Java注解和帽子](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-33.png) 注解可以标记在类上、方法上、属性上等,标记自身也可以设置一些值,比如帽子颜色是绿色。 @@ -1150,13 +1150,13 @@ list.add(new Dog()); 像常见的: -![Override注解](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-34.png) +![Override注解](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-34.png) 就是给编译器用的,编译器编译的时候检查没问题就 over 了,class 文件里面不会有 Override 这个标记。 再比如 Spring 常见的 Autowired ,就是 RUNTIME 的,所以**在运行的时候可以通过反射得到注解的信息**,还能拿到标记的值 required 。 -![Autowired注解](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-35.png) +![Autowired注解](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-35.png) ## 反射 @@ -1172,7 +1172,7 @@ list.add(new Dog()); 反射最核心的四个类: -![Java反射相关类](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-36.png) +![Java反射相关类](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-36.png) > 反射的应用场景? @@ -1198,7 +1198,7 @@ JDK 已经出到 17 了,但是你迭代你的版本,我用我的 8。JDK1.8 JDK1.8 有不少新特性,我们经常接触到的新特性如下: -![JDK1.8主要新特性](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-37.png) +![JDK1.8主要新特性](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-37.png) - 接口默认方法:Java 8 允许我们给接口添加一个非抽象的方法实现,只需要使用 default 关键字修饰即可 @@ -1375,7 +1375,7 @@ reduced.ifPresent(System.out::println); 以上是常见的几种流式操作,还有其它的一些流式操作,可以帮助我们更便捷地处理集合数据。 -![Java Stream流](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javase-38.png) +![Java Stream流](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javase-38.png) diff --git a/docs/sidebar/sanfene/javathread.md b/docs/sidebar/sanfene/javathread.md index 6791d2720..eb9f39af9 100644 --- a/docs/sidebar/sanfene/javathread.md +++ b/docs/sidebar/sanfene/javathread.md @@ -20,11 +20,11 @@ tag: - 并行就是同一时刻,两个线程都在执行。这就要求有两个CPU去分别执行两个线程。 - 并发就是同一时刻,只有一个执行,但是一个时间段内,两个线程都执行了。并发的实现依赖于CPU切换线程,因为切换的时间特别短,所以基本对于用户是无感知的。 -![并行和并发](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-1.png) +![并行和并发](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-1.png) 就好像我们去食堂打饭,并行就是我们在多个窗口排队,几个阿姨同时打菜;并发就是我们挤在一个窗口,阿姨给这个打一勺,又手忙脚乱地给那个打一勺。 -![并行并发和食堂打饭](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-2.png) +![并行并发和食堂打饭](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-2.png) ### 2.说说什么是进程和线程? @@ -37,7 +37,7 @@ tag: 比如在Java中,当我们启动 main 函数其实就启动了一个JVM进程,而 main 函数在的线程就是这个进程中的一个线程,也称主线程。 -![程序进程线程关系](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-3.png) +![程序进程线程关系](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-3.png) 一个进程中有多个线程,多个线程共用进程的堆和方法区资源,但是每个线程有自己的程序计数器和栈。 @@ -45,7 +45,7 @@ tag: Java中创建线程主要有三种方式,分别为继承Thread类、实现Runnable接口、实现Callable接口。 -![线程创建三种方式](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-4.png) +![线程创建三种方式](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-4.png) - 继承Thread类,重写run()方法,调用start()方法启动线程 @@ -119,13 +119,13 @@ public class CallerTask implements Callable { JVM执行start方法,会先创建一条线程,由创建出来的新线程去执行thread的run方法,这才起到多线程的效果。 -![start方法](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-5.png) +![start方法](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-5.png) **为什么我们不能直接调用run()方法?**也很清楚, 如果直接调用Thread的run()方法,那么run方法还是运行在主线程中,相当于顺序执行,就起不到多线程的效果。 ### 5.线程有哪些常用的调度方法? -![线程常用调度方法](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-6.png) +![线程常用调度方法](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-6.png) **线程等待与通知** @@ -182,17 +182,17 @@ Java 中的线程中断是一种线程间的协作模式,通过设置线程的 线程在自身的生命周期中, 并不是固定地处于某个状态,而是随着代码的执行在不同的状态之间进行切换,Java线程状态变化如图示: -![Java线程状态变化](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-7.png) +![Java线程状态变化](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-7.png) ### 7.什么是线程上下文切换? 使用多线程的目的是为了充分利用CPU,但是我们知道,并发其实是一个CPU来应付多个线程。 -![线程切换-2020-12-16-2107](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-8.png) +![线程切换-2020-12-16-2107](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-8.png) 为了让用户感觉多个线程是在同时执行的, CPU 资源的分配采用了时间片轮转也就是给每个线程分配一个时间片,线程在时间片内占用 CPU 执行任务。当线程使用完时间片后,就会处于就绪状态并让出 CPU 让其他线程占用,这就是上下文切换。 -![上下文切换时机](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-9.png) +![上下文切换时机](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-9.png) ### 8.守护线程了解吗? @@ -204,7 +204,7 @@ Java中的线程分为两类,分别为 daemon 线程(守护线程)和 user ### 9.线程间有哪些通信方式? -![线程间通信方式](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-10.png) +![线程间通信方式](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-10.png) - **volatile和synchronized关键字** @@ -245,7 +245,7 @@ ThreadLocal其实应用场景不是很多,但却是被炸了千百遍的面试 ThreadLocal,也就是线程本地变量。如果你创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个本地拷贝,多个线程操作这个变量的时候,实际是操作自己本地内存里面的变量,从而起到线程隔离的作用,避免了线程安全问题。 -![ThreadLocal线程副本](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-11.png) +![ThreadLocal线程副本](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-11.png) - 创建 @@ -284,7 +284,7 @@ localVariable.get(); 这时候我们就可以用到ThreadLocal,在控制层拦截请求把用户信息存入ThreadLocal,这样我们在任何一个地方,都可以取出ThreadLocal中存的用户数据。 -![ThreadLoca存放用户上下文](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-12.png) +![ThreadLoca存放用户上下文](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-12.png) 很多其它场景的cookie、session等等数据隔离也都可以通过ThreadLocal去实现。 @@ -346,7 +346,7 @@ public WeakReference(T referent) { key的赋值,使用的是WeakReference的赋值。 -![ThreadLoca结构图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-13.png) +![ThreadLoca结构图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-13.png) > 所以,怎么回答ThreadLocal原理?要答出这几个点: @@ -361,7 +361,7 @@ key的赋值,使用的是WeakReference的赋值。 所以呢,栈中存储了ThreadLocal、Thread的引用,堆中存储了它们的具体实例。 -![ThreadLocal内存分配](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-14.png) +![ThreadLocal内存分配](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-14.png) ThreadLocalMap中使用的 key 为 ThreadLocal 的弱引用。 @@ -394,7 +394,7 @@ key设计成弱引用同样是为了防止内存泄漏。 ThreadLocalMap虽然被叫做Map,其实它是没有实现Map接口的,但是结构还是和HashMap比较类似的,主要关注的是两个要素:`元素数组`和`散列方法`。 -![ThreadLocalMap结构示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-15.png) +![ThreadLocalMap结构示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-15.png) - 元素数组 @@ -430,7 +430,7 @@ int i = key.threadLocalHashCode & (table.length - 1); ThreadLocalMap没有使用链表,自然也不是用链地址法来解决冲突了,它用的是另外一种方式——**开放定址法**。开放定址法是什么意思呢?简单来说,就是这个坑被人占了,那就接着去找空着的坑。 -![ThreadLocalMap解决冲突](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-16.png) +![ThreadLocalMap解决冲突](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-16.png) 如上图所示,如果我们插入一个value=27的数据,通过 hash计算后应该落入第 4 个槽位中,而槽位 4 已经有了 Entry数据,而且Entry数据的key和当前不相等。此时就会线性向后查找,一直找到 Entry为 null的槽位才会停止查找,把元素放到空的槽中。 @@ -472,13 +472,13 @@ private void expungeStaleEntries() { 接着看看具体的`resize()`方法,扩容后的`newTab`的大小为老数组的两倍,然后遍历老的table数组,散列方法重新计算位置,开放地址解决冲突,然后放到新的`newTab`,遍历完成之后,`oldTab`中所有的`entry`数据都已经放入到`newTab`中了,然后table引用指向`newTab` -![ThreadLocalMap扩容](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-17.png) +![ThreadLocalMap扩容](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-17.png) 具体代码: -![ThreadLocalMap resize](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-18.png) +![ThreadLocalMap resize](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-18.png) ### 17.父子线程怎么共享数据? @@ -536,11 +536,11 @@ JMM定义了线程和主内存之间的抽象关系:线程之间的共享变 Java内存模型的抽象图: -![Java内存模型](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-19.png) +![Java内存模型](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-19.png) 本地内存是JMM的 一个抽象概念,并不真实存在。它其实涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。 -![实际线程工作模型](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-20.png) +![实际线程工作模型](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-20.png) 图里面的是一个双核 CPU 系统架构 ,每个核有自己的控制器和运算器,其中控制器包含一组寄存器和操作控制器,运算器执行算术逻辅运算。每个核都有自己的一级缓存,在有些架构里面还有一个所有 CPU 共享的二级缓存。 那么 Java 内存模型里面的工作内存,就对应这里的 Ll 缓存或者 L2 缓存或者 CPU 寄存器。 @@ -581,11 +581,11 @@ i = i + 1; 从Java源代码到最终实际执行的指令序列,会分别经历下面3种重排序,如图: -![多级指令重排](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-21.png) +![多级指令重排](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-21.png) 我们比较熟悉的双重校验单例模式就是一个经典的指令重排的例子,`Singleton instance=new Singleton();`对应的JVM指令分为三步:分配内存空间-->初始化对象--->对象指向分配的内存空间,但是经过了编译器的指令重排序,第二步和第三步就可能会重排序。 -![双重校验单例模式异常情形](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-22.png) +![双重校验单例模式异常情形](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-22.png) JMM属于语言级的内存模型,它确保在不同的编译器和不同的处理器平台之上,通过禁止特定类型的编译器重排序和处理器重排序,为程序员提供一致的内存可见性保证。 @@ -600,7 +600,7 @@ happens-before的定义: happens-before和我们息息相关的有六大规则: -![happens-before六大规则](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-23.png) +![happens-before六大规则](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-23.png) - **程序顺序规则**:一个线程中的每个操作,happens-before于该线程中的任意后续操作。 - **监视器锁规则**:对一个锁的解锁,happens-before于随后对这个锁的加锁。 @@ -623,13 +623,13 @@ double area = pi * r * r; // C 上面3个操作的数据依赖关系: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-24.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-24.png) A和C之间存在数据依赖关系,同时B和C之间也存在数据依赖关系。因此在最终执行的指令序列中,C不能被重排序到A和B的前面(C排到A和B的前面,程序的结果将会被改变)。但A和B之间没有数据依赖关系,编译器和处理器可以重排序A和B之间的执行顺序。 所以最终,程序可能会有两种执行顺序: -![两种执行结果](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-25.png) +![两种执行结果](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-25.png) as-if-serial语义把单线程程序保护了起来,遵守as-if-serial语义的编译器、runtime和处理器共同编织了这么一个“楚门的世界”:单线程程序是按程序的“顺序”来执行的。as- if-serial语义使单线程情况下,我们不需要担心重排序的问题,可见性的问题。 @@ -645,13 +645,13 @@ volatile可以确保对某个变量的更新对其他线程马上可见,一个 例如,我们声明一个 volatile 变量 volatile int x = 0,线程A修改x=1,修改完之后就会把新的值刷新回主内存,线程B读取x的时候,就会清空本地内存变量,然后再从主内存获取最新值。 -![volatile内存可见性](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-26.png) +![volatile内存可见性](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-26.png) > volatile怎么保证有序性的呢? 重排序可以分为编译器重排序和处理器重排序,valatile保证有序性,就是通过分别限制这两种类型的重排序。 -![volatile重排序规则表](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-27.png) +![volatile重排序规则表](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-27.png) 为了实现volatile的内存语义,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。 @@ -660,9 +660,9 @@ volatile可以确保对某个变量的更新对其他线程马上可见,一个 3. 在每个volatile读操作的后面插入一个`LoadLoad`屏障 4. 在每个volatile读操作的后面插入一个`LoadStore`屏障 -![volatile写插入内存屏障后生成的指令序列示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-28.png) +![volatile写插入内存屏障后生成的指令序列示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-28.png) -![volatile写插入内存屏障后生成的指令序列示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-29.png) +![volatile写插入内存屏障后生成的指令序列示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-29.png) @@ -710,13 +710,13 @@ synchronized(this) { 反编译一段synchronized修饰代码块代码,`javap -c -s -v -l SynchronizedDemo.class`,可以看到相应的字节码指令。 -![monitorenter和monitorexit](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-30.png) +![monitorenter和monitorexit](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-30.png) 2. synchronized修饰同步方法时,JVM采用`ACC_SYNCHRONIZED`标记符来实现同步,这个标识指明了该方法是一个同步方法。 同样可以写段代码反编译看一下。 -![synchronized修饰同步方法](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-31.png) +![synchronized修饰同步方法](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-31.png) > synchronized锁住的是什么呢? @@ -762,7 +762,7 @@ ObjectMonitor() { - 就诊结束后,**走出就诊室**,候诊室的**下一位候诊患者**进入就诊室。 -![就诊-图片来源参考[18]](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-32.png) +![就诊-图片来源参考[18]](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-32.png) 这个过程就和Monitor机制比较相似: @@ -770,7 +770,7 @@ ObjectMonitor() { - **就诊室**:就诊室**_Owner**里里只能有一个线程就诊,就诊完线程就自行离开 - **候诊室**:就诊室繁忙时,进入**等待区(Wait Set)**,就诊室空闲的时候就从**等待区(Wait Set)**叫新的线程 -![Java Montior机制](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-33.png) +![Java Montior机制](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-33.png) 所以我们就知道了,同步是锁住的什么东西: @@ -809,7 +809,7 @@ Java对象头里,有一块结构,叫`Mark Word`标记字段,这块结构 64 位虚拟机 Mark Word 是 64bit,我们来看看它的状态变化: -![Mark Word变化](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-34.png) +![Mark Word变化](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-34.png) @@ -830,7 +830,7 @@ Mark Word存储对象自身的运行数据,如**哈希码、GC分代年龄、 锁升级方向:无锁-->偏向锁---> 轻量级锁---->重量级锁,这个方向基本上是不可逆的。 -![锁升级方向](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-35.png) +![锁升级方向](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-35.png) 我们看一下升级的过程: @@ -863,11 +863,11 @@ Mark Word存储对象自身的运行数据,如**哈希码、GC分代年龄、 大体上省简的升级过程: -![锁升级简略过程](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-36.png) +![锁升级简略过程](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-36.png) 完整的升级过程: -![synchronized 锁升级过程-来源参考[14]](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-37.png) +![synchronized 锁升级过程-来源参考[14]](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-37.png) @@ -885,7 +885,7 @@ Mark Word存储对象自身的运行数据,如**哈希码、GC分代年龄、 下面的表格列出出了两种锁之间的区别: -![synchronized和ReentrantLock的区别](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-38.png) +![synchronized和ReentrantLock的区别](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-38.png) ### 29.AQS了解多少? @@ -896,13 +896,13 @@ AbstractQueuedSynchronizer 抽象同步队列,简称 AQS ,它是Java并发 - 获取state的方式分为两种,独占方式和共享方式,一个线程使用独占方式获取了资源,其它线程就会在获取失败后被阻塞。一个线程使用共享方式获取了资源,另外一个线程还可以通过CAS的方式进行获取。 - 如果共享资源被占用,需要一定的阻塞等待唤醒机制来保证锁的分配,AQS 中会将竞争共享资源失败的线程添加到一个变体的 CLH 队列中。 -![AQS抽象队列同步器](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-39.png) +![AQS抽象队列同步器](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-39.png) -![CLH队列](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-40.png) +![CLH队列](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-40.png) AQS 中的队列是 CLH 变体的虚拟双向队列,通过将每条请求共享资源的线程封装成一个节点来实现锁的分配: -![AQS变种CLH队列](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-41.png) +![AQS变种CLH队列](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-41.png) AQS 中的 CLH 变体等待队列拥有以下特性: @@ -951,7 +951,7 @@ try { - 如果锁当前没有被其它线程占用,并且当前线程之前没有获取过该锁,则当前线程会获取到该锁,然后设置当前锁的拥有者为当前线程,并设置 AQS 的状态值为1 ,然后直接返回。如果当前线程之前己经获取过该锁,则这次只是简单地把 AQS 的状态值加1后返回。 - 如果该锁己经被其他线程持有,非公平锁会尝试去获取锁,获取失败的话,则调用该方法线程会被放入 AQS 队列阻塞挂起。 -![ReentrantLock 非公平锁加锁流程简图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-42.png) +![ReentrantLock 非公平锁加锁流程简图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-42.png) ### 31.ReentrantLock怎么实现公平锁的? @@ -981,7 +981,7 @@ FairSync、NonfairSync 代表公平锁和非公平锁,两者都是 ReentrantLo 1. 非公平锁在调用 lock 后,首先就会调用 CAS 进行一次抢锁,如果这个时候恰巧锁没有被占用,那么直接就获取到锁返回了。 2. 非公平锁在 CAS 失败后,和公平锁一样都会进入到 tryAcquire 方法,在 tryAcquire 方法中,如果发现锁这个时候被释放了(state == 0),非公平锁会直接 CAS 抢锁,但是公平锁会判断等待队列是否有线程处于等待状态,如果有则不去抢锁,乖乖排到后面。 -![公平锁tryAcquire](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-43.png) +![公平锁tryAcquire](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-43.png) 相对来说,非公平锁会有更好的性能,因为它的吞吐量比较大。当然,非公平锁让获取锁的时间变得更加不确定,可能会导致在阻塞队列中的线程长期处于饥饿状态。 @@ -997,7 +997,7 @@ CAS 指令包含 3 个参数:共享变量的内存地址 A、预期的值 B CAS的经典三大问题: -![CAS三大问题](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-44.png) +![CAS三大问题](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-44.png) #### ABA 问题 @@ -1030,7 +1030,7 @@ CAS 保证的是对一个变量执行操作的原子性,如果对多个变量 ### 34.Java有哪些保证原子性的方法?如何保证多线程下i++ 结果正确? -![Java保证原子性方法](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-45.png) +![Java保证原子性方法](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-45.png) - 使用循环原子类,例如AtomicInteger,实现i++原子操作 - 使用juc包下的锁,如ReentrantLock ,对i++操作加锁lock.lock()来实现原子性 @@ -1044,7 +1044,7 @@ CAS 保证的是对一个变量执行操作的原子性,如果对多个变量 因为变量的类型有很多种,所以在Atomic包里一共提供了13个类,属于4种类型的原子更新方式,分别是原子更新基本类型、原子更新数组、原子更新引用和原子更新属性(字段)。 -![原子操作类](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-46.png) +![原子操作类](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-46.png) Atomic包里的类基本都是使用Unsafe实现的包装类。 @@ -1111,11 +1111,11 @@ compareAndSwapInt 是一个native方法,基于CAS来操作int类型变量。 死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的互相等待的现象,在无外力作用的情况下,这些线程会一直相互等待而无法继续运行下去。 -![死锁示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-47.png) +![死锁示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-47.png) 那么为什么会产生死锁呢? 死锁的产生必须具备以下四个条件: -![死锁产生必备四条件](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-48.png) +![死锁产生必备四条件](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-48.png) - 互斥条件:指线程对己经获取到的资源进行它性使用,即该资源同时只由一个线程占用。如果此时还有其它线程请求获取获取该资源,则请求者只能等待,直至占有资源的线程释放该资源。 - 请求并持有条件:指一个 线程己经持有了至少一个资源,但又提出了新的资源请求,而新资源己被其它线程占有,所以当前线程会被阻塞,但阻塞 的同时并不释放自己已经获取的资源。 @@ -1141,7 +1141,7 @@ compareAndSwapInt 是一个native方法,基于CAS来操作int类型变量。 还可以利用图形化工具,比如JConsole。出现线程死锁以后,点击JConsole线程面板的`检测到死锁`按钮,将会看到线程的死锁信息。 -![线程死锁检测](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-49.png) +![线程死锁检测](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-49.png) @@ -1157,7 +1157,7 @@ CountDownLatch允许一个或多个线程等待其他线程完成操作。 例如,我们很多人喜欢玩的王者荣耀,开黑的时候,得等所有人都上线之后,才能开打。 -![王者荣耀等待玩家确认-来源参考[18]](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-50.jpeg) +![王者荣耀等待玩家确认-来源参考[18]](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-50.jpeg) CountDownLatch模仿这个场景(参考[18]): @@ -1197,7 +1197,7 @@ public static void main(String[] args) throws InterruptedException { 所以大家得一块出生,在 -![王者荣耀-来源参考[18]](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-51.jpeg) +![王者荣耀-来源参考[18]](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-51.jpeg) 在这个场景中,仍然用五个线程代表大乔、兰陵王、安其拉、哪吒和铠等五个玩家。需要注意的是,各玩家虽然都调用了`start()`线程,但是它们在运行时都在等待`countDownLatch`的信号,在信号未收到前,它们不会往下执行。 @@ -1246,15 +1246,15 @@ CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier) 不知道你听没听过一个新人UP主小约翰可汗,小约翰生平有两大恨——“想结衣结衣不依,迷爱理爱理不理。”我们来还原一下事情的经过:小约翰在亲政后认识了新垣结衣,于是决定第一次选妃,向结衣表白,等待回应。然而新垣结衣回应嫁给了星野源,小约翰伤心欲绝,发誓生平不娶,突然发现了铃木爱理,于是小约翰决定第二次选妃,求爱理搭理,等待回应。 -![想结衣结衣不依,迷爱理爱理不理。](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-52.png) +![想结衣结衣不依,迷爱理爱理不理。](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-52.png) 我们拿代码模拟这一场景,发现CountDownLatch无能为力了,因为CountDownLatch的使用是一次性的,无法重复利用,而这里等待了两次。此时,我们用CyclicBarrier就可以实现,因为它可以重复利用。 -![小约翰可汗选妃模拟代码](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-53.png) +![小约翰可汗选妃模拟代码](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-53.png) 运行结果: -![运行结果](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-54.png) +![运行结果](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-54.png) CyclicBarrier最最核心的方法,仍然是await(): @@ -1262,7 +1262,7 @@ CyclicBarrier最最核心的方法,仍然是await(): 上面的例子抽象一下,本质上它的流程就是这样就是这样: -![CyclicBarrier工作流程](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-55.png) +![CyclicBarrier工作流程](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-55.png) ### 41.CyclicBarrier和CountDownLatch有什么区别? @@ -1289,11 +1289,11 @@ Semaphore(信号量)是用来控制同时访问特定资源的线程数量 听起来似乎很抽象,现在汽车多了,开车出门在外的一个老大难问题就是停车 。停车场的车位是有限的,只能允许若干车辆停泊,如果停车场还有空位,那么显示牌显示的就是绿灯和剩余的车位,车辆就可以驶入;如果停车场没位了,那么显示牌显示的就是绿灯和数字0,车辆就得等待。如果满了的停车场有车离开,那么显示牌就又变绿,显示空车位数量,等待的车辆就能进停车场。 -![停车场空闲车位提示-图片来源网络](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-56.jpeg) +![停车场空闲车位提示-图片来源网络](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-56.jpeg) 我们把这个例子类比一下,车辆就是线程,进入停车场就是线程在执行,离开停车场就是线程执行完毕,看见红灯就表示线程被阻塞,不能执行,Semaphore的本质就是**协调多个线程对共享资源的获取**。 -![Semaphore许可获取-来源参考[18]](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-57.jpeg) +![Semaphore许可获取-来源参考[18]](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-57.jpeg) 我们再来看一个Semaphore的用途:它可以用于做流量控制,特别是公用资源有限的应用场景,比如数据库连接。 @@ -1332,7 +1332,7 @@ public class SemaphoreTest { Exchanger(交换者)是一个用于线程间协作的工具类。Exchanger用于进行线程间的数据交换。它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据。 -![英雄交换猎物-来源参考[18]](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-58.jpeg) +![英雄交换猎物-来源参考[18]](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-58.jpeg) 这两个线程通过 exchange方法交换数据,如果第一个线程先执行exchange()方法,它会一直等待第二个线程也执行exchange方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。 @@ -1381,7 +1381,7 @@ public class ExchangerTest { **线程池:** 简单理解,它就是一个管理线程的池子。 -![管理线程的池子](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-59.png) +![管理线程的池子](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-59.png) - **它帮我们管理线程,避免增加创建线程和销毁线程的资源损耗**。因为线程其实也是一个对象,创建一个对象,需要经过类加载过程,销毁一个对象,需要走GC垃圾回收流程,都是需要资源开销的。 - **提高响应速度。** 如果任务到达了,相对于从线程池拿线程,重新去创建一条线程执行,速度肯定慢很多。 @@ -1391,11 +1391,11 @@ public class ExchangerTest { 之前我们有一个和第三方对接的需求,需要向第三方推送数据,引入了多线程来提升数据推送的效率,其中用到了线程池来管理线程。 -![业务示例](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-60.png) +![业务示例](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-60.png) 主要代码如下: -![主要代码](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-61.png) +![主要代码](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-61.png) 完整可运行代码地址:https://gitee.com/fighter3/thread-demo.git @@ -1426,21 +1426,21 @@ ps:这个例子只是简单地进行了数据推送,实际上还可以结合 1. 老三发现有空间的在营业的窗口,直接去找小姐姐办理业务。 -![直接办理](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-62.png) +![直接办理](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-62.png) 2. 老三发现没有空闲的窗口,就在排队区排队等。 -![排队等待](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-63.png) +![排队等待](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-63.png) 3. 老三发现没有空闲的窗口,等待区也满了,蚌埠住了,经理一看,就让休息的小姐姐赶紧回来上班,等待区号靠前的赶紧去新窗口办,老三去排队区排队。小姐姐比较辛苦,假如一段时间发现他们可以不用接着营业,经理就让她们接着休息。 -![排队区满](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-64.png) +![排队区满](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-64.png) 4. 老三一看,六个窗口都满了,等待区也没位置了。老三急了,要闹,经理赶紧出来了,经理该怎么办呢? -![等待区,排队区都满](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-65.png) +![等待区,排队区都满](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-65.png) > 1. 我们银行系统已经瘫痪 > @@ -1471,7 +1471,7 @@ ps:这个例子只是简单地进行了数据推送,实际上还可以结合 - 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务; - 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会根据拒绝策略来对应处理。 -![线程池执行流程](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-66.png) +![线程池执行流程](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-66.png) 3. 当一个线程完成任务时,它会从队列中取下一个任务来执行。 @@ -1479,7 +1479,7 @@ ps:这个例子只是简单地进行了数据推送,实际上还可以结合 ### 47.线程池主要参数有哪些? -![线程池参数](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-67.png) +![线程池参数](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-67.png) 线程池有七大参数,需要重点关注`corePoolSize`、`maximumPoolSize`、`workQueue`、`handler`这四个。 @@ -1523,7 +1523,7 @@ ps:这个例子只是简单地进行了数据推送,实际上还可以结合 类比前面的例子,无法办理业务时的处理方式,帮助记忆: -![四种策略](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-68.png) +![四种策略](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-68.png) - AbortPolicy :直接抛出异常,默认使用此策略 - CallerRunsPolicy:用调用者所在的线程来执行任务 @@ -1536,7 +1536,7 @@ ps:这个例子只是简单地进行了数据推送,实际上还可以结合 常用的阻塞队列主要有以下几种: -![线程池常用阻塞队列](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-69.png) +![线程池常用阻塞队列](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-69.png) - ArrayBlockingQueue:ArrayBlockingQueue(有界队列)是一个用数组实现的有界阻塞队列,按FIFO排序量。 - LinkedBlockingQueue:LinkedBlockingQueue(可设置容量队列)是基于链表结构的阻塞队列,按FIFO排序任务,容量可以选择进行设置,不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE,吞吐量通常要高于ArrayBlockingQuene;newFixedThreadPool线程池使用了这个队列 @@ -1597,7 +1597,7 @@ shutdown 和shutdownnow简单来说区别如下: 1. 计算密集型:大部分都在用CPU跟内存,加密,逻辑操作业务处理等。 2. IO密集型:数据库链接,网络通讯传输等。 -![常见线程池参数配置方案-来源美团技术博客](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-70.png) +![常见线程池参数配置方案-来源美团技术博客](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-70.png) 一般的经验,不同类型线程池的参数配置: @@ -1616,7 +1616,7 @@ Runtime.getRuntime().availableProcessors(); 面试常问,主要有四种,都是通过工具类Excutors创建出来的,需要注意,阿里巴巴《Java开发手册》里禁止使用这种方式来创建线程池。 -![四大线程池](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-71.png) +![四大线程池](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-71.png) - newFixedThreadPool (固定数目线程的线程池) @@ -1649,7 +1649,7 @@ public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactor - 阻塞队列是无界队列LinkedBlockingQueue,可能会导致OOM - keepAliveTime为0 -![SingleThreadExecutor运行流程](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-72.png) +![SingleThreadExecutor运行流程](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-72.png) 工作流程: @@ -1679,7 +1679,7 @@ public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory thr - 没有所谓的非空闲时间,即keepAliveTime为0 - 阻塞队列为无界队列LinkedBlockingQueue,可能会导致OOM -![FixedThreadPool](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-73.png) +![FixedThreadPool](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-73.png) 工作流程: @@ -1714,7 +1714,7 @@ public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { -![CachedThreadPool执行流程](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-74.png) +![CachedThreadPool执行流程](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-74.png) 工作流程: @@ -1745,7 +1745,7 @@ public ScheduledThreadPoolExecutor(int corePoolSize) { - scheduleAtFixedRate() :按某种速率周期执行 - scheduleWithFixedDelay():在某个延迟后执行 -![ScheduledThreadPool执行流程](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-75.png) +![ScheduledThreadPool执行流程](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-75.png) **工作机制** @@ -1754,7 +1754,7 @@ public ScheduledThreadPoolExecutor(int corePoolSize) { - 线程修改ScheduledFutureTask的time变量为下次将要被执行的时间。 - 线程把这个修改time之后的ScheduledFutureTask放回DelayQueue中(DelayQueue.add())。 -![ScheduledThreadPoolExecutor执行流程](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-76.png) +![ScheduledThreadPoolExecutor执行流程](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-76.png) **使用场景** @@ -1770,7 +1770,7 @@ public ScheduledThreadPoolExecutor(int corePoolSize) { 常见的异常处理方式: -![线程池异常处理](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-77.png) +![线程池异常处理](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-77.png) @@ -1789,7 +1789,7 @@ private static final int TERMINATED = 3 << COUNT_BITS; 线程池各个状态切换图: -![线程池状态切换图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-78.png) +![线程池状态切换图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-78.png) **RUNNING** @@ -1820,11 +1820,11 @@ private static final int TERMINATED = 3 << COUNT_BITS; 线程池提供了几个 setter方法来设置线程池的参数。 -![JDK 线程池参数设置接口来源参考[7]](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-79.png) +![JDK 线程池参数设置接口来源参考[7]](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-79.png) 这里主要有两个思路: -![动态修改线程池参数](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-80.png) +![动态修改线程池参数](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-80.png) - 在我们微服务的架构下,可以利用配置中心如Nacos、Apollo等等,也可以自己开发配置中心。业务服务读取线程池配置,获取相应的线程池实例来修改线程池的参数。 @@ -1834,7 +1834,7 @@ private static final int TERMINATED = 3 << COUNT_BITS; 线程池配置没有固定的公式,通常事前会对线程池进行一定评估,常见的评估方案如下: -![线程池评估方案 来源参考[7]](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-81.png) +![线程池评估方案 来源参考[7]](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-81.png) 上线之前也要进行充分的测试,上线之后要建立完善的线程池监控机制。 @@ -1842,7 +1842,7 @@ private static final int TERMINATED = 3 << COUNT_BITS; 事后要注意仔细观察,随时调整。 -![线程池调优](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-82.png) +![线程池调优](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-82.png) 具体的调优案例可以查看参考[7]美团技术博客。 @@ -1852,7 +1852,7 @@ private static final int TERMINATED = 3 << COUNT_BITS; 线程池实现原理可以查看 [要是以前有人这么讲线程池,我早就该明白了!](https://mp.weixin.qq.com/s/Exy7pRGND9TCjRd9TZK4jg) ,当然,我们自己实现, 只需要抓住线程池的核心流程-参考[6]: -![线程池主要实现流程](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-83.png) +![线程池主要实现流程](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-83.png) 我们自己的实现就是完成这个核心流程: @@ -1863,7 +1863,7 @@ private static final int TERMINATED = 3 << COUNT_BITS; 实现代码[6]: -![自定义线程池](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-84.png) +![自定义线程池](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-84.png) 这样,一个实现了线程池主要流程的类就完成了。 @@ -1887,7 +1887,7 @@ Fork/Join框架是Java7提供的一个用于并行执行任务的框架,是一 Fork/Join框架的定义,其实就体现了分治思想:将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。 -![Fork/Join分治算法](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-85.png) +![Fork/Join分治算法](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-85.png) **工作窃取算法** @@ -1897,7 +1897,7 @@ Fork/Join框架的定义,其实就体现了分治思想:将一个规模为N 工作窃取发生的时候,它们会访问同一个队列,为了减少窃取任务线程和被窃取任务线程之间的竞争,通常任务会使用双端队列,被窃取任务线程永远从双端队列的头部拿,而窃取任务的线程永远从双端队列的尾部拿任务执行。 -![工作窃取](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/javathread-86.png) +![工作窃取](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/javathread-86.png) 看一个Fork/Join框架应用的例子,计算1~n之间的和:1+2+3+…+n diff --git a/docs/sidebar/sanfene/jvm.md b/docs/sidebar/sanfene/jvm.md index 015bc27b9..469c28417 100644 --- a/docs/sidebar/sanfene/jvm.md +++ b/docs/sidebar/sanfene/jvm.md @@ -18,11 +18,11 @@ JVM——Java 虚拟机,它是 Java 实现平台无关性的基石。 Java 程序运行的时候,编译器将 Java 文件编译成平台无关的 Java 字节码文件(.class),接下来对应平台 JVM 对字节码文件进行解释,翻译成对应平台匹配的机器指令并运行。 -![Java语言编译运行](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-1.png) +![Java语言编译运行](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-1.png) 同时 JVM 也是一个跨语言的平台,和语言无关,只和 class 的文件格式关联,任何语言,只要能翻译成符合规范的字节码文件,都能被 JVM 运行。 -![JVM跨语言](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-2.png) +![JVM跨语言](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-2.png) ## 二、内存管理 @@ -30,7 +30,7 @@ Java 程序运行的时候,编译器将 Java 文件编译成平台无关的 Ja JVM 内存区域最粗略的划分可以分为`堆`和`栈`,当然,按照虚拟机规范,可以划分为以下几个区域: -![Java虚拟机运行时数据区](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-3.png) +![Java虚拟机运行时数据区](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-3.png) JVM 内存分为线程私有区和线程共享区,其中`方法区`和`堆`是线程共享区,`虚拟机栈`、`本地方法栈`和`程序计数器`是线程隔离的数据区。 @@ -46,7 +46,7 @@ Java 虚拟机栈(Java Virtual Machine Stack)也是线程私有的,它的 Java 虚拟机栈描述的是 Java 方法执行的线程内存模型:方法执行时,JVM 会同步创建一个栈帧,用来存储局部变量表、操作数栈、动态连接等。 -![Java虚拟机栈](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-4.png) +![Java虚拟机栈](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-4.png) **3)本地方法栈** @@ -60,7 +60,7 @@ Java 虚拟机规范允许本地方法栈被实现成固定大小的或者是根 Java 堆是垃圾收集器管理的内存区域,因此一些资料中它也被称作“GC 堆”(Garbage Collected Heap,)。从回收内存的角度看,由于现代垃圾收集器大部分都是基于分代收集理论设计的,所以 Java 堆中经常会出现`新生代`、`老年代`、`Eden空间`、`From Survivor空间`、`To Survivor空间`等名词,需要注意的是这种划分只是根据垃圾回收机制来进行的划分,不是 Java 虚拟机规范本身制定的。 -![Java 堆内存结构](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-5.png) +![Java 堆内存结构](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-5.png) **5)方法区** @@ -74,15 +74,15 @@ JDK1.6、1.7/1.8 内存区域发生了变化,主要体现在方法区的实现 - JDK1.6 使用永久代实现方法区: -![JDK 1.6内存区域](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-6.png) +![JDK 1.6内存区域](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-6.png) - JDK1.7 时发生了一些变化,将字符串常量池、静态变量,存放在堆上 -![JDK 1.7内存区域](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-7.png) +![JDK 1.7内存区域](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-7.png) - 在 JDK1.8 时彻底干掉了永久代,而在直接内存中划出一块区域作为**元空间**,运行时常量池、类常量池都移动到元空间。 -![JDK 1.8内存区域](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-8.png) +![JDK 1.8内存区域](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-8.png) ### 4.为什么使用元空间替代永久代作为方法区的实现? @@ -108,13 +108,13 @@ Java 虚拟机规范规定的方法区只是换种方式实现。有客观和主 这个过程大概图示如下: -![对象创建过程](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-9.png) +![对象创建过程](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-9.png) ### 6.什么是指针碰撞?什么是空闲列表? 内存分配有两种方式,**指针碰撞**(Bump The Pointer)、**空闲列表**(Free List)。 -![指针碰撞和空闲列表](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-10.png) +![指针碰撞和空闲列表](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-10.png) - 指针碰撞:假设 Java 堆中内存是绝对规整的,所有被使用过的内存都被放在一边,空闲的内存被放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把那个指针向空闲空间方向挪动一段与对象大小相等的距离,这种分配方式称为“指针碰撞”。 - 空闲列表:如果 Java 堆中的内存并不是规整的,已被使用的内存和空闲的内存相互交错在一起,那就没有办法简单地进行指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录,这种分配方式称为“空闲列表”。 @@ -126,7 +126,7 @@ Java 虚拟机规范规定的方法区只是换种方式实现。有客观和主 有两种可选方案来解决这个问题: -![堆抢占和解决方案](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-11.png) +![堆抢占和解决方案](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-11.png) - 采用 CAS 分配重试的方式来保证更新操作的原子性 @@ -138,7 +138,7 @@ Java 虚拟机规范规定的方法区只是换种方式实现。有客观和主 在 HotSpot 虚拟机里,对象在堆内存中的存储布局可以划分为三个部分:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。 -![对象的存储布局](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-12.png) +![对象的存储布局](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-12.png) **对象头**主要由两部分组成: @@ -156,11 +156,11 @@ Java 程序会通过栈上的 reference 数据来操作堆上的具体对象。 - 如果使用句柄访问的话,Java 堆中将可能会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自具体的地址信息,其结构如图所示: -![通过句柄访问对象](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-13.png) +![通过句柄访问对象](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-13.png) - 如果使用直接指针访问的话,Java 堆中对象的内存布局就必须考虑如何放置访问类型数据的相关信息,reference 中存储的直接就是对象地址,如果只是访问对象本身的话,就不需要多一次间接访问的开销,如图所示: -![通过直接指针访问对象](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-14.png) +![通过直接指针访问对象](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-14.png) 这两种对象访问方式各有优势,使用句柄来访问的最大好处就是 reference 中存储的是稳定句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而 reference 本身不需要被修改。 @@ -178,7 +178,7 @@ HotSpot 虚拟机主要使用直接指针来进行对象访问。 用一个有味道的比喻,内存溢出就是排队去蹲坑,发现没坑位了,内存泄漏,就是有人占着茅坑不拉屎,占着茅坑不拉屎的多了可能会导致坑位不够用。 -![内存泄漏、内存溢出](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-15.png) +![内存泄漏、内存溢出](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-15.png) ### 11.能手写内存溢出的例子吗? @@ -245,7 +245,7 @@ public class JavaVMStackOOM { 内存泄漏可能的原因有很多种: -![内存泄漏可能原因](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-16.png) +![内存泄漏可能原因](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-16.png) **静态集合类引起内存泄漏** @@ -319,12 +319,12 @@ ThreadLocal 的弱引用导致内存泄漏也是个老生常谈的话题了, 引用计数器的算法是这样的:在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的。 -![引用计数算法](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-17.png) +![引用计数算法](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-17.png) - **可达性分析算法** 目前 Java 虚拟机的主流垃圾回收器采取的是可达性分析算法。这个算法的实质在于将一系列 GC Roots 作为初始的存活对象合集(Gc Root Set),然后从该合集出发,探索所有能够被该集合引用到的对象,并将其加入到该集合中,这个过程我们也称之为标记(mark)。最终,未被探索到的对象便是死亡的,是可以回收的。 -![GC Root](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-18.png) +![GC Root](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-18.png) ### 14.Java 中可作为 GC Roots 的对象有哪几种? @@ -375,13 +375,13 @@ PhantomReference reference = new PhantomReference(obj, queue); obj = null; ``` -![四种引用总结](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-19.png) +![四种引用总结](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-19.png) ### 16.finalize()方法了解吗?有什么作用? 用一个不太贴切的比喻,垃圾回收就是古代的秋后问斩,finalize()就是刀下留人,在人犯被处决之前,还要做最后一次审计,青天大老爷看看有没有什么冤情,需不需要刀下留人。 -![刀下留人](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-20.png) +![刀下留人](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-20.png) 如果对象在进行可达性分析后发现没有与 GC Roots 相连接的引用链,那它将会被第一次标记,随后进行一次筛选,筛选的条件是此对象是否有必要执行 finalize()方法。如果对象在在 finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可,譬如把自己 (this 关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时它就”逃过一劫“;但是如果没有抓住这个机会,那么对象就真的要被回收了。 @@ -391,7 +391,7 @@ obj = null; 而新生代又可以分为三个区域,eden、from、to,比例是 8:1:1,而新生代的内存分区同样是从垃圾收集的角度来分配的。 -![Java堆内存划分](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-21.png) +![Java堆内存划分](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-21.png) ### 18.垃圾收集算法了解吗? @@ -404,7 +404,7 @@ obj = null; - **标记** : 标记出所有需要回收的对象 - **清除**:回收所有被标记的对象 -![标记-清除算法](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-22.png) +![标记-清除算法](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-22.png) 标记-清除算法比较基础,但是主要存在两个缺点: @@ -417,7 +417,7 @@ obj = null; 过程也比较简单:将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。 -![标记-复制算法](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-23.png) +![标记-复制算法](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-23.png) 这种算法存在一个明显的缺点:一部分空间没有使用,存在空间的浪费。 @@ -429,7 +429,7 @@ obj = null; 其中的标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存。 -![标记-整理算法](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-24.png) +![标记-整理算法](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-24.png) 标记-整理算法主要用于老年代,移动存活对象是个极为负重的操作,而且这种操作需要 Stop The World 才能进行,只是从整体的吞吐量来考量,老年代使用标记-整理算法更加合适。 @@ -439,7 +439,7 @@ obj = null; 基于这种算法,虚拟机将内存分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次分配内存只使用 Eden 和其中一块 Survivor。发生垃圾收集时,将 Eden 和 Survivor 中仍然存活的对象一次性复制到另外一块 Survivor 空间上,然后直接清理掉 Eden 和已用过的那块 Survivor 空间。默认 Eden 和 Survivor 的大小比例是 8∶1。 -![新生代内存划分](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-25.png) +![新生代内存划分](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-25.png) ### 20.Minor GC/Young GC、Major GC/Old GC、Mixed GC、Full GC 都是什么意思? @@ -459,7 +459,7 @@ obj = null; 这个触发条件稍微有点多,往下看: -![Full GC触发条件](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-26.png) +![Full GC触发条件](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-26.png) - **Young GC 之前检查老年代**:在要进行 Young GC 的时候,发现`老年代可用的连续内存空间` < `新生代历次Young GC后升入老年代的对象总和的平均大小`,说明本次 Young GC 后可能升入老年代的对象大小,可能超过了老年代当前可用内存空间,那就会触发 Full GC。 - **Young GC 之后老年代空间不足**:执行 Young GC 之后有一批对象需要放入老年代,此时老年代就是没有足够的内存空间存放这些对象了,此时必须立即触发一次 Full GC @@ -470,7 +470,7 @@ obj = null; ### 23.对象什么时候会进入老年代? -![对象进入老年代](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-27.png) +![对象进入老年代](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-27.png) **长期存活的对象将进入老年代** @@ -504,7 +504,7 @@ HotSpot 虚拟机提供了这个参数来设置。 主要垃圾收集器如下,图中标出了它们的工作区域、垃圾收集算法,以及配合关系。 -![HotSpot虚拟机垃圾收集器](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-28.png) +![HotSpot虚拟机垃圾收集器](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-28.png) 这些收集器里,面试的重点是两个——**CMS**和**G1**。 @@ -516,7 +516,7 @@ Serial 收集器是最基础、历史最悠久的收集器。 Serial/Serial Old 收集器的运行过程如图: -![Serial/Serial Old收集器运行示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-29.png) +![Serial/Serial Old收集器运行示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-29.png) - ParNew @@ -524,13 +524,13 @@ ParNew 收集器实质上是 Serial 收集器的多线程并行版本,使用 ParNew/Serial Old 收集器运行示意图如下: -![ParNew/Serial Old收集器运行示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-30.png) +![ParNew/Serial Old收集器运行示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-30.png) - Parallel Scavenge Parallel Scavenge 收集器是一款新生代收集器,基于标记-复制算法实现,也能够并行收集。和 ParNew 有些类似,但 Parallel Scavenge 主要关注的是垃圾收集的吞吐量——所谓吞吐量,就是 CPU 用于运行用户代码的时间和总消耗时间的比值,比值越大,说明垃圾收集的占比越小。 -![吞吐量](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-31.png) +![吞吐量](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-31.png) - Serial Old @@ -540,7 +540,7 @@ Serial Old 是 Serial 收集器的老年代版本,它同样是一个单线程 Parallel Old 是 Parallel Scavenge 收集器的老年代版本,支持多线程并发收集,基于标记-整理算法实现。 -![Parallel Scavenge/Parallel Old收集器运行示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-32.png) +![Parallel Scavenge/Parallel Old收集器运行示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-32.png) - CMS 收集器 @@ -568,7 +568,7 @@ Garbage First(简称 G1)收集器是垃圾收集器的一个颠覆性的产 用通俗的比喻,假如老王去拉车,车上东西很重,老王累的汗流浃背,但是老王不能在上坡或者下坡休息,只能在平地上停下来擦擦汗,喝口水。 -![老王拉车只能在平路休息](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-33.png) +![老王拉车只能在平路休息](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-33.png) ### 26.能详细说一下 CMS 收集器的垃圾收集过程吗? @@ -581,7 +581,7 @@ CMS 收集齐的垃圾收集分为四步: Concurrent Mark Sweep 收集器运行示意图如下: -![Concurrent Mark Sweep收集器运行示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-34.png) +![Concurrent Mark Sweep收集器运行示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-34.png) ### 27.G1 垃圾收集器了解吗? @@ -591,7 +591,7 @@ Garbage First(简称 G1)收集器是垃圾收集器的一个颠覆性的产 G1 把连续的 Java 堆划分为多个大小相等的独立区域(Region),每一个 Region 都可以根据需要,扮演新生代的 Eden 空间、Survivor 空间,或者老年代空间。收集器能够对扮演不同角色的 Region 采用不同的策略去处理。 -![G1 Heap Regions](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-35.png) +![G1 Heap Regions](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-35.png) 这样就避免了收集整个堆,而是按照若干个 Region 集进行收集,同时维护一个优先级列表,跟踪各个 Region 回收的“价值,优先收集价值高的 Region。 @@ -602,7 +602,7 @@ G1 收集器的运行过程大致可划分为以下四个步骤: - **最终标记**(Remark),STW,标记再并发标记过程中产生的垃圾。 - **筛选回收**(Live Data Counting And Evacuation),制定回收计划,选择多个 Region 构成回收集,把回收集中 Region 的存活对象复制到空的 Region 中,再清理掉整个旧 Region 的全部空间。需要 STW。 -![G1收集器运行示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-36.png) +![G1收集器运行示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-36.png) ### 28.有了 CMS,为什么还要引入 G1? @@ -675,11 +675,11 @@ Parallel Scavenge 的特点是什么? 通俗点讲,当一个对象被 new 出来之后,它可能被外部所调用,如果是作为参数传递到外部了,就称之为方法逃逸。 -![逃逸](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-37.png) +![逃逸](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-37.png) 除此之外,如果对象还有可能被外部线程访问到,例如赋值给可以在其它线程中访问的实例变量,这种就被称为线程逃逸。 -![逃逸强度](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-38.png) +![逃逸强度](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-38.png) **逃逸分析的好处** @@ -723,15 +723,15 @@ Parallel Scavenge 的特点是什么? - JConsole -![JConsole概览](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-39.png) +![JConsole概览](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-39.png) - VisualVM -![VisualVM安装插件](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-40.png) +![VisualVM安装插件](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-40.png) - Java Mission Control -![JMC主要界面](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-41.png) +![JMC主要界面](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-41.png) 除此之外,还有一些第三方的工具: @@ -798,7 +798,7 @@ Java 应用性能分析工具,开源、火焰图、跨平台。 JVM 调优是一件很严肃的事情,不是拍脑门就开始调优的,需要有严密的分析和监控机制,大概的一个 JVM 调优流程图: -![JVM调优大致流程图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-42.png) +![JVM调优大致流程图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-42.png) 实际上,JVM 调优是不得已而为之,有那功夫,好好把烂代码重构一下不比瞎调 JVM 强。 @@ -827,7 +827,7 @@ JVM 调优是一件很严肃的事情,不是拍脑门就开始调优的,需 问题分析:CPU 高一定是某个程序长期占用了 CPU 资源。 -![CPU飙高](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-43.png) +![CPU飙高](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-43.png) 1)所以先需要找出那个进程占用 CPU 高。 @@ -984,7 +984,7 @@ jmap -dump:format=b,file=heap pid 一个类从被加载到虚拟机内存中开始,到从内存中卸载,整个生命周期需要经过七个阶段:加载 (Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化 (Initialization)、使用(Using)和卸载(Unloading),其中验证、准备、解析三个部分统称为连接(Linking)。 -![类的生命周期](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-44.png) +![类的生命周期](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-44.png) ### 43.类加载的过程知道吗? @@ -992,7 +992,7 @@ jmap -dump:format=b,file=heap pid 在加载过程,JVM 要做三件事情: -![加载](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-45.png) +![加载](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-45.png) - 1)通过一个类的全限定名来获取定义此类的二进制字节流。 @@ -1018,7 +1018,7 @@ jmap -dump:format=b,file=heap pid ### 45.什么是双亲委派机制? -![双亲委派模型](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-46.png) +![双亲委派模型](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-46.png) 双亲委派模型的工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去完成加载。 @@ -1036,7 +1036,7 @@ jmap -dump:format=b,file=heap pid 双亲委派机制在历史上主要有三次破坏: -![双亲委派模型的三次破坏](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-47.png) +![双亲委派模型的三次破坏](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-47.png) > **第一次破坏** @@ -1090,7 +1090,7 @@ Tomcat 是主流的 Java Web 服务器之一,为了实现一些特殊的功能 Tomcat 类加载器如下: -![Tomcat类加载器](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/jvm-48.png) +![Tomcat类加载器](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/jvm-48.png) Tomcat 实际上也是破坏了双亲委派模型的。 diff --git a/docs/sidebar/sanfene/spring.md b/docs/sidebar/sanfene/spring.md index 6e947c117..0f6755998 100644 --- a/docs/sidebar/sanfene/spring.md +++ b/docs/sidebar/sanfene/spring.md @@ -13,13 +13,13 @@ tag: ### 1.Spring 是什么?特性?有哪些模块? -![Spring Logo](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-165c27b4-2ea0-409a-8fa5-389c105db0fa.png) +![Spring Logo](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-165c27b4-2ea0-409a-8fa5-389c105db0fa.png) 一句话概括:**Spring 是一个轻量级、非入侵式的控制反转 (IoC) 和面向切面 (AOP) 的框架。** 2003 年,一个音乐家 Rod Johnson 决定发展一个轻量级的 Java 开发框架,`Spring`作为 Java 战场的龙骑兵渐渐崛起,并淘汰了`EJB`这个传统的重装骑兵。 -![Spring重要版本](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-5d9efb93-03a5-400c-8429-3be7c5eeddfb.png) +![Spring重要版本](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-5d9efb93-03a5-400c-8429-3be7c5eeddfb.png) 到了现在,企业级开发的标配基本就是 **Spring5** + **Spring Boot 2** + **JDK 8** @@ -27,7 +27,7 @@ tag: Spring 有很多优点: -![Spring特性](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-a0f0ef9d-3289-41ea-94c2-34b7e37ef854.png) +![Spring特性](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-a0f0ef9d-3289-41ea-94c2-34b7e37ef854.png) 1. **IOC** 和 **DI** 的支持 @@ -57,7 +57,7 @@ Spring 对 JavaEE 开发中非常难用的一些 API(JDBC、JavaMail、远程 Spring 框架是分模块存在,除了最核心的`Spring Core Container`是必要模块之外,其他模块都是`可选`,大约有 20 多个模块。 -![Spring模块划分](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-bb7c13ea-3174-4b32-84b8-821849ddc377.png) +![Spring模块划分](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-bb7c13ea-3174-4b32-84b8-821849ddc377.png) 最主要的七大模块: @@ -73,7 +73,7 @@ Spring 框架是分模块存在,除了最核心的`Spring Core Container`是 Spring 有很多模块,甚至广义的 SpringBoot、SpringCloud 也算是 Spring 的一部分,我们来分模块,按功能来看一下一些常用的注解: -![Spring常用注解](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-8d0a1518-a425-4887-9735-45321095d927.png) +![Spring常用注解](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-8d0a1518-a425-4887-9735-45321095d927.png) **Web**: @@ -117,7 +117,7 @@ Spring 有很多模块,甚至广义的 SpringBoot、SpringCloud 也算是 Spri Spring 框架中广泛使用了不同类型的设计模式,下面我们来看看到底有哪些设计模式? -![Spring中用到的设计模式](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-ee1c5cee-8462-4bae-93ea-ec936cc77640.png) +![Spring中用到的设计模式](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-ee1c5cee-8462-4bae-93ea-ec936cc77640.png) 1. **工厂模式** : Spring 容器本质是一个大工厂,使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。 2. **代理模式** : Spring AOP 功能功能就是通过代理模式来实现的,分为动态代理和静态代理。 @@ -135,11 +135,11 @@ Java 是面向对象的编程语言,一个个实例对象相互合作组成了 所谓的**IOC**(控制反转):就是由容器来负责控制对象的生命周期和对象间的关系。以前是我们想要什么,就自己创建什么,现在是我们需要什么,容器就给我们送来什么。 -![引入IOC之前和引入IOC之后](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-619da277-c15e-4dd7-9f2b-dbd809a9aaa0.png) +![引入IOC之前和引入IOC之后](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-619da277-c15e-4dd7-9f2b-dbd809a9aaa0.png) 也就是说,控制对象生命周期的不再是引用它的对象,而是容器。对具体对象,以前是它控制其它对象,现在所有对象都被容器控制,所以这就叫**控制反转**。 -![控制反转示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-440f5d0e-f4db-462c-97fb-d54407a354d5.png) +![控制反转示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-440f5d0e-f4db-462c-97fb-d54407a354d5.png) **DI(依赖注入)**:指的是容器在实例化对象的时候把它依赖的类注入给它。有的说法 IOC 和 DI 是一回事,有的说法是 IOC 是思想,DI 是 IOC 的实现。 @@ -153,7 +153,7 @@ PS:这道题老三在面试中被问到过,问法是“**你有自己实现过 Spring 的 IOC 本质就是一个大工厂,我们想想一个工厂是怎么运行的呢? -![工厂运行](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-7678c40f-a48d-4bd5-80f8-e902ad688e11.png) +![工厂运行](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-7678c40f-a48d-4bd5-80f8-e902ad688e11.png) - **生产产品**:一个工厂最核心的功能就是生产产品。在 Spring 里,不用 Bean 自己来实例化,而是交给 Spring,应该怎么实现呢?——答案毫无疑问,**反射**。 @@ -167,7 +167,7 @@ Spring 的 IOC 本质就是一个大工厂,我们想想一个工厂是怎么 我们简单地实现一个 mini 版的 Spring IOC: -![mini版本Spring IOC](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-1d55c63d-2d12-43b1-9f43-428f5f4a1413.png) +![mini版本Spring IOC](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-1d55c63d-2d12-43b1-9f43-428f5f4a1413.png) **Bean 定义:** @@ -266,7 +266,7 @@ Bean 通过一个配置文件定义,把它解析成一个类型。 - **BeanFactory.java** -![BeanFactory](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-c6b3b707-cf53-4c7c-a6f9-8560950806fc.png) +![BeanFactory](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-c6b3b707-cf53-4c7c-a6f9-8560950806fc.png) - 对象工厂,我们最**核心**的一个类,在它初始化的时候,创建了 bean 注册器,完成了资源的加载。 @@ -372,7 +372,7 @@ PS:因为时间+篇幅的限制,这个 demo 比较简陋,没有面向接口 可以这么形容,BeanFactory 是 Spring 的“心脏”,ApplicantContext 是完整的“身躯”。 -![BeanFactory和ApplicantContext的比喻](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-66328446-f89f-4b7a-8d9f-0e1145dd9b2f.png) +![BeanFactory和ApplicantContext的比喻](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-66328446-f89f-4b7a-8d9f-0e1145dd9b2f.png) - BeanFactory(Bean 工厂)是 Spring 框架的基础设施,面向 Spring 本身。 - ApplicantContext(应用上下文)建立在 BeanFactoty 基础上,面向使用 Spring 框架的开发者。 @@ -383,7 +383,7 @@ BeanFactory 是类的通用工厂,可以创建并管理各种类的对象。 Spring 为 BeanFactory 提供了很多种实现,最常用的是 XmlBeanFactory,但在 Spring 3.2 中已被废弃,建议使用 XmlBeanDefinitionReader、DefaultListableBeanFactory。 -![Spring5 BeanFactory继承体系](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-6e6d4b69-f36c-41e6-b8ba-9277be147c9b.png) +![Spring5 BeanFactory继承体系](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-6e6d4b69-f36c-41e6-b8ba-9277be147c9b.png) BeanFactory 接口位于类结构树的顶端,它最主要的方法就是 getBean(String var1),这个方法从容器中返回特定名称的 Bean。 @@ -405,7 +405,7 @@ public class HelloWorldApp{ ApplicationContext 由 BeanFactory 派生而来,提供了更多面向实际应用的功能。可以这么说,使用 BeanFactory 就是手动档,使用 ApplicationContext 就是自动档。 -![Spring5 ApplicationContext部分体系类图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-e201c9a3-f23c-4768-b844-ac7e0ba4bcec.png) +![Spring5 ApplicationContext部分体系类图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-e201c9a3-f23c-4768-b844-ac7e0ba4bcec.png) ApplicationContext 继承了 HierachicalBeanFactory 和 ListableBeanFactory 接口,在此基础上,还通过其他的接口扩展了 BeanFactory 的功能,包括: @@ -439,11 +439,11 @@ Spring 的 IOC 容器工作的过程,其实可以划分为两个阶段:**容 其中容器启动阶段主要做的工作是加载和解析配置文件,保存到对应的 Bean 定义中。 -![容器启动和Bean实例化阶段](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-8f8103f7-2a51-4858-856e-96a4ac400d76.png) +![容器启动和Bean实例化阶段](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-8f8103f7-2a51-4858-856e-96a4ac400d76.png) 容器启动开始,首先会通过某种途径加载 Congiguration MetaData,在大部分情况下,容器需要依赖某些工具类(BeanDefinitionReader)对加载的 Congiguration MetaData 进行解析和分析,并将分析后的信息组为相应的 BeanDefinition。 -![xml配置信息映射注册过程](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-dfb3d8c4-ba8d-4a2c-aef2-4ad425f7180c.png) +![xml配置信息映射注册过程](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-dfb3d8c4-ba8d-4a2c-aef2-4ad425f7180c.png) 最后把这些保存了 Bean 定义必要信息的 BeanDefinition,注册到相应的 BeanDefinitionRegistry,这样容器启动就完成了。 @@ -455,7 +455,7 @@ Spring 的 IOC 容器工作的过程,其实可以划分为两个阶段:**容 Spring IOC 中 Bean 的生命周期大致分为四个阶段:**实例化**(Instantiation)、**属性赋值**(Populate)、**初始化**(Initialization)、**销毁**(Destruction)。 -![Bean生命周期四个阶段](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-595fce5b-36cb-4dcb-b08c-8205a1e98d8a.png) +![Bean生命周期四个阶段](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-595fce5b-36cb-4dcb-b08c-8205a1e98d8a.png) 我们再来看一个稍微详细一些的过程: @@ -464,12 +464,12 @@ Spring IOC 中 Bean 的生命周期大致分为四个阶段:**实例化**(In - **初始化**:初始化的阶段的步骤比较多,5、6 步是真正的初始化,第 3、4 步为在初始化前执行,第 7 步在初始化后执行,初始化完成之后,Bean 就可以被使用了 - **销毁**:第 8~10 步,第 8 步其实也可以算到销毁阶段,但不是真正意义上的销毁,而是先在使用前注册了销毁的相关调用接口,为了后面第 9、10 步真正销毁 Bean 时再执行相应的方法 -![SpringBean生命周期](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-942a927a-86e4-4a01-8f52-9addd89642ff.png) +![SpringBean生命周期](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-942a927a-86e4-4a01-8f52-9addd89642ff.png) 简单总结一下,Bean 生命周期里初始化的过程相对步骤会多一些,比如前置、后置的处理。 最后通过一个实例来看一下具体的细节: -![Bean一生实例](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-a3b7714e-38f2-433d-97c6-acb1d20f2887.png) +![Bean一生实例](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-a3b7714e-38f2-433d-97c6-acb1d20f2887.png) - 定义一个`PersonBean`类,实现`DisposableBean`,`InitializingBean`, `BeanFactoryAware`, `BeanNameAware`这 4 个接口,同时还有自定义的`init-method`和`destroy-method`。 @@ -613,13 +613,13 @@ Bean使用中:工作,只有对社会没有用的人才放假。。 关于源码,Bean 创建过程可以查看`AbstractBeanFactory#doGetBean`方法,在这个方法里可以看到 Bean 的实例化,赋值、初始化的过程,至于最终的销毁,可以看看`ConfigurableApplicationContext#close()`。 -![Bean生命周期源码追踪](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-d2da20a3-08d0-4648-b9a3-2fff8512b159.png) +![Bean生命周期源码追踪](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-d2da20a3-08d0-4648-b9a3-2fff8512b159.png) ### 10.Bean 定义和依赖定义有哪些方式? 有三种方式:**直接编码方式**、**配置文件方式**、**注解方式**。 -![Bean依赖配置方式](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-89f0f50d-a9e4-4dec-b267-cb1a526cb340.png) +![Bean依赖配置方式](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-89f0f50d-a9e4-4dec-b267-cb1a526cb340.png) - 直接编码方式:我们一般接触不到直接编码的方式,但其实其它的方式最终都要通过直接编码来实现。 - 配置文件方式:通过 xml、propreties 类型的配置文件,配置相应的依赖关系,Spring 读取配置文件,完成依赖关系的注入。 @@ -629,7 +629,7 @@ Bean使用中:工作,只有对社会没有用的人才放假。。 Spring 支持**构造方法注入**、**属性注入**、**工厂方法注入**,其中工厂方法注入,又可以分为**静态工厂方法注入**和**非静态工厂方法注入**。 -![Spring依赖注入方法](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-491f8444-54ba-4628-b8eb-8418a2197096.png) +![Spring依赖注入方法](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-491f8444-54ba-4628-b8eb-8418a2197096.png) - **构造方法注入** @@ -752,7 +752,7 @@ Spring 提供的这种方式,可以按照某些规则进行 Bean 的自动装 Spring 提供了 4 种自动装配类型: -![Spring四种自动装配类型](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-034120d9-88c7-490b-af07-7d48f3b6b7bc.png) +![Spring四种自动装配类型](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-034120d9-88c7-490b-af07-7d48f3b6b7bc.png) - **byName**:根据名称进行自动匹配,假设 Boss 又一个名为 car 的属性,如果容器中刚好有一个名为 car 的 bean,Spring 就会自动将其装配给 Boss 的 car 属性 - **byType**:根据类型进行自动匹配,假设 Boss 有一个 Car 类型的属性,如果容器中刚好有一个 Car 类型的 Bean,Spring 就会自动将其装配给 Boss 这个属性 @@ -763,7 +763,7 @@ Spring 提供了 4 种自动装配类型: Spring 的 Bean 主要支持五种作用域: -![Spring Bean支持作用域](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-08a9cb31-5a4f-4224-94cd-0c0f643a57ea.png) +![Spring Bean支持作用域](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-08a9cb31-5a4f-4224-94cd-0c0f643a57ea.png) - **singleton** : 在 Spring 容器仅存在一个 Bean 实例,Bean 以单实例的方式存在,是 Bean 默认的作用域。 - **prototype** : 每次从容器重调用 Bean 时,都会返回一个新的实例。 @@ -782,7 +782,7 @@ Spring 的 Bean 主要支持五种作用域: 假如这个 Bean 是有状态的,也就是会对 Bean 中的成员变量进行写操作,那么可能就存在线程安全的问题。 -![Spring单例Bean线程安全问题](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-35dacef4-1a9e-45e1-b3f2-5a91227eb244.png) +![Spring单例Bean线程安全问题](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-35dacef4-1a9e-45e1-b3f2-5a91227eb244.png) > **单例 Bean 线程安全问题怎么解决呢?** @@ -804,11 +804,11 @@ Spring 的 Bean 主要支持五种作用域: > **什么是循环依赖?** -![Spring循环依赖](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-f8fea53f-56fa-4cca-9199-ec7f648da625.png) +![Spring循环依赖](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-f8fea53f-56fa-4cca-9199-ec7f648da625.png) Spring 循环依赖:简单说就是自己依赖自己,或者和别的 Bean 相互依赖。 -![鸡和蛋](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-0035fd25-2972-4642-a8ec-ee44a566a5bd.png) +![鸡和蛋](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-0035fd25-2972-4642-a8ec-ee44a566a5bd.png) 只有单例的 Bean 才存在循环依赖的情况,**原型**(Prototype)情况下,Spring 会直接抛出异常。原因很简单,AB 循环依赖,A 实例化的时候,发现依赖 B,创建 B 实例,创建 B 的时候发现需要 A,创建 A1 实例……无限套娃,直接把系统干垮。 @@ -818,7 +818,7 @@ Spring 不支持基于构造器注入的循环依赖,但是假如 AB 循环依 看看几种情形: -![循环依赖的几种情形](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-37bb576d-b4af-42ed-91f4-d846ceb012b6.png) +![循环依赖的几种情形](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-37bb576d-b4af-42ed-91f4-d846ceb012b6.png) 第四种可以而第五种不可以的原因是 Spring 在创建 Bean 时默认会根据自然排序进行创建,所以 A 会先于 B 进行创建。 @@ -830,7 +830,7 @@ Spring 不支持基于构造器注入的循环依赖,但是假如 AB 循环依 我们都知道,单例 Bean 初始化完成,要经历三步: -![Bean初始化步骤](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-867066f1-49d1-4e57-94f9-4c66a3a8797e.png) +![Bean初始化步骤](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-867066f1-49d1-4e57-94f9-4c66a3a8797e.png) 注入就发生在第二步,**属性赋值**,结合这个过程,Spring 通过**三级缓存**解决了循环依赖: @@ -838,30 +838,30 @@ Spring 不支持基于构造器注入的循环依赖,但是假如 AB 循环依 2. 二级缓存 : `Map` **earlySingletonObjects**,早期曝光对象,用于保存实例化完成的 bean 实例 3. 三级缓存 : `Map>` **singletonFactories**,早期曝光对象工厂,用于保存 bean 创建工厂,以便于后面扩展有机会创建代理对象。 -![三级缓存](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-01d92863-a2cb-4f61-8d8d-30ecf0279b28.png) +![三级缓存](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-01d92863-a2cb-4f61-8d8d-30ecf0279b28.png) 我们来看一下三级缓存解决循环依赖的过程: 当 A、B 两个类发生循环依赖时: -![循环依赖](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-cfc09f84-f8e1-4702-80b6-d115843e81fe.png) +![循环依赖](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-cfc09f84-f8e1-4702-80b6-d115843e81fe.png) A 实例的初始化过程: 1. 创建 A 实例,实例化的时候把 A 对象⼯⼚放⼊三级缓存,表示 A 开始实例化了,虽然我这个对象还不完整,但是先曝光出来让大家知道 -![1](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-1a8bdc29-ff43-4ff4-9b61-3eedd9da59b3.png) +![1](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-1a8bdc29-ff43-4ff4-9b61-3eedd9da59b3.png) 2. A 注⼊属性时,发现依赖 B,此时 B 还没有被创建出来,所以去实例化 B 3. 同样,B 注⼊属性时发现依赖 A,它就会从缓存里找 A 对象。依次从⼀级到三级缓存查询 A,从三级缓存通过对象⼯⼚拿到 A,发现 A 虽然不太完善,但是存在,把 A 放⼊⼆级缓存,同时删除三级缓存中的 A,此时,B 已经实例化并且初始化完成,把 B 放入⼀级缓存。 -![2](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-bf2507bf-96aa-4b88-a58b-7ec41d11bc70.png) +![2](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-bf2507bf-96aa-4b88-a58b-7ec41d11bc70.png) 4. 接着 A 继续属性赋值,顺利从⼀级缓存拿到实例化且初始化完成的 B 对象,A 对象创建也完成,删除⼆级缓存中的 A,同时把 A 放⼊⼀级缓存 5. 最后,⼀级缓存中保存着实例化、初始化都完成的 A、B 对象 -![5](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-022f7cb9-2c83-4fe9-b252-b02bd0fb2435.png) +![5](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-022f7cb9-2c83-4fe9-b252-b02bd0fb2435.png) 所以,我们就知道为什么 Spring 能解决 setter 注入的循环依赖了,因为实例化和属性赋值是分开的,所以里面有操作的空间。如果都是构造器注入的化,那么都得在实例化这一步完成注入,所以自然是无法支持了。 @@ -873,7 +873,7 @@ A 实例的初始化过程: 假设只有⼆级缓存的情况,往⼆级缓存中放的显示⼀个普通的 Bean 对象,Bean 初始化过程中,通过 BeanPostProcessor 去⽣成代理对象之后,覆盖掉⼆级缓存中的普通 Bean 对象,那么可能就导致取到的 Bean 对象不一致了。 -![二级缓存不行的原因](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-6ece8a46-25b1-459b-8cfa-19fc696dd7d6.png) +![二级缓存不行的原因](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-6ece8a46-25b1-459b-8cfa-19fc696dd7d6.png) ### 18.@Autowired 的实现原理? @@ -944,19 +944,19 @@ A 实例的初始化过程: AOP:面向切面编程。简单说,就是把一些业务逻辑中的相同的代码抽取到一个独立的模块中,让业务逻辑更加清爽。 -![横向抽取](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-09dbcda4-7c1b-42d6-8520-1a5fc84abbde.png) +![横向抽取](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-09dbcda4-7c1b-42d6-8520-1a5fc84abbde.png) 具体来说,假如我现在要 crud 写一堆业务,可是如何业务代码前后前后进行打印日志和参数的校验呢? 我们可以把`日志记录`和`数据校验`可重用的功能模块分离出来,然后在程序的执行的合适的地方动态地植入这些代码并执行。这样就简化了代码的书写。 -![AOP应用示例](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-4754b4c0-0356-4077-a2f9-55e246cf8ba0.png) +![AOP应用示例](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-4754b4c0-0356-4077-a2f9-55e246cf8ba0.png) 业务逻辑代码中没有参和通用逻辑的代码,业务模块更简洁,只包含核心业务代码。实现了业务逻辑和通用逻辑的代码分离,便于维护和升级,降低了业务逻辑和通用逻辑的耦合性。 AOP 可以将遍布应用各处的功能分离出来形成可重用的组件。在编译期间、装载期间或运行期间实现在不修改源代码的情况下给程序动态添加功能。从而实现对业务逻辑的隔离,提高代码的模块化能力。 -![Java语言执行过程](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-2b9859e5-019c-4e87-a449-990d3deae135.png) +![Java语言执行过程](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-2b9859e5-019c-4e87-a449-990d3deae135.png) AOP 的核心其实就是**动态代理**,如果是实现了接口的话就会使用 JDK 动态代理,否则使用 CGLIB 代理,主要应用于处理一些具有横切性质的系统级服务,如日志收集、事务管理、安全检查、缓存、对象池管理等。 @@ -989,7 +989,7 @@ AOP 一般有 **5 种**环绕方式: - 后置通知 (@After) - 环绕通知 (@Around) -![环绕方式](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-320fa34f-6620-419c-b17a-4f516a83caeb.png) +![环绕方式](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-320fa34f-6620-419c-b17a-4f516a83caeb.png) 多个切面的情况下,可以通过 @Order 指定先后顺序,数字越小,优先级越高。 @@ -1100,7 +1100,7 @@ PS:这道题老三的同事面试候选人的时候问到了,候选人说了 ``` * 执行结果:可以看到日志打印了入参、出参和执行时间 -![执行结果](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-9c14f774-44b9-41b3-a8c0-f2a54385f6ff.png) +![执行结果](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-9c14f774-44b9-41b3-a8c0-f2a54385f6ff.png) ### 21.说说 JDK 动态代理和 CGLIB 代理 ? @@ -1120,11 +1120,11 @@ Spring 的 AOP 是通过[动态代理](https://mp.weixin.qq.com/s/aZtfwik0weJN5J 我们来看一个常见的小场景,客服中转,解决用户问题: -![用户向客服提问题](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-c5c4b247-62dd-43a2-a043-da51c58f77c8.png) +![用户向客服提问题](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-c5c4b247-62dd-43a2-a043-da51c58f77c8.png) **JDK 动态代理实现:** -![JDK动态代理类图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-65b14a3f-2653-463e-af77-a8875d3d635c.png) +![JDK动态代理类图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-65b14a3f-2653-463e-af77-a8875d3d635c.png) - 接口 @@ -1193,7 +1193,7 @@ Spring 的 AOP 是通过[动态代理](https://mp.weixin.qq.com/s/aZtfwik0weJN5J **Cglib 动态代理实现:** -![Cglib动态代理类图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-74da87af-20d1-4a5b-a212-3837a15f0bab.png) +![Cglib动态代理类图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-74da87af-20d1-4a5b-a212-3837a15f0bab.png) - 目标类:Solver,这里目标类不用再实现接口。 @@ -1285,7 +1285,7 @@ AspectJ 属于**静态织入**,通过修改代码来实现,在实际运行 整体对比如下: -![Spring AOP和AspectJ对比](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-d1dbe9d9-c55f-4293-8622-d9759064d613.png) +![Spring AOP和AspectJ对比](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-d1dbe9d9-c55f-4293-8622-d9759064d613.png) ## 事务 @@ -1295,7 +1295,7 @@ Spring 事务的本质其实就是数据库对事务的支持,没有数据库 Spring 支持`编程式事务`管理和`声明式`事务管理两种方式: -![Spring事务分类](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-d3ee77fa-926d-4c39-91f8-a8b1544a9134.png) +![Spring事务分类](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-d3ee77fa-926d-4c39-91f8-a8b1544a9134.png) 1. 编程式事务 @@ -1322,7 +1322,7 @@ Spring 事务的传播机制说的是,当多个事务同时存在的时候— 事务传播机制是使用简单的 ThreadLocal 实现的,所以,如果调用的方法是在新线程调用的,事务传播实际上是会失效的。 -![7种事务传播机制](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-a6e2a8dc-9771-4d8b-9d91-76ddee98af1a.png) +![7种事务传播机制](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-a6e2a8dc-9771-4d8b-9d91-76ddee98af1a.png) Spring 默认的事务传播行为是 PROPAFATION_REQUIRED,它适合绝大多数情况,如果多个 ServiceX#methodX()都工作在事务环境下(均被 Spring 事务增强),且程序中存在调用链 `Service1#method1()->Service2#method2()->Service3#method3()`,那么这 3 个服务类的三个方法通过 Spring 的事务传播机制都工作在同一个事务中。 @@ -1334,13 +1334,13 @@ Spring 默认的事务传播行为是 PROPAFATION_REQUIRED,它适合绝大多 - **在执行目标方法时进行事务增强操作**:当通过代理对象调用 Bean 方法的时候,会触发对应的 AOP 增强拦截器,声明式事务是一种环绕增强,对应接口为`MethodInterceptor`,事务增强对该接口的实现为`TransactionInterceptor`,类图如下: -![图片来源网易技术专栏](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-97493c7f-c596-4e98-a6a8-dab254d6d1ab.png) +![图片来源网易技术专栏](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-97493c7f-c596-4e98-a6a8-dab254d6d1ab.png) 事务拦截器`TransactionInterceptor`在`invoke`方法中,通过调用父类`TransactionAspectSupport`的`invokeWithinTransaction`方法进行事务处理,包括开启事务、事务提交、异常回滚。 ### 27.声明式事务在哪些情况下会失效? -![声明式事务的几种失效的情况](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-381e4ec9-a235-4cfa-9b4d-518095a7502a.png) +![声明式事务的几种失效的情况](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-381e4ec9-a235-4cfa-9b4d-518095a7502a.png) **1、@Transactional 应用在非 public 修饰的方法上** @@ -1369,7 +1369,7 @@ protected TransactionAttribute computeTransactionAttribute(Method method, rollbackFor 可以指定能够触发事务回滚的异常类型。Spring 默认抛出了未检查 unchecked 异常(继承自 RuntimeException 的异常)或者 Error 才回滚事务,其他异常不会触发回滚事务。 -![Spring默认支持的异常回滚](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-04053b02-3264-4d7f-b868-560a0333f08d.png) +![Spring默认支持的异常回滚](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-04053b02-3264-4d7f-b868-560a0333f08d.png) ```java // 希望自定义的异常可以进行回滚 @@ -1458,7 +1458,7 @@ org.springframework.transaction.UnexpectedRollbackException: Transaction rolled ### 29.Spring MVC 的工作流程? -![Spring MVC的工作流程](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-e29a122b-db07-48b8-8289-7251032e87a1.png) +![Spring MVC的工作流程](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-e29a122b-db07-48b8-8289-7251032e87a1.png) 1. 客户端向服务端发送一次请求,这个请求会先到前端控制器 DispatcherServlet(也叫中央控制器)。 2. DispatcherServlet 接收到请求后会调用 HandlerMapping 处理器映射器。由此得知,该请求该由哪个 Controller 来处理(并未调用 Controller,只是得知) @@ -1488,7 +1488,7 @@ PS:这是一道全新的八股,毕竟 ModelAndView 这种方式应该没人用 加入了这个注解后,整体的流程上和使用 ModelAndView 大体上相同,但是细节上有一些不同: -![Spring MVC Restful请求响应示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-2da963a0-5da9-4b3a-aafd-fd8dbc7e1807.png) +![Spring MVC Restful请求响应示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-2da963a0-5da9-4b3a-aafd-fd8dbc7e1807.png) 1. 客户端向服务端发送一次请求,这个请求会先到前端控制器 DispatcherServlet @@ -1514,7 +1514,7 @@ PS:这是一道全新的八股,毕竟 ModelAndView 这种方式应该没人用 Spring Boot 基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。 -![SpringBoot图标](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-d9164ee6-5c86-4313-8fd9-efb9acfa5f0b.png) +![SpringBoot图标](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-d9164ee6-5c86-4313-8fd9-efb9acfa5f0b.png) Spring Boot 以`约定大于配置`核心思想开展工作,相比 Spring 具有如下优势: @@ -1529,7 +1529,7 @@ Spring Boot 以`约定大于配置`核心思想开展工作,相比 Spring 具 SpringBoot 开启自动配置的注解是`@EnableAutoConfiguration` ,启动类上的注解`@SpringBootApplication`是一个复合注解,包含了@EnableAutoConfiguration: -![SpringBoot自动配置原理](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-df77ee15-2ff0-4ec7-8e65-e4ebb8ba88f1.png) +![SpringBoot自动配置原理](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-df77ee15-2ff0-4ec7-8e65-e4ebb8ba88f1.png) - `EnableAutoConfiguration` 只是一个简单的注解,自动装配核心功能的实现实际是通过 `AutoConfigurationImportSelector`类 @@ -1663,7 +1663,7 @@ SpringBoot 开启自动配置的注解是`@EnableAutoConfiguration` ,启动类 - 运行结果 -![运行结果](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-3ff3cc21-6a56-434b-a89e-d2b55d558bd6.png) +![运行结果](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-3ff3cc21-6a56-434b-a89e-d2b55d558bd6.png) 至此,随手写的一个自定义 SpringBoot-Starter 就完成了,虽然比较简单,但是完成了主要的自动装配的能力。 @@ -1678,7 +1678,7 @@ SpringApplication 这个类主要做了以下四件事情: SpringBoot 启动大致流程如下 : -![SpringBoot 启动大致流程-图片来源网络](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-68744556-a1ba-4e1f-a092-1582875f0da6.png) +![SpringBoot 启动大致流程-图片来源网络](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-68744556-a1ba-4e1f-a092-1582875f0da6.png) ## Spring Cloud @@ -1686,7 +1686,7 @@ SpringBoot 启动大致流程如下 : SpringCloud 是 Spring 官方推出的微服务治理框架。 -![Spring Cloud Netfilx核心组件-来源参考[2]](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-92ab53d5-f303-4fc5-bd26-e62cefe374b3.png) +![Spring Cloud Netfilx核心组件-来源参考[2]](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-92ab53d5-f303-4fc5-bd26-e62cefe374b3.png) > **什么是微服务?** @@ -1708,7 +1708,7 @@ SpringCloud 是 Spring 官方推出的微服务治理框架。 > **SpringCloud 有哪些核心组件?** -![SpringCloud](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/sidebar/sanfene/spring-2b988a72-0739-4fed-b271-eaf12589444f.png) +![SpringCloud](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/sidebar/sanfene/spring-2b988a72-0739-4fed-b271-eaf12589444f.png) PS:微服务后面有机会再扩展,其实面试一般都是结合项目去问。 diff --git a/docs/springboot/aop-log.md b/docs/springboot/aop-log.md index 496ecbe4f..b61d5f049 100644 --- a/docs/springboot/aop-log.md +++ b/docs/springboot/aop-log.md @@ -15,7 +15,7 @@ AOP,也就是 Aspect-oriented Programming,译为面向切面编程,是计 这种思想非常适用于,将那些与核心业务不那么密切关联的功能添加到程序中,就好比我们今天的主题——日志功能,就是一个典型的案例。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/aop-log-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/aop-log-1.png) AOP 是对面向对象编程(Object-oriented Programming,俗称 OOP)的一种补充,OOP 的核心单元是类(class),而 AOP 的核心单元是切面(Aspect)。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而降低耦合度,提高程序的可重用性,同时也提高了开发效率。 @@ -25,7 +25,7 @@ AOP 是对面向对象编程(Object-oriented Programming,俗称 OOP)的一 来看下面这幅图,这是一个 AOP 的模型图,就是在某些方法执行前后执行一些通用的操作,并且这些操作不会影响程序本身的运行。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/aop-log-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/aop-log-2.png) 我们了解下 AOP 涉及到的 5 个关键术语: **1)横切关注点**,从每个方法中抽取出来的同一类非核心业务 @@ -165,11 +165,11 @@ public class WebLogAspect { 执行登录用户查询操作: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/aop-log-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/aop-log-3.png) 可以在控制台可以看到以下日志信息: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/aop-log-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/aop-log-4.png) 源码地址: >[https://github.com/itwanger/coding-more](https://github.com/itwanger/coding-more) diff --git a/docs/springboot/cors.md b/docs/springboot/cors.md index df9c7dbd0..ca2f7caad 100644 --- a/docs/springboot/cors.md +++ b/docs/springboot/cors.md @@ -2,7 +2,7 @@ 跨域问题是前后端分离项目中非常常见的一个问题,举例来说,编程猫([codingmore](https://github.com/itwanger/coding-more))学习网站的前端服务跑在 8080 端口下,后端服务跑在 9002 端口下,那么前端在请求后端接口的时候就会出现跨域问题。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/cors-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/cors-1.png) 403 Forbidden 是HTTP协议中的一个状态码(Status Code),意味着后端服务虽然成功解析了请求,但前端却没有访问该资源的权限。 @@ -21,7 +21,7 @@ 那正确的打开方式是什么呢?我们前面也提到了,前端使用 Nodejs 代理或者后端开启跨域资源共享,我们一一来实践下。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/cors-2.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/cors-2.gif) ### 二、Nodejs 代理 @@ -62,13 +62,13 @@ module.exports = merge(prodEnv, { 第三步,重启前端服务 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/cors-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/cors-3.png) 再次点击「登录」按钮,可以看到请求的 URL 发生了改变,原来是 `http://localhost:9002/users/login`,现在是 `http://localhost:8080/api/users/login`。与此同时,可以看到多了一个 Remote Address,端口也是 8080,也就是说经过 Nodejs 的代理,前后端的交互在同一个源下面了,这样就不会发生跨域问题了。 同时,可以看得到,服务器端返回的状态码变成了 200,表示请求成功。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/cors-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/cors-4.png) ### 三、开启跨域资源共享 @@ -101,7 +101,7 @@ public class GlobalCorsConfig { 第二步,重启后端服务,再次点击登录按钮,发现请求已经可以正常访问了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/cors-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/cors-5.png) 本例中,后端返回 `Access-Control-Allow-Origin: http://localhost:8080` 就表示,跑在 9002 端口下的后端接口可以被 8080 端口的前端请求访问。 @@ -134,11 +134,11 @@ config.addAllowedOriginPattern("*"); 非简单请求在正式通信之前,会增加一次 HTTP 查询请求,称为“预检”请求。预检请求通过后,才会返回正常的响应内容。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/cors-6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/cors-6.png) 拿编程猫的文章管理页来举例,该页面会向后端发起一个 `posts/queryPageable` 的分页查询,该请求包含了一个自定义的消息头 Authorization,于是浏览器认为该请求是一个非简单请求,然后就会自动发起一次 OPTIONS 请求,但由于我们的 Spring Boot 项目整合了 SpringsScurity 安全管理框架,没有对OPTIONS请求放开登录认证,导致验证失败,文章分页请求的响应数据就没有返回回来。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/cors-7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/cors-7.png) 第三步,通过以下代码给 OPTIONS 请求放行。 @@ -157,17 +157,17 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { 再次重启后端服务,重新访问文章列表接口,发现有响应数据了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/cors-8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/cors-8.png) 非简单请求必须首先使用 OPTIONS 请求方法发起一个预检请求到服务器端,以获知服务器是否允许该实际请求。"预检请求“的使用,避免了跨域请求对服务器的用户数据造成未预期的影响。 我们来通过两张图片简单总结一下预检请求的整个过程,第一张,发起 OPTIONS 预检请求: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/cors-9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/cors-9.png) 第二章,发起正式请求: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/cors-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/cors-10.png) ### 四、源码路径 @@ -197,7 +197,7 @@ star 了这个仓库就等于你拥有了成为了一名优秀 Java 工程师的 [https://tobebetterjavaer.com/](https://tobebetterjavaer.com/) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/aop-log-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/aop-log-5.png) *没有什么使我停留——除了目的,纵然岸旁有玫瑰、有绿荫、有宁静的港湾,我是不系之舟*。 diff --git a/docs/springboot/initializr.md b/docs/springboot/initializr.md index fa73c32cb..4a36f9768 100644 --- a/docs/springboot/initializr.md +++ b/docs/springboot/initializr.md @@ -16,7 +16,7 @@ Spring 官方提供了 Spring Initializr 的方式来创建 Spring Boot 项目 打开后的界面如下: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/initializr-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/initializr-01.png) 可以将 Spring Initializr 看作是 Spring Boot 项目的初始化向导,它可以帮助开发人员在一分钟之内创建一个 Spring Boot 骨架,非常的傻瓜式。 @@ -43,26 +43,26 @@ Spring 官方提供了 Spring Initializr 的方式来创建 Spring Boot 项目 好,接下来我们使用 Spring Initializr 初始化一个 Web 项目,Project 选择 Maven,Spring Boot 选择 2.6.1,Java 选择 JDK 8,Dependencies 选择「Build web, including RESTful, applications using Spring MVC. Uses Apache Tomcat as the default embedded container.」 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/initializr-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/initializr-02.png) 这预示着我们会采用 SpringMVC 并且使用 Tomcat 作为默认服务器来开发一个 Web 项目。 然后点击底部的「generate」按钮,就会生成一个 Spring Boot 初始化项目的压缩包。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/initializr-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/initializr-03.png) ### 二、Spring Boot 项目结构分析 解开压缩包,并导入到 Intellij IDEA 中,可以看到 Spring Boot 项目的目录结构。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/initializr-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/initializr-04.png) 可以使用 `tree -CfL 3` 命令以树状图列出目录的内容: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/initializr-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/initializr-05.png) - src/main/java 为项目的开发目录,业务代码在这里写。 @@ -76,12 +76,12 @@ Spring 官方提供了 Spring Initializr 的方式来创建 Spring Boot 项目 第一次启动,我个人习惯在 main 类中右键,在弹出的右键菜单这种选择「run ... main()」启动。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/initializr-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/initializr-06.png) 经过 2.5s 左右的 build 后,项目启动成功了,可以在日志中看到 Web 项目是以 Tomcat 为容器的,默认端口号为 8080,根路径为空。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/initializr-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/initializr-07.png) 这要比传统的 Web 项目省事省心省力,不需要打成 war 包,不需要把 war 包放到 Tomcat 的 webapp 目录下再启动。 @@ -91,7 +91,7 @@ Spring 官方提供了 Spring Initializr 的方式来创建 Spring Boot 项目 打开 Terminal 终端, 执行命令 `mvn clean package`,等待打包结果。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/initializr-08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/initializr-08.png) 我们的项目在初始化的时候选择的是 Maven 构建方式,所以 pom.xml 文件中会引入 spring-boot-maven-plugin 插件。 @@ -109,17 +109,17 @@ Spring 官方提供了 Spring Initializr 的方式来创建 Spring Boot 项目 因此我们就可以利用 Maven 命令来完成项目打包,打包完成后,进入 target 目录下,就可以看到打包好的 jar 包了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/initializr-09.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/initializr-09.png) 利用终端工具 [Tabby](https://mp.weixin.qq.com/s/HeUAPe4LqqjfzIeWDe8KIg),将 jar 包上传到服务器。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/initializr-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/initializr-10.png) 执行 `java -jar tobebetterjavaer-0.0.1-SNAPSHOT.jar` 命令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/initializr-11.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/initializr-11.png) what??????竟然没有安装 JDK。好吧,为了带白票阿里云服务器的小伙伴一起学习 Linux,我下了血本自己买了一台零添加的服务器。 @@ -130,14 +130,14 @@ PS:需要在 centos 环境下安装 JDK 的小伙伴可以看这篇。 安装好 JDK 后,再次执行命令就可以看到 Spring Boot 项目可以正常在服务器上跑起来了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/initializr-12.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/initializr-12.png) ### 四、开发第一个 Spring Boot 项目 项目既然启动成功了,我们在浏览器里访问 8080 端口测试下吧。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/initializr-13.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/initializr-13.png) 咦,竟然 Whitelabel 了,这个 404 页面是 Spring Boot 默认的错误页面,表示我们的请求在 Web 服务中不存在。 @@ -160,7 +160,7 @@ public class HelloController { 这段代码的业务逻辑非常简单,用户发送 hello 请求,服务器端响应一个“hello, springboot”回去。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/initializr-14.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/initializr-14.png) OK,现在可以访问到了。也就表明我们的第一个 Spring Boot 项目开发完成了。 diff --git a/docs/springboot/ioc.md b/docs/springboot/ioc.md index 2c8d3e545..03a3853fd 100644 --- a/docs/springboot/ioc.md +++ b/docs/springboot/ioc.md @@ -9,7 +9,7 @@ tag: 大家好,我是二哥呀。不废话,今天来带你一分钟玩转 Spring IoC。Spring 框架是 Java 后端开发中非常重要的基础框架,可以说必不可缺,而 IoC 又是 Spring 体系中最重要的两个概念之一(另外一个是谁呢?)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-dfa5b7d3-43c3-492f-a9f5-59d3bf7b242b) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-dfa5b7d3-43c3-492f-a9f5-59d3bf7b242b) ## 是何 @@ -17,21 +17,21 @@ Spring 全家桶中最重要的几个项目都是基于 Spring Framework 的, 首先它的右侧有 Github 的链接,另外点到「LEARN」这里,就会看到各个版本的文档。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-ecc7dcc3-678f-4b97-8e2b-42fc0d66f555) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-ecc7dcc3-678f-4b97-8e2b-42fc0d66f555) 那我们点「Reference Doc」,就能够看到它的一些模块的介绍: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-7ba176c1-5d76-465d-b2b5-5751d6c1ef3b) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-7ba176c1-5d76-465d-b2b5-5751d6c1ef3b) 第一章 Overview,讲述它的历史、设计原理等等; 第二章 Core,包含了 IoC 容器,AOP 等等,那自然是讲 Spring 的核心了,要点进去好好看了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-b068d0cc-8e75-42fc-8391-0cc0e2d84fc8) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-b068d0cc-8e75-42fc-8391-0cc0e2d84fc8) 点进去之后发现了宝贵的学习资料,一切的 what, why, how 都可以在这里找到答案。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-e1f5ad2c-2b1a-453e-8672-dd4b6560eb7a) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-e1f5ad2c-2b1a-453e-8672-dd4b6560eb7a) 这里很好的解释了大名鼎鼎的 IoC - Inversion of Control, 控制反转。 @@ -53,7 +53,7 @@ Bean 是 Spring 的主角,有种说法叫 Spring 就是面向 bean 的编程 既然说容器是 IoC 最重要的部分,那么 Spring 如何设计容器的呢?还是回到官网,第二段有介绍哦: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-c5c48fc9-162b-4e7c-a660-2984bb7fb69a) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-c5c48fc9-162b-4e7c-a660-2984bb7fb69a) 答:使用 `ApplicationContext`,它是 `BeanFactory` 的子类,更好的补充并实现了 `BeanFactory` 的。 @@ -66,7 +66,7 @@ Bean 是 Spring 的主角,有种说法叫 Spring 就是面向 bean 的编程 而 `ApplicationContext` 多了很多功能,因为它继承了多个接口,可称之为“高级容器”。在下文的搭建项目中,我们会使用它。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-ac7721c3-7892-45a7-840a-2093548f13d6) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-ac7721c3-7892-45a7-840a-2093548f13d6) `ApplicationContext` 的里面有两个具体的实现子类,用来读取配置配件的: @@ -188,9 +188,9 @@ IoC 和 DI 也并非 Spring 框架提出来的,Spring 只是应用了这个设 在项目中,底层的实现都是由很多个对象组成的,对象之间彼此合作实现项目的业务逻辑。但是,很多很多对象紧密结合在一起,一旦有一方出问题了,必然会对其他对象有所影响,所以才有了解藕的这种设计思想。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-2231ebcf-6677-4007-8460-30cb4ed6ac6f) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-2231ebcf-6677-4007-8460-30cb4ed6ac6f) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-19cbe7a6-e7ae-43ed-a27a-91b39f05f899) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-19cbe7a6-e7ae-43ed-a27a-91b39f05f899) 如上图所示,本来 ABCD 是互相关联在一起的,当加入第三方容器的管理之后,每个对象都和第三方法的 IoC 容器关联,彼此之间不再直接联系在一起了,没有了耦合关系,全部对象都交由容器来控制,降低了这些对象的亲密度,就叫“解藕”。 @@ -204,7 +204,7 @@ IoC 和 DI 也并非 Spring 框架提出来的,Spring 只是应用了这个设 ### Spring Framework 八大模块 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-02dc5458-7423-44ce-97b4-d199decac2ad) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-02dc5458-7423-44ce-97b4-d199decac2ad) 模块化的思想是 Spring 中非常重要的思想。 @@ -241,15 +241,15 @@ Btw, 这张图在网上有很多,但是在我却没有在最新版的 referenc 下载地址: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-24ce85b8-33b2-4c6e-a39b-0e9233b29d9b) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-24ce85b8-33b2-4c6e-a39b-0e9233b29d9b) 如果你要问我怎么找的,那就还是从刚才 `4.3.26` 版本的 `Reference Doc` 中进去,然后刚开头就有一个 `Distribution Zip Files`, -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-7ddf8503-cfb4-4263-9fb2-f0ac3be66f66) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-7ddf8503-cfb4-4263-9fb2-f0ac3be66f66) 好奇心带着我打开了它,发现... -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-80b10d48-a976-4d11-9751-76f5c0355452) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-80b10d48-a976-4d11-9751-76f5c0355452) 发现了仓库地址! @@ -257,7 +257,7 @@ Btw, 这张图在网上有很多,但是在我却没有在最新版的 referenc 我们搜 5.2.3 版的,它在最下面: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-f64e3ddc-e5cf-4540-b7a1-96ce790df2e5) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-f64e3ddc-e5cf-4540-b7a1-96ce790df2e5) 然后就可以愉快的使用了~ @@ -272,11 +272,11 @@ Btw, 这张图在网上有很多,但是在我却没有在最新版的 referenc 答案是:`下载的 docs.zip → spring-framework-reference → images → spring-overview` -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-0bde232f-797f-449a-977f-cbe4efa989c0) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-0bde232f-797f-449a-977f-cbe4efa989c0) 我们需要导入 Intellij 的 jar 包在哪里呢?Dist.zip → libs -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-75e82791-6bfb-45ac-bc85-dffd6f493191) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-75e82791-6bfb-45ac-bc85-dffd6f493191) 这里可以看到,每个黑色框对应3个 jar 包,我们要导入 Intellij 的是 `RELEASE.jar`. @@ -286,13 +286,13 @@ Btw, 这张图在网上有很多,但是在我却没有在最新版的 referenc 然后在 External Libraries 中导入我们刚才在模块图里看到的那4个模块所对应的 jar 包,结构如下: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-d77fa83c-b82a-4fc5-8910-243798d03acb) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-d77fa83c-b82a-4fc5-8910-243798d03acb) 这样你以为就大功告成了吗?Too young too simple 啊~ 来运行一下: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-d33302bb-0d2d-403e-95ee-216d0b859952) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-d33302bb-0d2d-403e-95ee-216d0b859952) 出现了老盆友:`no class def found error`, 就是找不到这个类。 @@ -310,7 +310,7 @@ Btw, 这张图在网上有很多,但是在我却没有在最新版的 referenc 官网里都给我们写好了: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-dcef8c4b-f7d3-445e-a809-5a66eab9e1e9) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-dcef8c4b-f7d3-445e-a809-5a66eab9e1e9) 第一段是一些命名空间及其规范的介绍, @@ -329,11 +329,11 @@ Btw, 这张图在网上有很多,但是在我却没有在最新版的 referenc 我的 service 文件配置如下: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-6a0894e5-5765-48dd-a04f-29edb9c270fa) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-6a0894e5-5765-48dd-a04f-29edb9c270fa) #### 4\. 最后一步,我们再来看它是怎么用的: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-f349d1b3-de75-4bb7-98f1-93918cea5aa9) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-f349d1b3-de75-4bb7-98f1-93918cea5aa9) 这里面并没有直接的 new 这个 service,但是 Spring 容器帮我们创建了这个对象。 @@ -343,11 +343,11 @@ Btw, 这张图在网上有很多,但是在我却没有在最新版的 referenc 然后第二行,就是获取具体的 bean 了。这个其实有很多方式,在使用的时候就能看到: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-bbb71e68-a9b0-46d1-b4f8-02cf28130a9c) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-bbb71e68-a9b0-46d1-b4f8-02cf28130a9c) 点进去发现,是在 BeanFactory.class 里定义的: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-36952030-7731-4f76-bc34-184d35502df6) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-36952030-7731-4f76-bc34-184d35502df6) 这其中比较常用的是通过 @@ -359,7 +359,7 @@ Btw, 这张图在网上有很多,但是在我却没有在最新版的 referenc 照猫画虎,我的 test 文件改动如下: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-c0551d04-aa95-46c7-badf-b825f58d4694) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-c0551d04-aa95-46c7-badf-b825f58d4694) 成功运行~~🎉🎉 @@ -397,7 +397,7 @@ public class MyTest { 定义一个无参的 constructor,里面打印一句话,然后只 `new ClassPathXmlApplicationContext`,如下图: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-f987d03d-1f8a-47b2-b157-b325081f54b0) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-f987d03d-1f8a-47b2-b157-b325081f54b0) 发现也是可以打印的,所以其实是每次启动容器的时候,就已经创建好容器中的所有对象了。(当然,这在 `scope = "prototype"` 的时候不适用,只是 singleton 的时候。) @@ -419,13 +419,13 @@ public class MyTest { New Project 的时候要选择从 Maven 构建,而不是一个简单的 Java 项目。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-051637c7-1ae4-4b32-9e7c-ac05d5a8fc38) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-051637c7-1ae4-4b32-9e7c-ac05d5a8fc38) 建好之后,我们会发现比起刚才的 Java 项目,多了很多东西: 和之前的空项目不太一样,这里有 `main`, `test`,其中 `resources` 是放配置文件的地方,也就是我们刚才的 `service.xml` 应该放在这里,如果没有放对位置是代码找不到哦~ -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-773f1aaf-eef1-4fcb-abee-331d31e3e9e2) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-773f1aaf-eef1-4fcb-abee-331d31e3e9e2) #### 2\. 添加对应的 pom 依赖,就不用手动导 jar 包了 @@ -449,7 +449,7 @@ New Project 的时候要选择从 Maven 构建,而不是一个简单的 Java 答:`new 的过程`。把 new 的过程交给第三方来创建、管理,这就是「解藕」。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/ioc-1a4f2b12-34a4-4fc1-94be-bd9a1a4c16e8) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/ioc-1a4f2b12-34a4-4fc1-94be-bd9a1a4c16e8) Spring 也是用的 `set()` 方法,它只不过提供了一套更加完善的实现机制而已。 diff --git a/docs/springboot/jwt.md b/docs/springboot/jwt.md index 94c7764a0..f7c721768 100644 --- a/docs/springboot/jwt.md +++ b/docs/springboot/jwt.md @@ -10,7 +10,7 @@ 单机情况下,这种模式是没有任何问题的,但对于前后端分离的 Web 应用来说,就非常痛苦了。于是就有了另外一种解决方案,服务器端不再保存 session 数据,而是将其保存在客户端,客户端每次发起请求时再把这个数据发送给服务器端进行验证。**JWT**(JSON Web Token)就是这种方案的典型代表。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/jwt-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/jwt-1.png) ### 一、关于 JWT @@ -29,11 +29,11 @@ JWT,是目前最流行的一个[跨域](https://mp.weixin.qq.com/s/HTMDZaukCb7 假如我现在使用用户名 wanger 和密码 123456 进行访问编程喵(Codingmore)的 login 接口,那么实际的 JWT 是一串看起来像是加过密的字符串。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/jwt-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/jwt-2.png) 为了让大家看的更清楚一点,我将其复制到了 [jwt 的官网](https://jwt.io/)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/jwt-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/jwt-3.png) @@ -73,7 +73,7 @@ export function setToken (token) { Authorization: Bearer ``` -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/jwt-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/jwt-4.png) 服务器端接收到请求后,再对 JWT 进行验证,如果验证通过就返回相应的资源。 @@ -315,7 +315,7 @@ JwtAuthenticationTokenFilter 继承了 OncePerRequestFilter,该过滤器能 这个过滤器非常关键啊,基本上每行代码我都添加了注释,当然了,为了确保大家都能搞清楚这个类到底做了什么,我再来画一幅流程图,这样就一清二楚了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/jwt-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/jwt-5.png) SpringSecurity 是一个安全管理框架,可以和 Spring Boot 应用无缝衔接,SecurityContextHolder 是其中非常关键的一个工具类,持有安全上下文信息,里面保存有当前操作的用户是谁,用户是否已经被认证,用户拥有的权限等关键信息。 @@ -334,20 +334,20 @@ SecurityContextHolder 默认使用了 ThreadLocal 策略来存储认证信息, 第一步,访问 login 接口,输入用户名和密码进行登录,获取服务器端返回的 JWT。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/jwt-6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/jwt-6.png) 第二步,收集服务器端返回的 tokenHead 和 token,将其填入 Authorize(注意 tokenHead 和 token 之间有一个空格)完成登录认证。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/jwt-7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/jwt-7.png) 第三步,再次请求其他接口时,Swagger 会自动将 Authorization 作为请求头信息发送到服务器端。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/jwt-8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/jwt-8.png) 第四步,服务器端接收到该请求后,会通过 JwtAuthenticationTokenFilter 过滤器对 JWT 进行校验。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/jwt-9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/jwt-9.png) 到此为止,整个流程全部打通了,完美! @@ -380,7 +380,7 @@ star 了这个仓库就等于你拥有了成为了一名优秀 Java 工程师的 [https://tobebetterjavaer.com/](https://tobebetterjavaer.com/) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/jwt-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/jwt-10.png) *没有什么使我停留——除了目的,纵然岸旁有玫瑰、有绿荫、有宁静的港湾,我是不系之舟*。 diff --git a/docs/springboot/tomcat.md b/docs/springboot/tomcat.md index 7ef1fc8be..5a93a22c2 100644 --- a/docs/springboot/tomcat.md +++ b/docs/springboot/tomcat.md @@ -33,16 +33,16 @@ tag: 如果你不确定自己的 Maven 本地仓库在哪里,可以在终端执行 `mvn help:effective-settings` 命令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/tomcat-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/tomcat-01.png) 顺藤摸瓜,根据 parent 的 groupId、artifactId、version 可以锁定 spring-boot-starter-parent.pom 文件的位置。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/tomcat-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/tomcat-02.png) 使用文本编辑器打开以后大致可以看到以下内容: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/tomcat-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/tomcat-03.png) - 定义了 JDK 的版本为 1.8 - 项目默认的编码方式为 UTF-8 @@ -51,7 +51,7 @@ tag: 照葫芦画瓢,我们按照同样的方法找到 spring-boot-dependencies.pom 文件。可以看到这里面定义了一系列的属性和依赖,差不多 2800 行。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/tomcat-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/tomcat-04.png) 有消息队列依赖、commons 工具包依赖、数据库链接依赖、HTTP 链接依赖、Spring 家族依赖、Web 服务器依赖等等。 @@ -64,7 +64,7 @@ Spring Boot 会帮我们选好最稳定的新版本,这体现出了 Spring Boo 理解了这一点,我们再来继续看 pom.xml 文件,里面有一个 `spring-boot-starter-web` 依赖。这一次,我们直接按住 Ctrl 键(macOS 是 Command 键),点击鼠标左键就可以跳转到 spring-boot-starter-web.pom 的源文件了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/tomcat-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/tomcat-05.png) 部分源码如下: @@ -160,17 +160,17 @@ spring-webmvc 是 Spring MVC 的一个实现。spring-webmvc 依赖于 spring-we 从这里可以看出来SpringBoot默认的启动容器是Tomcat,Tomcat 的组成核心 jakarta.annotation、tomcat-embed-core、tomcat-annotations-api、org.apache.tomcat.embed 全部都通过 Maven 引入过来了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/tomcat-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/tomcat-06.png) core 的版本是 9.0.55,Tomcat 官网上最新的 9.0.x 版本是 9.0.56,高了一个版本。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/tomcat-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/tomcat-07.png) 不过无所谓,直接下载 9.0.56 的 src,对比看一下,是否大致相同。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/springboot/tomcat-08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/springboot/tomcat-08.png) 对比之下可以看得出,Spring Boot 引入的 Tomcat 更精简一点,大体上都是相同的,这也就是**为什么Spring Boot 不需要额外安装 Tomcat 的根本原因了**。 diff --git a/docs/string/constant-pool.md b/docs/string/constant-pool.md index dc9751106..21ddebeb6 100644 --- a/docs/string/constant-pool.md +++ b/docs/string/constant-pool.md @@ -65,11 +65,11 @@ String s1 = "三妹"; 在 Java 8 之前,字符串常量池在永久代中。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/string/constant-pool-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/string/constant-pool-01.png) Java 8 之后,移除了永久代,字符串常量池就移到了堆中。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/string/constant-pool-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/string/constant-pool-02.png) “哥,能再简单给我解释一下方法区,永久代和元空间的概念吗?有点模糊。”三妹说。 diff --git a/docs/string/equals.md b/docs/string/equals.md index d8f9bac4e..57f205ddd 100644 --- a/docs/string/equals.md +++ b/docs/string/equals.md @@ -20,7 +20,7 @@ tag: 有一对双胞胎,姐姐叫阿丽塔,妹妹叫洛丽塔。我们普通人可能完全无法分辨谁是姐姐谁是妹妹,可她们的妈妈却可以轻而易举地辨认出。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/string/equals-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/string/equals-01.png) `.equals()` 就好像我们普通人,看见阿丽塔以为是洛丽塔,看见洛丽塔以为是阿丽塔,看起来一样就觉得她们是同一个人;“==”操作符就好像她们的妈妈,要求更严格,观察更细致,一眼就能分辨出谁是姐姐谁是妹妹。 diff --git a/docs/string/intern.md b/docs/string/intern.md index 99ff6d7b2..10e3e8134 100644 --- a/docs/string/intern.md +++ b/docs/string/intern.md @@ -67,7 +67,7 @@ false “我来画幅图,帮助你理解下。”看到三妹惊讶的表情,我耐心地说。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/string/intern-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/string/intern-01.png) “这下理解了吧?”我问三妹。 @@ -99,7 +99,7 @@ true “我再来画幅图,帮助你理解下。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/string/intern-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/string/intern-02.png) “哇,我明白了!”三妹长舒一口气,大有感慨 intern 也没什么难理解的意味。 diff --git a/docs/string/split.md b/docs/string/split.md index 47a414cfb..806abc7b1 100644 --- a/docs/string/split.md +++ b/docs/string/split.md @@ -94,7 +94,7 @@ cmower.split("[.]"); 除此之外, 还可以使用 Pattern 类的 `quote()` 方法来包裹英文逗点“.”,该方法会返回一个使用 `\Q\E` 包裹的字符串。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/string/split-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/string/split-01.png) 来看示例: @@ -185,7 +185,7 @@ String [] parts = cmower.split("(?=,)"); “它其实是正则表达式中的断言模式。”我说,“你有时间的话,可以看看前面我推荐的两份开源文档。” -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/string/split-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/string/split-02.png) “`split()` 方法可以传递 2 个参数,第一个为分隔符,第二个为拆分的字符串个数。”我说。 @@ -199,7 +199,7 @@ if (cmower.contains(",")) { 进入 debug 模式的话,可以看到以下内容: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/string/split-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/string/split-03.png) 也就是说,传递 2 个参数的时候,会直接调用 `substring()` 进行截取,第二个分隔符后的就不再拆分了。 diff --git a/docs/szjy/tobebetterjavaer-beian.md b/docs/szjy/tobebetterjavaer-beian.md index a70618682..1dc4c4924 100644 --- a/docs/szjy/tobebetterjavaer-beian.md +++ b/docs/szjy/tobebetterjavaer-beian.md @@ -26,18 +26,18 @@ u1s1,二哥是全天下最良心的博主——之一,是没毛病的。 对了,剩下的 9 单新客(仍然可以返 60 元),还有需要的小伙伴可以扫描下面的二维码添加二哥的微信,备注「**服务器**」即可。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-3d9a98e4-2ae2-4e71-805c-70a918176b8d) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-3d9a98e4-2ae2-4e71-805c-70a918176b8d) 有些买过服务器的小伙伴,已经把个人博客整起来了,这里推荐三个给大家欣赏一下(可以直接复制图片下的链接到浏览器地址栏),真的惊艳~ -![http://zhuoke.xyz/](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-7379f1e6-6967-447e-9f62-e19ba1af927d.png) +![http://zhuoke.xyz/](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-7379f1e6-6967-447e-9f62-e19ba1af927d.png) **打开这个网站的时候要小心,小心差点鼻血流出来**~~~~ -![https://laifeng.xyz/about_me/](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-182e856b-771c-4445-b508-dbabded164f2.png) +![https://laifeng.xyz/about_me/](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-182e856b-771c-4445-b508-dbabded164f2.png) -![https://www.zm211314.top/](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-67d47432-e571-48ad-8c12-4638264d82bf.png) +![https://www.zm211314.top/](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-67d47432-e571-48ad-8c12-4638264d82bf.png) 不得不说,都是二哥的铁粉,个人博客还不忘夸二哥一番。 @@ -55,27 +55,27 @@ u1s1,二哥是全天下最良心的博主——之一,是没毛病的。 今天来给大家推荐一款玩转云服务器的神器——**宝塔面板**,有了这玩意,服务器能玩一整年,甚至余生! -![https://www.bt.cn/](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-ad40a5f7-30a8-4c6c-9671-c389432e16de.png) +![https://www.bt.cn/](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-ad40a5f7-30a8-4c6c-9671-c389432e16de.png) --------开始保姆级演示如何安装宝塔面板------- 为了给大家呈现出保姆级的教程,我自己新下单了一款轻量应用服务器。登录阿里云服务器后台,可以看到这台服务器正在运行当中。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-11df60df-a51c-48d4-a704-343d01d44777.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-11df60df-a51c-48d4-a704-343d01d44777.png) 点击「远程链接」的小图标,第一次需要手机验证码,之后进入到在线版的终端窗口。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-1fc16ac7-10b1-4e61-98f5-d48bb09ab626.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-1fc16ac7-10b1-4e61-98f5-d48bb09ab626.png) 可以按照提示输入 `sudo su root` 命令切换到 root 账户。 切换到宝塔Linux 面板页,可以看到对应的安装命令,如下图所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-ce7e94b5-c90b-422e-a7e3-942c615c2837.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-ce7e94b5-c90b-422e-a7e3-942c615c2837.png) 我的云服务器镜像选择的是 CentOS,所以可以直接复制 yum 命令 `yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh` 到云服务器的「终端」下进行在线安装。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-b7dcf834-294a-4bd0-86f0-7b0b92c5b3fe.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-b7dcf834-294a-4bd0-86f0-7b0b92c5b3fe.png) yum 命令是 CentOS 的优势,可以在线安装和升级软件。当出现上图提示的内容(外网面板地址、用户名和密码)后就表示宝塔面板安装成功了! @@ -83,19 +83,19 @@ yum 命令是 CentOS 的优势,可以在线安装和升级软件。当出现 如果无法访问,表示服务器没有开放 8888 端口。选择我的轻量服务器,选择防火墙,选择「添加规则」。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-65f67086-cc35-4b7f-89b4-d73e51a859e0.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-65f67086-cc35-4b7f-89b4-d73e51a859e0.png) 能看到目前服务器只开放了 HTTP、HTTPS 和 SSH 的端口,8888 端口还没有放行。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-5f7fa1a0-1810-40a0-b371-ae2ee5948f80.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-5f7fa1a0-1810-40a0-b371-ae2ee5948f80.png) 再次访问外网面板地址,就可以看到登录页面了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-1233ea14-2e6b-48d9-b278-ab87b3cc3cbd.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-1233ea14-2e6b-48d9-b278-ab87b3cc3cbd.png) 登录成功后(让浏览器帮你记住账号和密码),如果之前有宝塔官方账号,可以选择绑定,如果没有的话,注册一个。之后就可以看到宝塔面板推荐我们安装的服务器软件了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-e618418b-1862-4db2-9d61-61358330b77d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-e618418b-1862-4db2-9d61-61358330b77d.png) 通常来说,直接安装 LNMP 组合包就可以了,包括可以一键安装到 Linux 环境的 Nginx、MySQL、PHP 等成员。 @@ -110,7 +110,7 @@ MySQL 基本上是中小型服务器必备的关系型数据库软件;PHP 虽 点击「一键安装」,宝塔面板就会帮我们自动安排上了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-036631d0-5160-4b9e-a750-0f15966e1ee3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-036631d0-5160-4b9e-a750-0f15966e1ee3.png) 到此为止,宝塔面板的基础环境就准备完成了。可以在上面按照左侧的菜单项目对服务器进行操作了,比如说: @@ -128,7 +128,7 @@ MySQL 基本上是中小型服务器必备的关系型数据库软件;PHP 虽 这里先说一下终端,第一次进来的时候需要进行 SSH 账号验证。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-08cec2c4-0c4d-4b06-aefb-be29ef1052e6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-08cec2c4-0c4d-4b06-aefb-be29ef1052e6.png) 密码从哪里来的呢? @@ -136,17 +136,17 @@ MySQL 基本上是中小型服务器必备的关系型数据库软件;PHP 虽 为了方便演示,这里选择选择「设置密码」。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-0560b7fc-4c22-4dd3-be91-d35b631b93e2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-0560b7fc-4c22-4dd3-be91-d35b631b93e2.png) 设置完成后,重启服务器生效。之后在宝塔面板的「终端」面板下填写密码就可以链接了。不过在浏览器的终端里敲命令总感觉有点不太方便,我们最好选择 iterm2、putty、xshell 这样的客户端。 再说一下软件商店,有付费的有免费的,不过对于我们个人服务器来说,免费版的都足够用了。需要什么安装什么就好了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-6f4f2ed4-74cf-4cc2-af6c-1750f14d3846.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-6f4f2ed4-74cf-4cc2-af6c-1750f14d3846.png) 上传下载文件也非常方便。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-aefbcc8e-28c3-48ff-bf66-efdcb8fd57ae.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-aefbcc8e-28c3-48ff-bf66-efdcb8fd57ae.png) ---再再割一下------ @@ -170,15 +170,15 @@ MySQL 基本上是中小型服务器必备的关系型数据库软件;PHP 虽 我这里以阿里云为例,购入一个 tobebetterjavaer.com 的域名(寓意通过 Java 程序员进阶之路,成为一个更好的 Java 工程师)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-yuming-jiexi-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-yuming-jiexi-02.png) 在此之前呢,我已经购买了一台阿里云的服务器,2核4G内存的轻量级云服务器。就是上次带大家白票的那波,我自己也购入了一台。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-yuming-jiexi-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-yuming-jiexi-03.png) 这台服务器上目前已经安装了[宝塔面板](https://mp.weixin.qq.com/s/ditN9J80rSWwnYRumwb4ww)、[Nginx](https://mp.weixin.qq.com/s/OYOcjUwPZyPo8K4KAgJ4kw),并且可以通过 IP 地址成功访问 80 端口。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-yuming-jiexi-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-yuming-jiexi-04.png) 我想做什么呢? @@ -186,7 +186,7 @@ MySQL 基本上是中小型服务器必备的关系型数据库软件;PHP 虽 直接在浏览器地址栏里输入域名访问肯定是不行的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-yuming-jiexi-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-yuming-jiexi-05.png) 那该怎么办呢? @@ -200,19 +200,19 @@ MySQL 基本上是中小型服务器必备的关系型数据库软件;PHP 虽 云解析 DNS 支持 A、AAAA 、CNAME 等记录类型。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-yuming-jiexi-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-yuming-jiexi-06.png) 进入域名控制台,选择要解析的域名,点击「解析」会跳转到解析设置页面。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-yuming-jiexi-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-yuming-jiexi-07.png) 直接点击「新手指导」按钮,填写服务器的 IP 地址。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-yuming-jiexi-08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-yuming-jiexi-08.png) 该方法可以同时添加 www 和 @ 记录,成功后,可以通过带 www 和不带 www 的方式访问网站。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-yuming-jiexi-09.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-yuming-jiexi-09.png) - 主机记录 @ 表示可以直接通过不带 www 的域名访问,也就是 tobebetterjavaer.com; - 主机记录 www 表示可以带 www 的域名访问,也就是 www.tobebetterjavaer.com; @@ -221,7 +221,7 @@ TTL 为缓存时间,数值越小,表示修改记录生效的时间越快, 记得对域名进行实名认证,认证通过后(否则域名会处于锁定状态 serverhold),再次刷新页面,就可以访问成功了! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-yuming-jiexi-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-yuming-jiexi-10.png) nice! @@ -229,7 +229,7 @@ nice! 新注册的域名在短时间内是可以访问到的(参照[上篇](https://mp.weixin.qq.com/s/9Tn5d2ey2lr06oGPZSp6Qw)),但过一段时间后,就会提示「网站暂时无法访问」。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-1.png) 这就意味着我们需要对网站进行备案。关于网站备案,百度百科是这样解释的: @@ -241,25 +241,25 @@ nice! 登录阿里云,点击「ICP 备案」(指网站在信息产业部提交网站信息进行官方认可)菜单,点击「开始备案」。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-2.png) 初次备案的小伙伴建议看一遍「秒懂备案」的视频,对备案进行简单地了解,方便后续操作的时候对备案有一个大致的印象。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-3.png) 填写基本信息。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-4.png) 点击「信息校验」,通过后进入下一步。如果不通过,按照对应提示信息进行修改,一般新注册的域名需要 3 天的实名认证审核周期。 填写主办者信息: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-5.png) 填写网站信息: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-6.png) 完成后会提示我们下载「阿里云 APP」上传一些资料,主要是方便上传身份证和人脸认证(注意不要带眼镜,否则容易校验不通过,踩坑人良心建议)。 @@ -267,20 +267,20 @@ nice! 这一步审核通过后,会受到工信部的短信核验,记得及时处理。然后等待管局审核,预计 9 天左右。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-7.png) 我差不多等了一周左右,管局审核终于通过了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-8.png) 现在立刻马上,登录[全国互联网安全管理平台](http://www.beian.gov.cn/portal/index)注册登录提交公安联网备案申请。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-9.png) 填写申请信息的时候最好不要用 Safari 浏览器,不然会提示要安装 flash 插件(果然政企网站的技术都非常地追求稳定啊)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-10.png) macOS 系统可以选择用 Chrome 浏览器。 @@ -290,27 +290,27 @@ macOS 系统可以选择用 Chrome 浏览器。 提交后再次等待审核。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-11.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-11.png) 等了四天,上去看了一下,审核通过了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-12.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-12.png) 我们需要做的是下载备案编号图标和复制备案编号 HTML 代码到网站上。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-13.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-13.png) ## HTTP升级到HTTPS 上一次,我们完成[域名解析](https://mp.weixin.qq.com/s/9Tn5d2ey2lr06oGPZSp6Qw)后,发现浏览器地址栏里的域名被提示为不安全,就是因为它还是个宝宝,没有升级为 HTTPS 证书。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-https-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-https-01.png) 那怎么升级为 HTTPS 证书呢?可以直接通过阿里云购买 SSL 证书,但特么巨贵! 本来想尝试一下 AWS 的免费 SSL 证书,但卡到验证码这一步就是收不到信息。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-https-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-https-02.png) 索性就还用 FreeSSL 吧。 @@ -320,55 +320,55 @@ FreeSSL.cn 是一个提供免费HTTPS证书申请的网站,网址如下: 输入域名 tobebetterjavaer.com 选择 trustAsia 品牌证书,点击「创建」,这次我选择的是三年期自动化(刚好我的服务器申请的是三年,域名也是三年),9.9 元,还是非常良心的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-https-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-https-03.png) 微信/支付宝支付完成后会跳到证书的订单列表。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-https-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-https-04.png) 选择「更多操作」里的订单详情,会跳转到 CertCloud 页的管理订单。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-https-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-https-05.png) 点击「提交 CSR」后点击「提交」。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-https-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-https-06.png) 接下来就到了域名验证环节,点击「获取验证信息」。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-https-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-https-07.png) 切换到域名解析设置页,准备添加记录。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-https-08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-https-08.png) 按照 CertCloud 提供的域名验证信息,添加记录。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-https-09.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-https-09.png) 添加完成后切换到 CertCloud,点击「域名验证」。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-https-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-https-10.png) 如果不确定上一步的记录是否添加成功,可以点击「诊断」按钮进行测试,如果没有问题会提示匹配成功的信息。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-https-11.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-https-11.png) 之后,点击「我已完成配置,检测一下」,如果没有问题,会先提示等待 CA 颁发证书,之后再次检测会提示「证书已签发,请刷新页面查看」。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-https-12.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-https-12.png) 好的,直接刷新页面,可以看到订单状态已经变成「已签发」的状态。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-https-13.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-https-13.png) 点击证书操作中的「下载证书」,选择适用于 Nginx 的 PEM 格式证书,点击下载。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-https-14.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-https-14.png) 使用 [Tabby 终端](https://mp.weixin.qq.com/s/HeUAPe4LqqjfzIeWDe8KIg)的「SFTP」将证书上传到网站的云服务器。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-https-15.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-https-15.png) 打开[宝塔面板](https://mp.weixin.qq.com/s/ditN9J80rSWwnYRumwb4ww),准备配置 Nginx 的 SSL 证书。将以下信息复制到 Nginx 的配置文件中,保存后重新加载配置。 @@ -397,15 +397,15 @@ server { 记得在宝塔面板和云服务器后台放行 443 端口。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-https-16.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-https-16.png) 在地址栏访问 `https://tobebetterjavaer.com` 就可以看到我们的域名已经升级为 HTTPS 了(安全锁的小图标也显示出来了)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-https-17.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-https-17.png) 这时候,如果我们访问 80 端口的 http,仍然是可以的。只不过仍然会显示一个不安全的提示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-https-18.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-https-18.png) 此时,我们需要将 HTTP 重定向到 HTTPS。 @@ -420,16 +420,16 @@ server { 注释掉原来的 80 端口监听,改为 return 跳转。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-https-19.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-https-19.png) 再次刷新原来的 HTTP 访问链接,可以看到已经跳转到 HTTPS 了,如果你查看地址栏的话,也会看到地址变成了 `https://tobebetterjavaer.com`。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-https-20.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-https-20.png) 顺带再给大家分享一个好消息,《Java 程序员进阶之路》网站 PV 访问人数已经突破了 1000,来到了 1168,又一个小小的里程碑~ -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-beian-c0665162-9d0d-4ded-b485-5ea535954457.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-beian-c0665162-9d0d-4ded-b485-5ea535954457.png) 三年之后又三年,希望这个小破站能自力更生地活下去。目前已有的花费有: diff --git a/docs/szjy/tobebetterjavaer-wangzhan-shangxian.md b/docs/szjy/tobebetterjavaer-wangzhan-shangxian.md index 318ef8069..94ae0d35a 100644 --- a/docs/szjy/tobebetterjavaer-wangzhan-shangxian.md +++ b/docs/szjy/tobebetterjavaer-wangzhan-shangxian.md @@ -16,7 +16,7 @@ tag: 经常逛 GitHub 的小伙伴应该已经发现了,二哥的《Java 程序员进阶之路》最近在持续霸榜,今天仍然在 GitHub 的周榜上。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-01.png) 这也是 2021-2022 年二哥收获的最后一点点小惊喜了。 @@ -24,7 +24,7 @@ tag: >码云 Pages:https://itwanger.gitee.io/tobebetterjavaer -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-02.png) 怎么解决这个问题呢? @@ -38,7 +38,7 @@ tag: 里面除了 md 文档和图片之外,还有代码示例,以及 docsify 的基础环境文件。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-03.png) - index.html 入口文件 - README.md 会做为主页内容渲染 @@ -52,17 +52,17 @@ docsify 是一个神奇的文档网站生成器,不同于 GitBook、Hexo 的 >码云地址:https://gitee.com/itwanger/toBeBetterJavaer -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-04.png) 同时,码云 Pages 也支持 Jekyll、Hugo、Hexo、docsify 等静态网站的服务。当 GitHub 仓库有更新后,直接在 GitHub Pages 上点一下刷新图标就会立即完成网站服务的同步工作。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-05.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-05.png) 通过码云 Pages 提供的网址就可以访问《Java 程序员进阶之路》网址了。 但由于种种原因,码云 Pages 没有提供自定义域名+ HTTPS 的服务,Pro 版支持,但也因为业务调整,关闭了个人用户的购买入口。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-06.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-06.png) 这就很扯了。 @@ -74,11 +74,11 @@ docsify 是一个神奇的文档网站生成器,不同于 GitBook、Hexo 的 进入本地《Java 程序员进阶之路》的仓库目录,执行 `docsify serve` 启动服务。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-07.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-07.png) 在浏览器地址栏访问 `http://localhost:3000`: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-08.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-08.png) 可以确认是没有问题的。 @@ -86,7 +86,7 @@ docsify 是一个神奇的文档网站生成器,不同于 GitBook、Hexo 的 不过,这样做会存在一个很严重的问题,就是云服务器和本地、GitHub 仓库之间没办法进行同步。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-09.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-09.png) ### 解决方案二 这是比较完善一点的解决方案:**在服务器上搭建一个 Git 仓库,从 GitHub 上拉取,再通过 Nginx 部署静态网站**。 @@ -95,7 +95,7 @@ docsify 是一个神奇的文档网站生成器,不同于 GitBook、Hexo 的 关于 Git 环境的搭建,我在《Java 程序员进阶之路》专栏的「Git」篇里已经详细的讲解了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-10.png) 搭建过程我这里简单演示下。 @@ -103,13 +103,13 @@ docsify 是一个神奇的文档网站生成器,不同于 GitBook、Hexo 的 CentOS 上可以直接通过 `yum install git` 命令来安装 Git 环境。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-11.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-11.png) **第二步,初始化 Git** 执行 `git init` 初始化 Git 目录。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-12.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-12.png) **第三步,克隆 GitHub 仓库到云服务器** @@ -121,50 +121,50 @@ CentOS 上可以直接通过 `yum install git` 命令来安装 Git 环境。 在 GitHub 仓库上点击「Code」菜单,复制 SSH 地址。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-13.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-13.png) 然后执行 `git clone` 命令就可以从远程仓库上拉取到最新内容了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-14.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-14.png) **第四步,通过 Nginx 部署静态网站** [Nginx](https://mp.weixin.qq.com/s/OYOcjUwPZyPo8K4KAgJ4kw) 非常适合用来部署静态网站,只需要将服务器的访问目录设定为 index.html 文件就可以了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-15.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-15.png) OK,此时再访问域名 `https://tobebetterjavaer.com` 就可以看到《Java 程序员进阶之路》的内容了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-16.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-16.png) 用这种方案的话,本地、GitHub、云服务器之间的同步就完全打通了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-17.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-17.png) 当 GitHub 上有更新的时候,再将内容拉取到云服务器上。 举例来说,我们在《Java 程序员进阶之路》专栏的 GitHub 仓库中修改 _sidebar.md 文件,追加一个感叹号的标点符号。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-18.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-18.png) 有两种办法拉取。 第一种,先执行 `git fetch`,再执行 `git merge`。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-19.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-19.png) `9909f82..7f4b815 master -> origin/master` 就表示内容有变动。 第二种,直接执行 `git pull` 命令。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-20.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-20.png) 不过,这有一点不尽善尽美,每当 GitHub 上有更新的时候,还要手动在云服务器上拉取更新,能不能做到自动化呢? 可以利用[宝塔面板](https://mp.weixin.qq.com/s/ditN9J80rSWwnYRumwb4ww)的计划任务,添加一个 Shell 脚本。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-21.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-21.png) 脚本内容很简单,就两行内容: @@ -179,19 +179,19 @@ git pull 我们在《Java 程序员进阶之路》专栏的 GitHub 仓库中修改 _sidebar.md 文件,修改中文的感叹号为英文的感叹号。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-22.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-22.png) 点击计划任务的「执行」按钮。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-23.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-23.png) 查看云服务器上的文件是否发生了变化。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-24.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-24.png) 到此为止,我们就完成了《Java 程序员进阶之路》网站从码云 Pages 到 VPS(Virtual private server,虚拟专用服务器)迁移的整个工作。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-25.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-25.png) 最后,再带着大家使用[不蒜子](https://busuanzi.ibruce.info/)给网站加一个总访问次数和总访客数吧,看看一个月后《Java 程序员进阶之路》的 PV 有多少。 @@ -204,11 +204,11 @@ git pull 刷新网页,发现已经有了哈,我是第一个。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-26.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-26.png) 紧接着,可以把 GitHub/码云上的预览链接从码云 Pages 修改为 `https://tobebetterjavaer.com` 了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tobebetterjavaer-wangzhan-shangxian-27.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tobebetterjavaer-wangzhan-shangxian-27.png) 简单总结一波:为了搭建这个网站,真的是能学到非常多的实战知识,比如说: diff --git a/docs/szjy/tupian-zhuanlian.md b/docs/szjy/tupian-zhuanlian.md index 3fad81495..193372ca2 100644 --- a/docs/szjy/tupian-zhuanlian.md +++ b/docs/szjy/tupian-zhuanlian.md @@ -5,16 +5,16 @@ ``` 举个例子,现在有这样一段 MD 文档,里面有一张图片。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tupian-zhuanlian-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tupian-zhuanlian-1.png) ``` 把上面的 MD 文档复制到掘金编辑器的时候,就会出现「图片解析中...」!但会一直卡在这里,再也解析不下去了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tupian-zhuanlian-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tupian-zhuanlian-2.png) 这是因为图片加了防盗链,掘金这么牛逼的社区在解析的时候也会失败。CSDN 的转链功能更牛逼一点,基本上可以无视防盗链。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tupian-zhuanlian-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tupian-zhuanlian-3.png) 还有一些博客平台是没有转链功能的,比如说二哥的静态小破站《Java 程序员进阶之路》。怎么办呢?我一开始的解决方案是: @@ -26,7 +26,7 @@ 首先要解决的是图片下载的问题,可以利用爬虫技术:爬虫爬得早,局子进的早。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tupian-zhuanlian-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tupian-zhuanlian-4.png) @@ -38,7 +38,7 @@ Java 爬虫的类库非常多,比如说 crawler4j,我个人更喜欢 jsoup jsoup 目前在 GitHub 上已经收获 9.3k+ 的 star,可以说是非常的受欢迎了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tupian-zhuanlian-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tupian-zhuanlian-5.png) jsoup 有以下特性: @@ -84,7 +84,7 @@ for (Element image : images) { 输出结构如下所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tupian-zhuanlian-6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tupian-zhuanlian-6.png) ### 四、下载图片 @@ -139,7 +139,7 @@ List list = fileReader.readLines(); 第三步,通过正则表达式来匹配是否有需要替换的图片标签,MD 中的图片标记关键字为 `![]()`。 ``` -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tupian-zhuanlian-7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tupian-zhuanlian-7.png) ``` 如果匹配到,就替换为 jsDelivr CDN 链接的地址,写文件时需要用到 hutool 的FileWriter 类。 @@ -161,15 +161,15 @@ writer.flush(); 转换前的 MD 文件如下所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tupian-zhuanlian-8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tupian-zhuanlian-8.png) 运行代码转换后,发现图片地址已经变成 jsDelivr CDN 图库了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tupian-zhuanlian-9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tupian-zhuanlian-9.png) 使用 GitHub 桌面版把图片和 MD 文档提交到 GitHub 仓库后,就可以看到图片已经加载完成可以访问了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tupian-zhuanlian-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tupian-zhuanlian-10.png) ### 六、一点小心得 @@ -178,7 +178,7 @@ writer.flush(); 这不,重新把《Java 程序员进阶之路》的小破站整理排版了一下,新增了不少优质的内容。学习 Java 的小伙伴可以开卷了,有需要增加的内容也欢迎提交 issue 啊! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/szjy/tupian-zhuanlian-11.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/szjy/tupian-zhuanlian-11.png) 再次感谢各位小伙伴的厚爱,我也会一如既往地完善这个专栏,我们下期见~ diff --git a/docs/thread/BlockingQueue.md b/docs/thread/BlockingQueue.md index fef7d9df2..c91ac1d47 100644 --- a/docs/thread/BlockingQueue.md +++ b/docs/thread/BlockingQueue.md @@ -16,7 +16,7 @@ tag: BlockingQueue基本操作总结如下(此图来源于JAVA API文档): -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/BlockingQueue-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/BlockingQueue-01.png) BlockingQueue继承于Queue接口,因此,对数据元素的基本操作有: @@ -370,7 +370,7 @@ tryTransfer方法如果当前有消费者线程(调用take方法或者具有 LinkedBlockingDeque是基于链表数据结构的有界阻塞双端队列,如果在创建对象时为指定大小时,其默认大小为Integer.MAX_VALUE。与LinkedBlockingQueue相比,主要的不同点在于,LinkedBlockingDeque具有双端队列的特性。LinkedBlockingDeque基本操作如下图所示(来源于java文档) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/BlockingQueue-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/BlockingQueue-02.png) @@ -384,7 +384,7 @@ LinkedBlockingDeque是基于链表数据结构的有界阻塞双端队列,如 另外,LinkedBlockingDeque实现了BlockingDueue接口而LinkedBlockingQueue实现的是BlockingQueue,这两个接口的主要区别如下图所示(来源于java文档): -![BlockingQueue和BlockingDeque的区别](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/BlockingQueue-03.png) +![BlockingQueue和BlockingDeque的区别](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/BlockingQueue-03.png) 从上图可以看出,两个接口的功能是可以等价使用的,比如BlockingQueue的add方法和BlockingDeque的addLast方法的功能是一样的。 diff --git a/docs/thread/ConcurrentHashMap.md b/docs/thread/ConcurrentHashMap.md index dd188e9e7..502d1baa1 100644 --- a/docs/thread/ConcurrentHashMap.md +++ b/docs/thread/ConcurrentHashMap.md @@ -360,7 +360,7 @@ put方法的代码量有点长,我们按照上面的分解的步骤一步步 在之前了解过HashMap以及1.8版本之前的ConcurrenHashMap都应该知道ConcurrentHashMap结构图,为了方面下面的讲解这里先直接给出,如果对这有疑问的话,可以在网上随便搜搜即可。 -![ConcurrentHashMap散列桶数组结构示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ConcurrentHashMap-01.png) +![ConcurrentHashMap散列桶数组结构示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ConcurrentHashMap-01.png) 如图(图片摘自网络),ConcurrentHashMap是一个哈希桶数组,如果不出现哈希冲突的时候,每个元素均匀的分布在哈希桶数组中。当出现哈希冲突的时候,是**标准的链地址的解决方式**,将hash值相同的节点构成链表的形式,称为“拉链法”,另外,在1.8版本中为了防止拉链过长,当链表的长度大于8的时候会将链表转换成红黑树。 @@ -674,7 +674,7 @@ private final void transfer(Node[] tab, Node[] nextTab) { 3. 如果这个位置是TreeBin节点(fh<0),也做一个反序处理,并且判断是否需要untreefi,把处理的结果分别放在nextTable的i和i+n的位置上 4. 遍历过所有的节点以后就完成了复制工作,这时让nextTable作为新的table,并且更新sizeCtl为新容量的0.75倍 ,完成扩容。设置为新容量的0.75倍代码为 `sizeCtl = (n << 1) - (n >>> 1)`,仔细体会下是不是很巧妙,n<<1相当于n右移一位表示n的两倍即2n,n>>>1左右一位相当于n除以2即0.5n,然后两者相减为2n-0.5n=1.5n,是不是刚好等于新容量的0.75倍即2n*0.75=1.5n。最后用一个示意图来进行总结(图片摘自网络): -![ConcurrentHashMap扩容示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ConcurrentHashMap-02.png) +![ConcurrentHashMap扩容示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ConcurrentHashMap-02.png) diff --git a/docs/thread/ConcurrentLinkedQueue.md b/docs/thread/ConcurrentLinkedQueue.md index 62a95eafe..7ac7325c2 100644 --- a/docs/thread/ConcurrentLinkedQueue.md +++ b/docs/thread/ConcurrentLinkedQueue.md @@ -44,7 +44,7 @@ head和tail指针会指向一个item域为null的节点,此时ConcurrentLinkedQu -![ConcurrentLinkedQueue初始化状态](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ConcurrentLinkedQueue-01.png) +![ConcurrentLinkedQueue初始化状态](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ConcurrentLinkedQueue-01.png) @@ -139,7 +139,7 @@ public boolean offer(E e) { CAS操作成功走到第8行,此时p==t,if判断为false,直接return true返回。如果成功插入1的话,此时ConcurrentLinkedQueue的状态如下图所示: -![offer 1后队列的状态](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ConcurrentLinkedQueue-02.png) +![offer 1后队列的状态](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ConcurrentLinkedQueue-02.png) 如图,此时队列的尾节点应该为Node1,而tail指向的节点依然还是Node0,因此可以说明tail是延迟更新的。那么我们继续来看offer 2的时候的情况,很显然此时第4行q指向的节点不为null了,而是指向Node1,第5行if判断为false,第11行if判断为false,代码会走到第13行。 @@ -155,12 +155,12 @@ p = (p != t && t != (t = tail)) ? t : q; 在第一次循环中指针p指向了队列真正的队尾节点Node1,那么在下一次循环中第4行q指向的节点为null,那么在第5行中if判断为true,那么在第7行依然通过casNext方法设置p节点的next为当前新增的Node,接下来走到第8行,这个时候p!=t,第8行if判断为true,会通过`casTail(t, newNode)`将当前节点Node设置为队列的队尾节点,此时的队列状态示意图如下图所示: -![队列offer 2后的状态](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ConcurrentLinkedQueue-03.png) +![队列offer 2后的状态](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ConcurrentLinkedQueue-03.png) **tail指向的节点由Node0改变为Node2**,这里的casTail失败不需要重试的原因是,offer代码中主要是通过p的next节点q(`Node q = p.next`)决定后面的逻辑走向的,当casTail失败时状态示意图如下: -![队列进行入队操作后casTail失败后的状态图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ConcurrentLinkedQueue-04.png) +![队列进行入队操作后casTail失败后的状态图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ConcurrentLinkedQueue-04.png) 如图,**如果这里casTail设置tail失败即tail还是指向Node0节点的话,无非就是多循环几次通过13行代码定位到队尾节点**。 @@ -180,7 +180,7 @@ p = (p != t && t != (t = tail)) ? t : q; 很显然这么写另有深意,其实在**多线程环境**下这行代码很有意思的。 `t != (t = tail)`这个操作**并非一个原子操作**,有这样一种情况: -![线程A和线程B有可能的执行时序](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ConcurrentLinkedQueue-05.png) +![线程A和线程B有可能的执行时序](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ConcurrentLinkedQueue-05.png) 如图,假设线程A此时读取了变量t,线程B刚好在这个时候offer一个Node后,此时会修改tail指针,那么这个时候线程A再次执行t=tail时t会指向另外一个节点,很显然线程A前后两次读取的变量t指向的节点不相同,即`t != (t = tail)`为true,并且由于t指向节点的变化`p != t`也为true,此时该行代码的执行结果为p和t最新的t指针指向了同一个节点,并且此时t也是队列真正的对尾节点。那么,现在已经定位到队列真正的队尾节点,就可以执行offer操作了。 @@ -223,14 +223,14 @@ public E poll() { 我们还是先站在**单线程的角度**去理清该方法的基本逻辑。假设ConcurrentLinkedQueue初始状态如下图所示: -![队列初始状态](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ConcurrentLinkedQueue-06.png) +![队列初始状态](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ConcurrentLinkedQueue-06.png) 参数offer时的定义,我们还是先将**变量p作为队列要删除真正的队头节点,h(head)指向的节点并不一定是队列的队头节点**。先来看poll出Node1时的情况,由于`p=h=head`,参照上图,很显然此时p指向的Node1的数据域不为null,在第4行代码中`item!=null`判断为true后接下来通过`casItem`将Node1的数据域设置为null。如果CAS设置失败则此次循环结束等待下一次循环进行重试。若第4行执行成功进入到第5行代码,此时p和h都指向Node1,第5行if判断为false,然后直接到第7行return回Node1的数据域1,方法运行结束,此时的队列状态如下图。 -![队列出队操作后的状态](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ConcurrentLinkedQueue-07.png) +![队列出队操作后的状态](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ConcurrentLinkedQueue-07.png) 下面继续从队列中poll,很显然当前h和p指向的Node1的数据域为null,那么第一件事就是要**定位准备删除的队头节点(找到数据域不为null的节点)**。 @@ -242,7 +242,7 @@ public E poll() { -![经过一次循环后的状态](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ConcurrentLinkedQueue-08.png) +![经过一次循环后的状态](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ConcurrentLinkedQueue-08.png) 进行下一次循环,第4行的操作同上述,当前假设第4行中casItem设置成功,由于p已经指向了Node2,而h还依旧指向Node1,此时第5行的if判断为true,然后执行`updateHead(h, ((q = p.next) != null) ? q : p)`,此时q指向的Node3,所有传入updateHead方法的分别是指向Node1的h引用和指向Node3的q引用。updateHead方法的源码为: @@ -256,7 +256,7 @@ final void updateHead(Node h, Node p) { 该方法主要是通过`casHead`将队列的head指向Node3,并且通过 `h.lazySetNext`将Node1的next域指向它自己。最后在第7行代码中返回Node2的值。此时队列的状态如下图所示: -![Node2从队列中出队后的状态](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ConcurrentLinkedQueue-09.png) +![Node2从队列中出队后的状态](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ConcurrentLinkedQueue-09.png) @@ -316,15 +316,15 @@ queue当前是否为空队列:false 在offer方法的第11行代码`if (p == q)`,能够让if判断为true的情况为p指向的节点为**哨兵节点**,而什么时候会构造哨兵节点呢?在对poll方法的讨论中,我们已经找到了答案,即**当head指向的节点的item域为null时会寻找真正的队头节点,等到待插入的节点插入之后,会更新head,并且将原来head指向的节点设置为哨兵节点。**假设队列初始状态如下图所示: -![offer和poll相互影响分析时队列初始状态.png](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ConcurrentLinkedQueue-10.png) +![offer和poll相互影响分析时队列初始状态.png](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ConcurrentLinkedQueue-10.png) 因此在线程A执行offer时,线程B执行poll就会存在如下一种情况: -![线程A和线程B可能存在的执行时序](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ConcurrentLinkedQueue-11.png) +![线程A和线程B可能存在的执行时序](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ConcurrentLinkedQueue-11.png) 如图,线程A的tail节点存在next节点Node1,因此会通过引用q往前寻找队列真正的队尾节点,当执行到判断`if (p == q)`时,此时线程B执行poll操作,在对线程B来说,head和p指向Node0,由于Node0的item域为null,同样会往前递进找到队列真正的队头节点Node1,在线程B执行完poll之后,Node0就会转换为**哨兵节点**,也就意味着队列的head发生了改变,此时队列状态为下图。 -![线程B进行poll后队列的状态图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ConcurrentLinkedQueue-12.png) +![线程B进行poll后队列的状态图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ConcurrentLinkedQueue-12.png) 此时线程A在执行判断`if (p == q)`时就为true,会继续执行` p = (t != (t = tail)) ? t : head;`,由于tail指针没有发生改变所以p被赋值为head,重新从head开始完成插入操作。 diff --git a/docs/thread/CopyOnWriteArrayList.md b/docs/thread/CopyOnWriteArrayList.md index 8dec40fd1..6dab08105 100644 --- a/docs/thread/CopyOnWriteArrayList.md +++ b/docs/thread/CopyOnWriteArrayList.md @@ -127,7 +127,7 @@ add方法的逻辑也比较容易理解,请看上面的注释。需要注意 假设COW的变化如下图所示: -![最终一致性的分析](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/CopyOnWriteArrayList-01.png) +![最终一致性的分析](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/CopyOnWriteArrayList-01.png) 数组中已有数据1,2,3,现在写线程想往数组中添加数据4,我们在第5行处打上断点,让写线程暂停。读线程依然会“不受影响”的能从数组中读取数据,可是还是只能读到1,2,3。**如果读线程能够立即读到新添加的数据的话就叫做能保证数据实时性**。当对第5行的断点放开后,读线程才能感知到数据变化,读到完整的数据1,2,3,4,而保证**数据最终一致性**,尽管有可能中间间隔了好几秒才感知到。 diff --git a/docs/thread/ReentrantReadWriteLock.md b/docs/thread/ReentrantReadWriteLock.md index b529f550b..6905c85c0 100644 --- a/docs/thread/ReentrantReadWriteLock.md +++ b/docs/thread/ReentrantReadWriteLock.md @@ -85,7 +85,7 @@ protected final boolean tryAcquire(int acquires) { 该方法是获取读锁被获取的次数,是将同步状态(int c)右移16次,即取同步状态的高16位,现在我们可以得出另外一个结论**同步状态的高16位用来表示读锁被获取的次数**。现在还记得我们开篇说的需要弄懂的第一个问题吗?读写锁是怎样实现分别记录读锁和写锁的状态的,现在这个问题的答案就已经被我们弄清楚了,其示意图如下图所示: -![读写锁的读写状态设计](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ReentrantReadWriteLock-f714bdd6-917a-4d25-ac11-7e85b0ec1b14.png) +![读写锁的读写状态设计](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ReentrantReadWriteLock-f714bdd6-917a-4d25-ac11-7e85b0ec1b14.png) 现在我们回过头来看写锁获取方法tryAcquire,其主要逻辑为:**当读锁已经被读线程获取或者写锁已经被其他写线程获取,则写锁获取失败;否则,获取成功并支持重入,增加写状态。** diff --git a/docs/thread/ScheduledThreadPoolExecutor.md b/docs/thread/ScheduledThreadPoolExecutor.md index 297346696..977e74118 100644 --- a/docs/thread/ScheduledThreadPoolExecutor.md +++ b/docs/thread/ScheduledThreadPoolExecutor.md @@ -148,7 +148,7 @@ public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) 我们先看看里面涉及到的几个类和接口`ScheduledFuture`、 `RunnableScheduledFuture`、 `ScheduledFutureTask`的关系: -![类图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ScheduledThreadPoolExecutor-cd4cead8-2ce3-4460-8ea3-9534cd4925f2.jpg) +![类图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ScheduledThreadPoolExecutor-cd4cead8-2ce3-4460-8ea3-9534cd4925f2.jpg) 我们先看看这几个接口和类: diff --git a/docs/thread/ThreadLocal.md b/docs/thread/ThreadLocal.md index d4586f34d..5f4b7ca19 100644 --- a/docs/thread/ThreadLocal.md +++ b/docs/thread/ThreadLocal.md @@ -180,7 +180,7 @@ Entry是一个以ThreadLocal为key,Object为value的键值对,另外需要注 到这里我们可以用一个图来理解下thread,threadLocal,threadLocalMap,Entry之间的关系: -![ThreadLocal各引用间的关系](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ThreadLocal-01.png) +![ThreadLocal各引用间的关系](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ThreadLocal-01.png) 注意上图中的实线表示强引用,虚线表示弱引用。如图所示,每个线程实例中可以通过threadLocals获取到threadLocalMap,而threadLocalMap实际上就是一个以threadLocal实例为key,任意对象为value的Entry数组。 @@ -200,7 +200,7 @@ Entry是一个以ThreadLocal为key,Object为value的键值对,另外需要注 理想状态下,散列表就是一个包含关键字的固定大小的数组,通过使用散列函数,将关键字映射到数组的不同位置。下面是 -![理想散列表的一个示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ThreadLocal-02.png) +![理想散列表的一个示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ThreadLocal-02.png) 在理想状态下,哈希函数可以将关键字均匀的分散到数组的不同位置,不会出现两个关键字散列值相同(假设关键字数量小于数组的大小)的情况。 @@ -212,7 +212,7 @@ Entry是一个以ThreadLocal为key,Object为value的键值对,另外需要注 分散链表法使用链表解决冲突,将散列值相同的元素都保存到一个链表中。当查询的时候,首先找到元素所在的链表,然后遍历链表查找对应的元素,典型实现为hashMap,concurrentHashMap的拉链法。下面是一个示意图: -![分离链表法示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ThreadLocal-02.gif) +![分离链表法示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ThreadLocal-02.gif) ##### 开放定址法 @@ -220,7 +220,7 @@ Entry是一个以ThreadLocal为key,Object为value的键值对,另外需要注 探测数组空单元的方式有很多,这里介绍一种最简单的 -- 线性探测法。线性探测法就是从冲突的数组单元开始,依次往后搜索空单元,如果到数组尾部,再从头开始搜索(环形查找)。如下图所示: -![开放定址法示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ThreadLocal-03.jpg) +![开放定址法示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ThreadLocal-03.jpg) 关于两种方式的比较,可以参考 [这篇文章](http://www.nowamagic.net/academy/detail/3008060)。 diff --git a/docs/thread/ali-executors.md b/docs/thread/ali-executors.md index 7a29d2a71..ed36fa32d 100644 --- a/docs/thread/ali-executors.md +++ b/docs/thread/ali-executors.md @@ -9,7 +9,7 @@ tag: 看阿里巴巴开发手册并发编程这块有一条:**线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式**,今天我们来通过源码分析一下禁用的原因。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ali-executors-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ali-executors-1.png) @@ -70,7 +70,7 @@ public ThreadPoolExecutor(int corePoolSize, 线程池执行任务逻辑和线程池参数的关系。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ali-executors-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ali-executors-2.png) 执行逻辑说明: @@ -181,7 +181,7 @@ public class TaskTest { 在启动测试类之前先将 JVM 内存调整小一点,不然很容易将电脑跑出问题【别问我为什么知道,是铁憨憨没错了!!!】,在 idea 里:Run -> Edit Configurations。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/ali-executors-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/ali-executors-3.png) JVM 参数说明: diff --git a/docs/thread/cas.md b/docs/thread/cas.md index f838feb78..5a61795e0 100644 --- a/docs/thread/cas.md +++ b/docs/thread/cas.md @@ -14,7 +14,7 @@ tag: `synchronized`是悲观锁,线程开始执行第一步就是获取锁,一旦获得锁,其他的线程进入后就会阻塞等待锁。如果不好理解,举个生活中的例子:一个人进入厕所后首先把门锁上(获取锁),然后开始上厕所,这个时候有其他人来了只能在外面等(阻塞),就算再急也没用。上完厕所完事后把门打开(解锁),其他人就可以进入了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/cas-973e8804-c713-43f6-9a63-4b9f2be54f10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/cas-973e8804-c713-43f6-9a63-4b9f2be54f10.png) `CAS`是乐观锁,线程执行的时候不会加锁,假设没有冲突去完成某项操作,如果因为冲突失败了就重试,最后直到成功为止。 @@ -90,7 +90,7 @@ Linux的X86下主要是通过`cmpxchgl`这个指令在CPU级完成CAS操作的 JDK提供了一些用于原子操作的类,在`java.util.concurrent.atomic`包下面。在JDK 11中,有如下17个类: -![原子类](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/cas-f6a2281a-d322-4022-8c07-162ccc9dcede.jpg) +![原子类](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/cas-f6a2281a-d322-4022-8c07-162ccc9dcede.jpg) 从名字就可以看得出来这些类大概的用途: diff --git a/docs/thread/condition.md b/docs/thread/condition.md index b691c7ebd..1266b5dea 100644 --- a/docs/thread/condition.md +++ b/docs/thread/condition.md @@ -92,7 +92,7 @@ public static void main(String[] args) { ``` 这段代码没有任何实际意义,甚至很臭,只是想说明下我们刚才所想的。新建了10个线程,没有线程先获取锁,然后调用condition.await方法释放锁将当前线程加入到等待队列中,通过debug控制当走到第10个线程的时候查看`firstWaiter`即等待队列中的头结点,debug模式下情景图如下: -![debug模式下情景图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/condition-01.png) +![debug模式下情景图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/condition-01.png) @@ -101,7 +101,7 @@ public static void main(String[] args) { 1. 调用condition.await方法后线程依次尾插入到等待队列中,如图队列中的线程引用依次为Thread-0,Thread-1,Thread-2....Thread-8; 2. 等待队列是一个单向队列。通过我们的猜想然后进行实验验证,我们可以得出等待队列的示意图如下图所示: -![等待队列的示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/condition-02.png) +![等待队列的示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/condition-02.png) 同时还有一点需要注意的是:我们可以多次调用`lock.newCondition()`方法创建多个condition对象,也就是一个lock可以持有多个等待队列。 @@ -110,7 +110,7 @@ public static void main(String[] args) { -![AQS持有多个Condition](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/condition-03.png) +![AQS持有多个Condition](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/condition-03.png) @@ -225,7 +225,7 @@ while (!isOnSyncQueue(node)) { 到目前为止,开头的三个问题我们通过阅读源码的方式已经完全找到了答案,也对await方法的理解加深。await方法示意图如下图: -![await方法示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/condition-04.png) +![await方法示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/condition-04.png) @@ -313,7 +313,7 @@ final boolean transferForSignal(Node node) { **调用condition的signal的前提条件是当前线程已经获取了lock,该方法会使得等待队列中的头节点即等待时间最长的那个节点移入到同步队列,而移入到同步队列后才有机会使得等待线程被唤醒,即从await方法中的LockSupport.park(this)方法中返回,从而才有机会使得调用await方法的线程成功退出**。signal执行示意图如下图: -![signal执行示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/condition-05.png) +![signal执行示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/condition-05.png) @@ -338,7 +338,7 @@ private void doSignalAll(Node first) { 文章开篇提到等待/通知机制,通过使用condition提供的await和signal/signalAll方法就可以实现这种机制,而这种机制能够解决最经典的问题就是“生产者与消费者问题”,关于“生产者消费者问题”之后会用单独的一篇文章进行讲解,这也是面试的高频考点。await和signal和signalAll方法就像一个开关控制着线程A(等待方)和线程B(通知方)。它们之间的关系可以用下面一个图来表现得更加贴切: -![condition下的等待通知机制.png](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/condition-06.png) +![condition下的等待通知机制.png](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/condition-06.png) 如图,**线程awaitThread先通过lock.lock()方法获取锁成功后调用了condition.await方法进入等待队列,而另一个线程signalThread通过lock.lock()方法获取锁成功后调用了condition.signal或者signalAll方法,使得线程awaitThread能够有机会移入到同步队列中,当其他线程释放lock后使得线程awaitThread能够有机会获取lock,从而使得线程awaitThread能够从await方法中退出执行后续操作。如果awaitThread获取lock失败会直接进入到同步队列**。 diff --git a/docs/thread/fork-join.md b/docs/thread/fork-join.md index b817c9ac0..88ddae613 100644 --- a/docs/thread/fork-join.md +++ b/docs/thread/fork-join.md @@ -19,7 +19,7 @@ Fork/Join框架是一个实现了ExecutorService接口的多线程处理器, Fork/Join的运行流程大致如下所示: -![fork/join流程图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/fork-join-ba0c0e3f-dc9b-445d-874a-5878503a98f7.png) +![fork/join流程图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/fork-join-ba0c0e3f-dc9b-445d-874a-5878503a98f7.png) 需要注意的是,图里的次级子任务可以一直分下去,一直分到子任务足够小为止。用伪代码来表示如下: @@ -42,7 +42,7 @@ solve(任务): 工作窃取流程如下图所示: -![工作窃取算法流程](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/fork-join-819f4ad9-25ce-4e7e-a1d7-e36a70e584a4.png) +![工作窃取算法流程](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/fork-join-819f4ad9-25ce-4e7e-a1d7-e36a70e584a4.png) 值得注意的是,当一个线程窃取另一个线程的时候,为了减少两个任务线程之间的竞争,我们通常使用**双端队列**来存储任务。被窃取的任务线程都从双端队列的**头部**拿任务执行,而窃取其他任务的线程从双端队列的**尾部**执行任务。 @@ -115,7 +115,7 @@ private int doJoin() { ``` 我们在之前介绍过说Thread.join()会使线程阻塞,而ForkJoinPool.join()会使线程免于阻塞,下面是ForkJoinPool.join()的流程图: -![join流程图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/fork-join-8e03485d-efe0-4edf-8516-a9b10dea6e7f.png) +![join流程图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/fork-join-8e03485d-efe0-4edf-8516-a9b10dea6e7f.png) **RecursiveAction和RecursiveTask** diff --git a/docs/thread/jmm.md b/docs/thread/jmm.md index 056923e88..d5edfa229 100644 --- a/docs/thread/jmm.md +++ b/docs/thread/jmm.md @@ -28,14 +28,14 @@ tag: 这两种模型之间的区别如下表所示: -![两种并发模型的比较](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/jmm-a610752d-ef73-47f2-b02c-6954eb3d62bf.png) +![两种并发模型的比较](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/jmm-a610752d-ef73-47f2-b02c-6954eb3d62bf.png) **在Java中,使用的是共享内存并发模型**。 ## Java内存模型的抽象结构 ### 运行时内存的划分 先谈一下运行时数据区,下面这张图相信大家一点都不陌生: -![Java运行时数据区域](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/jmm-0b9e4b1e-90e2-41bb-be89-f65e3a10fa08.png) +![Java运行时数据区域](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/jmm-0b9e4b1e-90e2-41bb-be89-f65e3a10fa08.png) 对于每一个线程来说,栈都是私有的,而堆是共有的。 @@ -50,7 +50,7 @@ tag: Java线程之间的通信由Java内存模型(简称JMM)控制,从抽象的角度来说,JMM定义了线程和主内存之间的抽象关系。JMM的抽象示意图如图所示: -![JMM抽象示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/jmm-f02219aa-e762-4df0-ac08-6f4cceb535c2.jpg) +![JMM抽象示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/jmm-f02219aa-e762-4df0-ac08-6f4cceb535c2.jpg) 从图中可以看出: 1. 所有的共享变量都存在主内存中。 @@ -150,13 +150,13 @@ Java内存模型(JMM)对于正确同步多线程程序的内存一致性做 假设**正确使用了同步**,A线程的3个操作执行后释放锁,B线程获取同一个锁。那么在**顺序一致性模型**中的执行效果如下所示: -![正确同步图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/jmm-9ce5973e-6100-41e6-96b8-29ddb738e7f8.png) +![正确同步图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/jmm-9ce5973e-6100-41e6-96b8-29ddb738e7f8.png) 操作的执行整体上有序,并且两个线程都只能看到这个执行顺序。 假设**没有使用同步**,那么在**顺序一致性模型**中的执行效果如下所示: -![没有正确同步图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/jmm-6357c025-a6e0-4c89-939d-040e549fac12.png) +![没有正确同步图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/jmm-6357c025-a6e0-4c89-939d-040e549fac12.png) 操作的执行整体上无序,但是两个线程都只能看到这个执行顺序。之所以可以得到这个保证,是因为顺序一致性模型中的**每个操作必须立即对任意线程可见**。 diff --git a/docs/thread/map.md b/docs/thread/map.md index 3cdac2f03..03938a833 100644 --- a/docs/thread/map.md +++ b/docs/thread/map.md @@ -58,7 +58,7 @@ public class TestVector { 整体架构(列举常用的容器类) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/map-a6a020a3-4573-4cf8-b5ae-1541ae45801c.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/map-a6a020a3-4573-4cf8-b5ae-1541ae45801c.png) 下面分别介绍一些常用的并发容器类和接口,因篇幅原因,这里只介绍这些类的用途和基本的原理,不做过多的源码解析。 @@ -111,7 +111,7 @@ ConcurrentHashMap在JDK 1.7中,提供了一种粒度更细的加锁机制来 有些方法需要跨段,比如size()、isEmpty()、containsValue(),它们可能需要锁定整个表而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁。如下图: -![分段锁机制](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/map-3d991ca1-5d58-465c-b097-ffaf6c31f3bc.png) +![分段锁机制](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/map-3d991ca1-5d58-465c-b097-ffaf6c31f3bc.png) ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。Segment是一种可重入锁ReentrantLock,HashEntry则用于存储键值对数据。 diff --git a/docs/thread/pool.md b/docs/thread/pool.md index 34d974f18..82b773964 100644 --- a/docs/thread/pool.md +++ b/docs/thread/pool.md @@ -225,7 +225,7 @@ public void execute(Runnable command) { 整个过程如图所示: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/pool-f9e419fa-9d42-44fc-b14e-5b618a6f906d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/pool-f9e419fa-9d42-44fc-b14e-5b618a6f906d.png) ### ThreadPoolExecutor如何做到线程复用的? diff --git a/docs/thread/synchronized.md b/docs/thread/synchronized.md index 49a9aa636..8a376eb27 100644 --- a/docs/thread/synchronized.md +++ b/docs/thread/synchronized.md @@ -145,7 +145,7 @@ Hotspot的作者经过以往的研究发现大多数情况下**锁不仅不存 线程竞争偏向锁的过程如下: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/synchronized-c223913c-6c10-4dd0-849c-5e8f981cba48.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/synchronized-c223913c-6c10-4dd0-849c-5e8f981cba48.jpg) 图中涉及到了lock record指针指向当前堆栈中的最近一个lock record,是轻量级锁按照先来先服务的模式进行了轻量级锁的加锁。 @@ -167,7 +167,7 @@ Hotspot的作者经过以往的研究发现大多数情况下**锁不仅不存 下面这个经典的图总结了偏向锁的获得和撤销: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/synchronized-23a5729f-71d5-44ce-af19-72ee20ae753e.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/synchronized-23a5729f-71d5-44ce-af19-72ee20ae753e.png) ### 轻量级锁 @@ -193,7 +193,7 @@ JVM会为每个线程在当前线程的栈帧中创建用于存储锁记录的 一张图说明加锁和释放锁的过程: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/synchronized-1bb5f956-d3da-4e7b-b426-1d5d9314fe5b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/synchronized-1bb5f956-d3da-4e7b-b426-1d5d9314fe5b.png) ### 重量级锁 diff --git a/docs/thread/thread-bring-some-problem.md b/docs/thread/thread-bring-some-problem.md index 2dfad10da..564e325ca 100644 --- a/docs/thread/thread-bring-some-problem.md +++ b/docs/thread/thread-bring-some-problem.md @@ -14,15 +14,15 @@ tag: 在一个单向行驶的道路上,每辆汽车都遵守交通规则,这时候整体通行是正常的。『单向车道』意味着『一个线程』,『多辆车』意味着『多个job任务』。 -![单线程顺利同行](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/thread-bring-some-problem-c0a03b79-36d8-4120-888e-0597aa66ca5b.png) +![单线程顺利同行](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/thread-bring-some-problem-c0a03b79-36d8-4120-888e-0597aa66ca5b.png) 如果需要提升车辆的同行效率,一般的做法就是扩展车道,对应程序来说就是『加线程池』,增加线程数。这样在同一时间内,通行的车辆数远远大于单车道。 -![多线程顺利同行](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/thread-bring-some-problem-a65346bc-7b8b-4883-9d85-d07859df2e69.png) +![多线程顺利同行](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/thread-bring-some-problem-a65346bc-7b8b-4883-9d85-d07859df2e69.png) 然而成年人的世界没有那么完美,车道一旦多起来『加塞』的场景就会越来越多,出现碰撞后也会影响整条马路的通行效率。这么一对比下来『多车道』确实可能比『单车道』要慢。 -![多线程故障](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/thread-bring-some-problem-532930da-03fe-4a59-aee8-0b97b5f1a966.png) +![多线程故障](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/thread-bring-some-problem-532930da-03fe-4a59-aee8-0b97b5f1a966.png) 防止汽车频繁变道加塞可以采取在车道间增加『护栏』,那在程序的世界该怎么做呢? @@ -36,11 +36,11 @@ tag: 举一个银行转账的例子,比如从账户A向账户B转1000元,那么必然包括2个操作:从账户A减去1000元,往账户B加上1000元,两个操作都成功才意味着一次转账最终成功。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/thread-bring-some-problem-eba43c92-e42d-4318-a40c-b9365c32d922.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/thread-bring-some-problem-eba43c92-e42d-4318-a40c-b9365c32d922.png) 试想一下,如果这两个操作不具备原子性,从A的账户扣减了1000元之后,操作突然终止了,账户B没有增加1000元,那问题就大了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/thread-bring-some-problem-c22ae9be-bd80-4613-9c7e-3feb83c6c83f.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/thread-bring-some-problem-c22ae9be-bd80-4613-9c7e-3feb83c6c83f.png) 银行转账这个例子有两个步骤,出现了意外后导致转账失败,说明没有原子性。 @@ -99,7 +99,7 @@ class Test { > 可见性:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/thread-bring-some-problem-d91ca0c2-4f39-4e98-90e2-8acb793eb983.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/thread-bring-some-problem-d91ca0c2-4f39-4e98-90e2-8acb793eb983.png) 如上图每个线程都有属于自己的工作内存,工作内存和主内存间需要通过store和load等进行交互。 @@ -123,7 +123,7 @@ class Test { 死锁是指多个线程因为环形的等待锁的关系而永远的阻塞下去。一图胜千语,不多解释。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/thread-bring-some-problem-d4e65d5f-3de1-4a1c-8ae1-02cb3bfb528c.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/thread-bring-some-problem-d4e65d5f-3de1-4a1c-8ae1-02cb3bfb528c.png) **(2)活锁** @@ -131,7 +131,7 @@ class Test { 当多个线程都在运行并且修改各自的状态,而其他线程彼此依赖这个状态,导致任何一个线程都无法继续执行,只能重复着自身的动作和修改自身的状态,这种场景就是发生了活锁。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/thread-bring-some-problem-d1f9e916-0985-46fe-bf87-63fccfd27bae.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/thread-bring-some-problem-d1f9e916-0985-46fe-bf87-63fccfd27bae.png) 如果大家还有疑惑,那我再举一个生活中的例子,大家平时在走路的时候,迎面走来一个人,两个人互相让路,但是又同时走到了一个方向,如果一直这样重复着避让,这俩人就是发生了活锁,学到了吧,嘿嘿。 @@ -146,7 +146,7 @@ class Test { 有一个非常经典的饥饿问题就是`哲学家用餐问题`,如下图所示,有五个哲学家在用餐,每个人必须要同时拿两把叉子才可以开始就餐,如果哲学家1和哲学家3同时开始就餐,那哲学家2、4、5就得饿肚子等待了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/thread-bring-some-problem-314a47df-c953-4b7d-831c-007173981819.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/thread-bring-some-problem-314a47df-c953-4b7d-831c-007173981819.png) @@ -158,7 +158,7 @@ class Test { 线程创建完之后,还会遇到线程`上下文切换`。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/thread-bring-some-problem-d125d0b9-3b60-46cd-a79f-a26dd5210b44.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/thread-bring-some-problem-d125d0b9-3b60-46cd-a79f-a26dd5210b44.png) CPU是很宝贵的资源速度也非常快,为了保证雨露均沾,通常为给不同的线程分配`时间片`,当CPU从执行一个线程切换到执行另一个线程时,CPU 需要保存当前线程的本地数据,程序指针等状态,并加载下一个要执行的线程的本地数据,程序指针等,这个开关被称为『上下文切换』。 @@ -175,7 +175,7 @@ CPU是很宝贵的资源速度也非常快,为了保证雨露均沾,通常 用一张图总结一下上面讲的: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/thread-bring-some-problem-119223c9-83a9-42e1-9a0c-f9c706a1e793.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/thread-bring-some-problem-119223c9-83a9-42e1-9a0c-f9c706a1e793.png) --- diff --git a/docs/thread/thread-state-and-method.md b/docs/thread/thread-state-and-method.md index 603dcd958..706ddcace 100644 --- a/docs/thread/thread-state-and-method.md +++ b/docs/thread/thread-state-and-method.md @@ -14,7 +14,7 @@ tag: > 在现在的操作系统中,线程是被视为轻量级进程的,所以**操作系统线程的状态其实和操作系统进程的状态是一致的**。 -![系统进程/线程转换图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/thread-state-and-method-f60caaad-ad47-4edc-8d0a-ab736c2e8500.png) +![系统进程/线程转换图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/thread-state-and-method-f60caaad-ad47-4edc-8d0a-ab736c2e8500.png) 操作系统线程主要有以下三个状态: @@ -193,7 +193,7 @@ public static State toThreadState(int var0) { ## 线程状态的转换 根据上面关于线程状态的介绍我们可以得到下面的**线程状态转换图**: -![线程状态转换图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/thread-state-and-method-18f0d338-1c19-4e18-a0cc-62e97fc39272.png) +![线程状态转换图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/thread-state-and-method-18f0d338-1c19-4e18-a0cc-62e97fc39272.png) ### BLOCKED与RUNNABLE状态的转换 我们在上面说到:处于BLOCKED状态的线程是因为在等待锁的释放。假如这里有两个线程a和b,a线程提前获得了锁并且暂未释放锁,此时b就处于BLOCKED状态。我们先来看一个例子: diff --git a/docs/thread/volatile.md b/docs/thread/volatile.md index d79cdcd91..7eca38ee0 100644 --- a/docs/thread/volatile.md +++ b/docs/thread/volatile.md @@ -63,7 +63,7 @@ public class VolatileExample { 假设在时间线上,线程A先执行方法`writer`方法,线程B后执行`reader`方法。那必然会有下图: -![volatile内存示意图](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/volatile-1f5e263e-dd3e-4fb9-a21f-67e160b3dbf2.jpg) +![volatile内存示意图](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/volatile-1f5e263e-dd3e-4fb9-a21f-67e160b3dbf2.jpg) 而如果`flag`变量**没有**用`volatile`修饰,在step 2,线程A的本地内存里面的变量就不会立即更新到主内存,那随后线程B也同样不会去主内存拿最新的值,仍然使用线程B本地内存缓存的变量的值`a = 0,flag = false`。 @@ -98,7 +98,7 @@ public class VolatileExample { 大概示意图是这个样子: -![内存屏障](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/volatile-aaa0fa44-341f-401f-bfb7-03d0c03dc2b1.png) +![内存屏障](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/volatile-aaa0fa44-341f-401f-bfb7-03d0c03dc2b1.png) > 再逐个解释一下这几个屏障。注:下述Load代表读操作,Store代表写操作 > diff --git a/docs/thread/wangzhe-thread.md b/docs/thread/wangzhe-thread.md index 2e726b82c..1503526ae 100644 --- a/docs/thread/wangzhe-thread.md +++ b/docs/thread/wangzhe-thread.md @@ -75,7 +75,7 @@ t3.start(); 来看一下执行后的结果: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/wangzhe-thread-01.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/wangzhe-thread-01.png) ♠②:创建一个类实现Runnable接口,并重写run方法。 @@ -113,7 +113,7 @@ t3.start(); 来看一下执行后的结果: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/wangzhe-thread-02.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/wangzhe-thread-02.png) **❤1、为什么要重写run方法?** @@ -169,7 +169,7 @@ t3.start(); 来看一下执行后的结果: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/wangzhe-thread-03.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/wangzhe-thread-03.png) 3)`setDaemon()`:将此线程标记为守护线程,准确来说,就是服务其他的线程,像 Java 中的垃圾回收线程,就是典型的守护线程。 @@ -195,6 +195,6 @@ t3.start(); 最后再来看一下线程的生命周期吧,一图胜千言。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/thread/wangzhe-thread-04.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/thread/wangzhe-thread-04.png) diff --git a/docs/xianliaolaoke/aliyun-shuaiguo-gongchengshi.md b/docs/xianliaolaoke/aliyun-shuaiguo-gongchengshi.md index b695c3d35..a154ef4e8 100644 --- a/docs/xianliaolaoke/aliyun-shuaiguo-gongchengshi.md +++ b/docs/xianliaolaoke/aliyun-shuaiguo-gongchengshi.md @@ -2,17 +2,17 @@ 给大家看一下我们的第一波交锋。从在线沟通转工单的时候,为了给这位阿里云的售后工程师加油打气,我还特意准备了一句“辛苦了”,就差跪下了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/aliyun-shuaiguo-gongchengshi-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/aliyun-shuaiguo-gongchengshi-1.png) 结果等到的是阿里云售后工程师的一句:“我打开看了一下,这个图片不是您给的这个资源啊”。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/aliyun-shuaiguo-gongchengshi-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/aliyun-shuaiguo-gongchengshi-2.png) 我当时心里暗想,好家伙,果然甩锅小能手啊! 然后我就耐心地给他解释,GitHub 会对图片转链,另外,用 HTTP 的时候是可以显示的,只有 CDN 启用了 HTTPS 才不显示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/aliyun-shuaiguo-gongchengshi-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/aliyun-shuaiguo-gongchengshi-3.png) 结果他回了一句,“这个我们确认不了哈,这个链接没有请求 CDN 域名,不显示也不是 CDN 返回的”。 @@ -20,11 +20,11 @@ 接着我又给他解释,我说你看看这个图片标签里还有一个 `data-canonical-src` 属性,它就来自你们阿里云的 CDN 啊,另外,我把 HTTPS 改成 HTTP 就可以访问了,别的什么都不用动。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/aliyun-shuaiguo-gongchengshi-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/aliyun-shuaiguo-gongchengshi-4.png) 我以为,这位阿里售后工程师在收到我这个确凿的证据后,会稍微研究一番,结果没想到,他马上就开始正儿八经地甩锅了,“**这个我们也确认不了,因为这个类似代理请求了,拿不到实际请求 CDN 的返回信息**”。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/aliyun-shuaiguo-gongchengshi-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/aliyun-shuaiguo-gongchengshi-5.png) 好吧,我被打败了,彻彻底底地败了,败的一塌糊涂! @@ -34,13 +34,13 @@ - 第二,你为什么能转 HTTP 的 CDN 链接,转不了 HTTPS 的呢? - 第三,你 HTTPS 也不是不能转,直接用 OSS 的 HTTPS 链接你就能转,加了 CDN 的你就不行? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/aliyun-shuaiguo-gongchengshi-6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/aliyun-shuaiguo-gongchengshi-6.png) 之前给大家提到过,二哥的小破站《Java 程序员进阶之路》的图床是用 GitHub+jsDelivr 做的,免费啊,可以白嫖啊,所以我觉得用起来很爽! 但直到有一天,有个小伙伴提了一个 issue,说 jsDelivr 撤出了国内节点,导致部分图片不显示或者加载缓慢,我就坐立不安、寝食难安了! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/aliyun-shuaiguo-gongchengshi-7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/aliyun-shuaiguo-gongchengshi-7.png) 毕竟二哥可是一名负责任的好同志啊,必须得解决这个图床的问题。于是我就折腾了两天的 **OSS + CDN**,小破站的图片是能正常访问了,只是没想到,阿里云的这套图床组合在 GitHub 上这么“不靠谱”。 @@ -48,11 +48,11 @@ 1)HTTPS 的 CDN 链接统统替换为 HTTP 的,至少能显示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/aliyun-shuaiguo-gongchengshi-8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/aliyun-shuaiguo-gongchengshi-8.png) 2)如果 HTTP 的也显示有问题,有些会只显示一部分(莫名其妙),就改成 OSS 的链接。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/aliyun-shuaiguo-gongchengshi-9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/aliyun-shuaiguo-gongchengshi-9.png) 先把问题解决了再说。 diff --git a/docs/xianliaolaoke/chadiansanhuo.md b/docs/xianliaolaoke/chadiansanhuo.md index 43827e9c7..a523c69a1 100644 --- a/docs/xianliaolaoke/chadiansanhuo.md +++ b/docs/xianliaolaoke/chadiansanhuo.md @@ -2,7 +2,7 @@ 今天和两个合伙人开了个腾讯会议,没想到差点因为一个功能上的分歧散了伙!!好在大家都是奔三的男人了,吵归吵,但都是奔着做事去的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/chadiansanhuo-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/chadiansanhuo-1.png) 不过冷静下来后,我感觉主要的责任在我,所以我打算写篇文章反思一下自己。 @@ -10,7 +10,7 @@ 这一点老读者应该能感受得到。今天经过沉痛的反思后,我决定还是做回从前的自己。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/chadiansanhuo-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/chadiansanhuo-2.png) 人不能患得患失,否则就变得谨小慎微,很多话很多事就不敢说不敢做了。 @@ -56,7 +56,7 @@ 私下里仍然有不少小伙伴问我考研和工作之间到底该如何选择,现在去微信上搜了一下“考研”的关键字,好家伙,有好多好多!!!! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/chadiansanhuo-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/chadiansanhuo-3.png) 我的观点是,根据自身的实际情况出发,你能考上研究生的几率有多大?你为什么要考研? @@ -66,7 +66,7 @@ 但其实我们就是因为一个很小很小的点,存在分歧,贴出来也不怕大家笑话,就是,我认为不应该新建一个名叫“未分类”的栏目,把没有分类的文章归类到它下面。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/chadiansanhuo-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/chadiansanhuo-4.png) 而我的合伙人认为,未分类的存在很合理,否则那些没有栏目的文章如何检索如何管理。 diff --git a/docs/xianliaolaoke/chengxuyuannv-chonghui-java.md b/docs/xianliaolaoke/chengxuyuannv-chonghui-java.md index 35083fcd0..6f54afa27 100644 --- a/docs/xianliaolaoke/chengxuyuannv-chonghui-java.md +++ b/docs/xianliaolaoke/chengxuyuannv-chonghui-java.md @@ -2,11 +2,11 @@ 大家好,我是二哥呀!前几天一个91年的小姐姐给我发私信说,她19年回老家创业开了个实体店做建材生意,但受困于疫情,生意越来越不好做,**于是就萌生重回软件开发 base 武汉**的想法,问我有什么建议。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/chengxuyuannv-chonghui-java-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/chengxuyuannv-chonghui-java-1.png) 刚好今天有空,就找她好好聊了一下,给了她一些建议,同时也给了一些我最近整理的学习资料。小姐姐说她会仔细学习的,那份决心给我的触动很大,所以我打算再来写一篇文章帮她深入地分析一下,同时也希望给有同样困惑的小伙伴一点点启发和帮助~ -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/chengxuyuannv-chonghui-java-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/chengxuyuannv-chonghui-java-2.png) ### 一、不服不行 @@ -32,11 +32,11 @@ 小姐姐 base 武汉,那我们就到 boss 直聘上看一下 Java 岗的招聘情况哈,岗位还是比较充裕的,3 到 5 年的薪资待遇在 15k-25k 之间。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/chengxuyuannv-chonghui-java-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/chengxuyuannv-chonghui-java-3.png) 再来看一下任职要求: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/chengxuyuannv-chonghui-java-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/chengxuyuannv-chonghui-java-4.png) 第一,精通 Java 语言,现在的招聘要求基本上都这样,精通是常态。针对这种“无理”的要求,办法只有一个,就是去背八股文!先背会再说。面试官问的你能答上来就表明你是精通。 diff --git a/docs/xianliaolaoke/daxue-nuli-jisuanji.md b/docs/xianliaolaoke/daxue-nuli-jisuanji.md index 8a27151ff..c90b2b566 100644 --- a/docs/xianliaolaoke/daxue-nuli-jisuanji.md +++ b/docs/xianliaolaoke/daxue-nuli-jisuanji.md @@ -4,7 +4,7 @@ 于是趁着假期连续爆肝了两周,终于肝出了这期视频:希望能给学弟学妹们一点点启发和帮助。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/daxue-nuli-jisuanji-1.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/daxue-nuli-jisuanji-1.jpg) 下面是视频链接,感兴趣的小伙伴可以跳过去给二哥刷个三连,谢谢支持了。 @@ -40,7 +40,7 @@ 可以看得出,我这个学习计划安排得非常合理,劳逸结合,不浪费一分一秒。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/daxue-nuli-jisuanji-2.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/daxue-nuli-jisuanji-2.gif) 只要你在大学一天,你就有责任、有义务把自己的大学生活过得充实,精彩,别犹豫了,卷起来吧!!!! @@ -53,7 +53,7 @@ 短暂的迷茫后我选择走出寝室,走进图书馆,没想到结识了很多优秀的学长学姐,大学最努力的同学基本上都在这里。我不厌其烦地向他们请教问题,他们都热心帮我解答,不得不承认,优秀的人从来都不吝啬于帮助他人。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/daxue-nuli-jisuanji-3.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/daxue-nuli-jisuanji-3.jpg) 我向他们请教的问题有: @@ -68,7 +68,7 @@ 刚入门的时候,最大的忌讳就是朝三暮四,这个月学 C语言,下个月学 Python,再下个月学 Java,以至于自己在入门阶段就迷茫了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/daxue-nuli-jisuanji-4.jpeg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/daxue-nuli-jisuanji-4.jpeg) 我的建议是最好专注于一门编程语言,如果想要走人工智能这条路的话,选 Python 就对了;如果想走算法岗的话,可以选择 C语言,然后再深入学习 C++,之后去把 LeetCode 刷起来;如果想本科毕业就工作赚钱的话,Java 是最优的选择,岗位多,薪资待遇也给力;以后也可以直接转go岗,如果不想那么卷的话,前端也是一个不错的选择。 @@ -79,14 +79,14 @@ 大学四年,我的整体规划是,大一时入门,大二时夯实基础,大三时拼命卷,大四时好好准备校招,能早点出去实习就早点去。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/daxue-nuli-jisuanji-5.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/daxue-nuli-jisuanji-5.jpg) 至于学习资料,网上已经有很多免费开源的了,并且绝大多数都非常的优质,只要你肯花时间去筛选和整理。 这是我自己整理的一套,偏 Java 后端路线的 PDF 书单,在GitHub上也有3。6k的star了,说明很多小伙伴都很认可,包括入门→工具→框架→数据库→并发编程→jvm→性能优化→设计模式→操作系统→计算机网络→数据结构与算法→面试→架构等等,可以说非常的全面和系统。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/daxue-nuli-jisuanji-6.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/daxue-nuli-jisuanji-6.jpg) 你也可以拿来作为参考。 @@ -96,7 +96,7 @@ 所以说我的建议是代码敲起来,笔记记起来! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/daxue-nuli-jisuanji-7.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/daxue-nuli-jisuanji-7.gif) 记住卖油翁的那句话:无他,唯手熟尔! @@ -107,7 +107,7 @@ 记得大三寒假的时候,很多同学都回家过年了,我一个人选择南下苏州去 ,本以为能找一个实习就很不错了,没想到好几个公司的面试都过了,这给了我很大的信心。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/daxue-nuli-jisuanji-8.jpeg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/daxue-nuli-jisuanji-8.jpeg) 唐代诗人刘禹锡有句诗是这样写的:**千淘万漉虽辛苦,吹尽狂沙始到金**。 diff --git a/docs/xianliaolaoke/fumutuanju.md b/docs/xianliaolaoke/fumutuanju.md index 919bcd153..1e1598467 100644 --- a/docs/xianliaolaoke/fumutuanju.md +++ b/docs/xianliaolaoke/fumutuanju.md @@ -1,6 +1,6 @@ 大家好,我是二哥呀!刚好趁着周末,就带老婆、女儿、妹妹一起从洛阳来三门峡和父母小聚了一下。洛阳到三门峡的距离,大概就 11 个小时的骑行时间,开车大概 2 个小时。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/fumutuanju-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/fumutuanju-1.png) 我老家是洛阳一个很偏远的农村,处在三门峡和洛阳的交界处,小时候看电视,收不到我们县城的信号,反而能收到隔壁县城的,不知道有多少小伙伴和我有类似的经历哈? @@ -8,7 +8,7 @@ 这是我老家院子的一个近况,每次回去,落叶都会堆积一层,仿佛就像是被时间遗忘了的角落。大概是我大学毕业去苏州参加工作后,我们全家就很少在这里住了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/fumutuanju-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/fumutuanju-2.png) 最近爆火的电视剧《人世间》里好像有这样一段旁白:“**穷人家的孩子,大抵会分为两类,一类会因为自己的成长环境而感到自卑、自怨,以至于失去自我,丧失了对未来的主动权;一类会不懈努力,尝试去改变命运,赢得这个世界里一片属于自己的天空**。” @@ -20,13 +20,13 @@ 公众号后台是能看到数据的,父亲的微信号是阅读最多的前几位。要知道,技术文章他可是看不懂的,看得懂的也就是我现在写的这种类型的小散文。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/fumutuanju-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/fumutuanju-3.png) **父亲是自己最忠实的读者之一,想想也是一件挺幸福的事情**。 我是昨天夜里开车来三门峡的,因为周六女儿要上舞蹈课和英语课,上完就晚上 7 点左右了。夜里开车非常累,因为对面车道和后面倒车镜里的远光灯非常的刺眼,但又不想请假耽误女儿的课程。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/fumutuanju-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/fumutuanju-4.png) 快到父母住处的时候,我的眼睛里扫到了一个熟悉的背影,那背影,即便是在漆黑的夜里,即便这夜里只有暗淡的月光和我那不争气的卤素车灯,我也知道,那是父亲的背影。 @@ -46,7 +46,7 @@ **真的不是我矫情,我是个念家的人。打小就喜欢待在父母身边,成家后,更是不愿意出远门**。前段时间,阿里云社区的一个负责人邀请我去做无影评测大赛颁奖典礼的嘉宾,被我各种理由推掉了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xianliaolaoke/fumutuanju-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xianliaolaoke/fumutuanju-5.png) 一想到要离开家,又是只身一人,我就好像脚下生了根,寸步难移。 diff --git a/docs/xuexijianyi/electron-information-engineering.md b/docs/xuexijianyi/electron-information-engineering.md index 3e4114316..1ad439d55 100644 --- a/docs/xuexijianyi/electron-information-engineering.md +++ b/docs/xuexijianyi/electron-information-engineering.md @@ -23,7 +23,7 @@ IC 就是半导体元件产品的统称,主要分为数字 IC 和模拟 IC。 从入门门槛上来看,模拟比数字的门槛高一些,模拟如果想做的好一点,研究生学历是必须的,读博更好;数字门槛也不能说低,如果足够优秀,本科也是可以顺利就职的,版图岗位的要求更低一些,只有要数电、模电的基础,对大专生也是比较友好的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexijianyi/electron-information-engineering-30e29fba-9b5d-40da-bda1-dbd0e81b8247.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexijianyi/electron-information-engineering-30e29fba-9b5d-40da-bda1-dbd0e81b8247.png) 至于薪资的话,如果是名校出身的科班生,50 万的薪资可以说羡煞旁人了。 @@ -58,7 +58,7 @@ IC 就是半导体元件产品的统称,主要分为数字 IC 和模拟 IC。 中间的计算机,算是这个时代给予贫寒子弟仅有的一点温柔。反正我就是靠着计算机专业完成逆袭的。晒张图,这是我的家乡,虽然树很多、空气也很新鲜,但确实比较穷。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexijianyi/electron-information-engineering-4e3f8c93-9df5-48dd-9824-79005d5c5518.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexijianyi/electron-information-engineering-4e3f8c93-9df5-48dd-9824-79005d5c5518.png) 我之所以能够逆袭成功,因素有很多,但我想以下这三点非常重要: @@ -71,7 +71,7 @@ IC 就是半导体元件产品的统称,主要分为数字 IC 和模拟 IC。 **里面的书单真的非常 nice,不管是学习编程的新手,还是工作多年的老手,应该都很有帮助**: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexijianyi/electron-information-engineering-e40c4215-3078-42b3-8ab6-fe84c34d0a07.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexijianyi/electron-information-engineering-e40c4215-3078-42b3-8ab6-fe84c34d0a07.jpg) 戳:[计算机必读经典书单(可 download)!](https://tobebetterjavaer.com/download/java.html) @@ -138,7 +138,7 @@ IoC(Inverse of Control),也就是控制反转,是一种设计思想, 答案就是学习计算机组成原理。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexijianyi/electron-information-engineering-5270d8c0-5e03-4a06-924c-c9a8d25dd446.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexijianyi/electron-information-engineering-5270d8c0-5e03-4a06-924c-c9a8d25dd446.jpg) 计算机组成原理的知识点可以拆分为四个部分: @@ -157,7 +157,7 @@ IoC(Inverse of Control),也就是控制反转,是一种设计思想, 这门课的视频拍的那叫一个优质,虽然没有中文翻译,但视频下方的字幕会有节奏的移动。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexijianyi/electron-information-engineering-a5f13f0d-c0e1-486a-933a-7bfa81983a6c.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexijianyi/electron-information-engineering-a5f13f0d-c0e1-486a-933a-7bfa81983a6c.jpg) 国内的推荐哈工大的这门视频课,整体评价非常高(建议 1.5 倍速食用),戳:[计算机组成原理(唐朔飞)_哔哩哔哩_bilibili](https://www.bilibili.com/video/BV1WW411Q7PF) @@ -191,7 +191,7 @@ IoC(Inverse of Control),也就是控制反转,是一种设计思想, 我画了一张图,里面几乎涵盖了所有数据结构与算法书籍中都会讲到的知识点。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexijianyi/electron-information-engineering-46294124-8e8f-4d2e-ac48-f68a1be9b2ac.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexijianyi/electron-information-engineering-46294124-8e8f-4d2e-ac48-f68a1be9b2ac.jpg) 如果英语功底不错的话,推荐国外的 MIT 6.006,是经典的数据结构和算法入门课程,涉及到排序、哈希、图论、动态规划。 @@ -217,7 +217,7 @@ IoC(Inverse of Control),也就是控制反转,是一种设计思想, 考过研的同学应该知道《深入理解计算机系统》这本书是必读的,我自己最近又买了一本新的,算是我读的第二遍了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexijianyi/electron-information-engineering-52358273-5b8b-416a-b466-986b5f3ee96a.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexijianyi/electron-information-engineering-52358273-5b8b-416a-b466-986b5f3ee96a.jpg) 操作系统中的很多思想和经典算法,都可以在日常开发使用的各种工具或者框架中找到影子。 @@ -239,7 +239,7 @@ IoC(Inverse of Control),也就是控制反转,是一种设计思想, 每当进程需要调用内核时,它会触发一个*system call*(系统调用),system call进入内核执行相应的服务然后返回。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexijianyi/electron-information-engineering-b7db672b-7a5a-443f-8a4d-5a8e1d65886c.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexijianyi/electron-information-engineering-b7db672b-7a5a-443f-8a4d-5a8e1d65886c.jpg) 网友在学习这门课程的时候做的笔记:[MIT 6.S081 Lecture Notes](https://fanxiao.tech/posts/MIT-6S081-notes/),大家可以作为参考。 @@ -260,7 +260,7 @@ IoC(Inverse of Control),也就是控制反转,是一种设计思想, 下图是课程的封面,一个简易版的操作系统,和我们当前使用操作系统界面比起来,显然丑露无比,但它能让你体会到快乐——一种真正地学到了本领的快乐。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexijianyi/electron-information-engineering-cdccae94-aca8-4e88-b241-0b6d95dee5f9.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexijianyi/electron-information-engineering-cdccae94-aca8-4e88-b241-0b6d95dee5f9.jpg) >更详细的学习路线戳:[https://tobebetterjavaer.com/xuexiluxian/os.html](https://tobebetterjavaer.com/xuexiluxian/os.html) @@ -280,14 +280,14 @@ IoC(Inverse of Control),也就是控制反转,是一种设计思想, 一个资深的嵌入式硬件工程师的年薪在 15-30 万左右。需要有非常扎实的理论知识,以及高频 CPU、多层 PCB 板的设计经验。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexijianyi/electron-information-engineering-34380fff-fcbd-4d1a-805d-72d38a1d8834.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexijianyi/electron-information-engineering-34380fff-fcbd-4d1a-805d-72d38a1d8834.png) 嵌入式软件工程师指在 wince、linux 等嵌入式操作系统下进行用户应用软件开发的人员,硬件平台有手机、pad 等;开发工具有 wince 下的 evc,Linux 下的 QT 等。 嵌入式软件工程师的主要职责是根据产品的功能需求设计出好的软件,让硬件工作起来。一般情况下,软件工程师的需求量更大一些。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexijianyi/electron-information-engineering-5e3615e1-54c3-41a2-beca-97fcac8bc2cb.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexijianyi/electron-information-engineering-5e3615e1-54c3-41a2-beca-97fcac8bc2cb.png) 一个资深的嵌入式软件工程师的年薪也在 15-30 万左右。要熟练掌握 C/C++,熟练使用CortexM4系列单片机、RTOS(FreeRTOS, UcosII,RT-thread)等。 diff --git a/docs/xuexijianyi/read-csapp.md b/docs/xuexijianyi/read-csapp.md index e61c98668..9cd599c7e 100644 --- a/docs/xuexijianyi/read-csapp.md +++ b/docs/xuexijianyi/read-csapp.md @@ -30,7 +30,7 @@ tag: 我截了一张图,给大家看一下,这门课的目的:“让你深入了解代码在执行的时候到底发生了什么?”可以说包含了计算机专业的方方面面,计算机组成原理、操作系统、计算机网络等等。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexijianyi/read-csapp-eb4daee0-0de3-4f7f-8b7b-6892dba3305d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexijianyi/read-csapp-eb4daee0-0de3-4f7f-8b7b-6892dba3305d.png) 第二个视频地址: @@ -51,7 +51,7 @@ tag: - 进程间的交互和通信 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexijianyi/read-csapp-3f838b31-1551-438b-8f44-d4a2b5ab27dd.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexijianyi/read-csapp-3f838b31-1551-438b-8f44-d4a2b5ab27dd.png) 分享一些我的视频观感——计算机系统漫游部分。 @@ -60,29 +60,29 @@ tag: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexijianyi/read-csapp-9967518e-7807-4d84-a774-bc6eb5f44229.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexijianyi/read-csapp-9967518e-7807-4d84-a774-bc6eb5f44229.png) 第二个视频讲解了计算机的整个硬件组成部分,看下面这幅图,也太清晰了吧! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexijianyi/read-csapp-2bc0b332-7039-4978-86fa-f33701afb0e1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexijianyi/read-csapp-2bc0b332-7039-4978-86fa-f33701afb0e1.png) 第三个视频,普及了进程和线程、以及虚拟内存等重要知识点。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexijianyi/read-csapp-7b171f32-4418-4ba2-84ef-fe55881a1874.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexijianyi/read-csapp-7b171f32-4418-4ba2-84ef-fe55881a1874.png) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexijianyi/read-csapp-298ded3a-e510-466e-809e-57a9a998ab80.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexijianyi/read-csapp-298ded3a-e510-466e-809e-57a9a998ab80.png) 第四个视频,讲解了阿姆达尔定律,以及如何提高程序性能的解决方案。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexijianyi/read-csapp-fe228ea9-4317-4b19-881e-08972ff20c9e.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexijianyi/read-csapp-fe228ea9-4317-4b19-881e-08972ff20c9e.png) 这两套视频我还没有看完,除了有一些看不懂之外,很多知识点都需要看多遍,并且查找一些资料后才能懂。 @@ -111,7 +111,7 @@ tag: 这门课是 2020 年新出的,所以食用起来会更加的舒适。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexijianyi/read-csapp-5a759059-81da-4814-881c-5b58fb168de8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexijianyi/read-csapp-5a759059-81da-4814-881c-5b58fb168de8.png) 当然了,要学好 CSAPP 这门课,并不容易,因为它包含了汇编、计算机组成原理、操作系统、计算机网络等等方方面面的知识,任何一个知识点展开去学习,都是需要花费大量时间和精力的。 diff --git a/docs/xuexiluxian/algorithm.md b/docs/xuexiluxian/algorithm.md index 907eab083..8386a7adb 100644 --- a/docs/xuexiluxian/algorithm.md +++ b/docs/xuexiluxian/algorithm.md @@ -14,11 +14,11 @@ tag: 等找到了工作后才恍然大悟,原来当初根本不用那么辛苦。这不,刚好有小伙伴在《[Java 程序员进阶之路](https://tobebetterjavaer.com/)》上问我算法的学习路线,我就毫不保留地把我算法方面的学习经验分享出来,希望能给大家一点点启发和帮助。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/algorithm-59f3bc36-4e6c-48c0-86e5-8afdd6165147.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/algorithm-59f3bc36-4e6c-48c0-86e5-8afdd6165147.png) 我画了一张图,里面几乎涵盖了所有数据结构与算法书籍中都会讲到的知识点。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/algorithm-220e86a7-fa5d-44a2-a560-3cb6cfa70ad0.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/algorithm-220e86a7-fa5d-44a2-a560-3cb6cfa70ad0.png) 但讲良心话,对于一个初学者,或者不打算卷算法岗的程序员来说,完全就没必要把思维导图里面的所有知识点都学了,那样就太不高效了。 @@ -154,7 +154,7 @@ class 二哥 { B 站上浙江大学的一个数据结构课非常不错,很系统很经典。每次看这些大学老师的讲课就越觉得考好大学很重要,很多学校的老师,在讲数据结构和算法的时候,自己都不知道自己在讲什么。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/yitiaolong-e2812c94-5945-41ad-adcd-4dbb02fae8d5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/yitiaolong-e2812c94-5945-41ad-adcd-4dbb02fae8d5.png) 不得不说,陈越姥姥等人对数据结构和算法这个领域的知识理解是真的透彻,我在听这个课的时候感觉整个人都通透了。 @@ -169,26 +169,26 @@ B 站上浙江大学的一个数据结构课非常不错,很系统很经典。 入门阶段推荐陈小玉老师的《[趣学数据结构](https://book.douban.com/book/subject/34785269/)》和《[趣学算法](https://book.douban.com/subject/27109832/)》。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/algorithm-2ac7da9b-e812-49d9-8012-5e2f792f87bf.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/algorithm-2ac7da9b-e812-49d9-8012-5e2f792f87bf.png) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/algorithm-18b8f9d6-cbfa-482f-80a0-57081e3616d8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/algorithm-18b8f9d6-cbfa-482f-80a0-57081e3616d8.png) 算法领域的经典参考书《[算法 4](https://book.douban.com/subject/19952400/)》也非常值得推荐,里面的代码是用 Java 实现的,所以 Java 开发者可以直接选用这本书。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/algorithm-766885af-dfac-452f-b110-c9ca89a0bb9a.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/algorithm-766885af-dfac-452f-b110-c9ca89a0bb9a.png) 学霸型人才可以直接刷《[算法导论](https://book.douban.com/subject/20432061/)》,严谨全面,可以直接拿来作为研究生阶段的算法课程教材。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/algorithm-fc895095-927c-42a4-a934-f98371a6f376.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/algorithm-fc895095-927c-42a4-a934-f98371a6f376.png) **3)开源电子书** 算法方面的开源电子书是真的非常多,这里推荐下《labuladong 的算法小抄》,仓库里面有句话我非常认同——“刷题刷题,刷的是题,培养的是思维”,这个仓库的优势就在于,它的解题思路很完备,我相信可以帮助到不少读者,至少在刷题的时候少走很多弯路。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/algorithm-d573f369-7dff-47a3-9929-46044e419aa8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/algorithm-d573f369-7dff-47a3-9929-46044e419aa8.png) >GitHub 地址:[https://github.com/labuladong/fucking-algorithm](https://github.com/labuladong/fucking-algorithm) @@ -198,7 +198,7 @@ B 站上浙江大学的一个数据结构课非常不错,很系统很经典。 除此之外,再给大家推荐三份高质量的刷题笔记,C/C++、Java、Go 版的全有了!同样可以通过上面的方式获取。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/algorithm-6c0c84be-ba1c-48d0-8ea7-70ef66e04ff2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/algorithm-6c0c84be-ba1c-48d0-8ea7-70ef66e04ff2.png) **4)在线网站** @@ -208,20 +208,20 @@ B 站上浙江大学的一个数据结构课非常不错,很系统很经典。 这里我给大家整理了 100 道面试高频算法题,大多数的小伙伴们按照这个路线去刷就足够应付面试了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/algorithm-c4d31ba3-6d81-4617-a68b-81d935b2a106.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/algorithm-c4d31ba3-6d81-4617-a68b-81d935b2a106.png) >VisuAlgo:[https://visualgo.net/zh](https://visualgo.net/zh) 算法的难点在于,没办法在脑例子抽象出它的步骤,那 VisuAlgo 就是一个非常值得推荐的可视化算法网站。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/algorithm-b0d17b5d-d13a-46aa-a8b0-6b16772a30a6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/algorithm-b0d17b5d-d13a-46aa-a8b0-6b16772a30a6.png) **5)付费专栏** 小争哥在极客时间上开了一门《数据结构与算法之美》的付费专栏,风评非常不错,喜欢的小伙伴可以[戳链](http://gk.link/a/11ijz)接去购买。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/algorithm-705c33b5-b90e-49c7-a3bc-a1f3c4630a66.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/algorithm-705c33b5-b90e-49c7-a3bc-a1f3c4630a66.png) ### 五、一点小心得 diff --git a/docs/xuexiluxian/c.md b/docs/xuexiluxian/c.md index 960c6d40b..5a31059a4 100644 --- a/docs/xuexiluxian/c.md +++ b/docs/xuexiluxian/c.md @@ -18,7 +18,7 @@ tag: 我第一时间就整理了一份 PDF 版,截个图大家可以感受下。我对这份教程非常满意,该讲的地方都讲到了,示例也给了很多,对初学者来说,入门必备。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/c-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/c-1.png) 直接长按识别/扫描下方二维码,关注后回复 「**08**」 下载这份 PDF 吧: @@ -30,13 +30,13 @@ tag: 可以先看阮一峰老师的《C语言入门教程》,也可以先花一个月的时间,去 mooc 把《程序设计入门 C语言》这门视频课过一遍。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/c-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/c-2.png) >视频地址:[https://www.icourse163.org/course/ZJU-199001](https://www.icourse163.org/course/ZJU-199001) 看完这套视频,大家至少能学到: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/c-3.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/c-3.jpg) 学完这门入门课后,可以再看一下进阶版的。课程以一个 Windows 下的图形游戏程序入手,借助一个 C 语言图形库,展开全部的教学内容,非常有趣。 @@ -56,13 +56,13 @@ tag: C语言是由贝尔实验室的 Dennis Ritchie 在 1969 年~ 1973 年间发明创造的。 -![C语言之父](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/c-1.jpg) +![C语言之父](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/c-1.jpg) Dennis Ritchie是这样评价自己的亲儿子的:“**古怪的、有缺陷的,但同时也是一个巨大的成功**。”这句评语还是非常中肯的。 **第一,C语言在计算机领域起到了承上启下的作用**。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/c-4.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/c-4.jpg) C语言非常的简洁,几乎没有任何冗余。上手难度几乎为 0,除了指针难以被新手掌握以外,其他的基础语法都很容易上手。很多编程语言都受到了C语言的影响,比如说 Java、C++、C#、Python、Go 等等,那学会了 C语言,再学习其他的编程语言就会丝滑得多。 @@ -77,7 +77,7 @@ C语言非常的简洁,几乎没有任何冗余。上手难度几乎为 0, C语言能够直接操作硬件、管理内存、跟操作系统交互,这使得它成为了一种非常接近底层的编程语言,非常适合有极高性能要求的程序。 -![MATLAB+C语言来控制机械臂](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/c-2.jpg) +![MATLAB+C语言来控制机械臂](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/c-2.jpg) **04)C语言核心知识点汇总** @@ -98,7 +98,7 @@ C语言能够直接操作硬件、管理内存、跟操作系统交互,这使 再来看详细版的思维导图: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/c-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/c-5.png) C语言有两个关键部分: @@ -124,7 +124,7 @@ C语言有两个关键部分: **1)可以进行加减乘除的计算器** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/c-6.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/c-6.jpg) >课程地址:[https://www.lanqiao.cn/courses/75](https://www.lanqiao.cn/courses/75) @@ -139,7 +139,7 @@ C语言有两个关键部分: **2)实现一个属于自己的编程语言** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/c-7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/c-7.png) >课程地址:[https://www.shiyanlou.com/courses/670](https://www.shiyanlou.com/courses/670) @@ -165,7 +165,7 @@ C语言有两个关键部分: 如果英语功底比较扎实的话,可以直接看原版。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/c-8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/c-8.png) 认真跟着做这些 lab,真本领就学到手了。 @@ -174,18 +174,18 @@ C语言有两个关键部分: 给初中生看的《啊哈 C语言》,简单易懂,生动有趣,虽然只能学到 C语言的冰山一角,但也值了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/c-9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/c-9.png) 给高中生、大一新生看的入门书《c primer plus》,语法全覆盖,细节多,知识点讲的清楚,缺点是琐碎,细节弯弯绕。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/c-10.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/c-10.png) 还有一本《C 程序设计语言》,这本书的作者是 C语言之父,所以全书讲的都是 C语言的核心。代码示例大多都是库函数的实现方法,内容简洁明了,干净利落,没有废话。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/c-11.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/c-11.png) 给 @@ -200,7 +200,7 @@ C语言有两个关键部分: 看的进阶书《C 陷阱与缺陷》、《C 专家编程》、《C 和指针》 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/c-12.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/c-12.png) 尽管我不推荐大家看书入门,但我知道很多读者还是喜欢通过看书的方式学习。直接长按扫描下方二维码,关注后回复 「**C**」 下载这 PDF 版吧: @@ -212,7 +212,7 @@ C语言有两个关键部分: - GitHub 地址:[https://github.com/itwanger/JavaBooks](https://github.com/itwanger/JavaBooks#c) - 码云地址:[https://gitee.com/itwanger/JavaBooks](https://gitee.com/itwanger/JavaBooks#c) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/c-books.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/c-books.jpg) 简单罗列一下: diff --git a/docs/xuexiluxian/ccc.md b/docs/xuexiluxian/ccc.md index 85d9239db..e12a7f592 100644 --- a/docs/xuexiluxian/ccc.md +++ b/docs/xuexiluxian/ccc.md @@ -10,7 +10,7 @@ tag: 学习 C++ 语法,首推《C++ Primer 第五版》。我在参加第一份工作的时候,就买过一本《C++ Primer 第四版》,这本书一直不舍得扔,尽管它已经非常破了,但我们之间的感情是深厚的,毕竟跟随我辗转了好几个城市了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/ccc-primer-book.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/ccc-primer-book.png) *是不是一下子就暴露自己工作年限了*? @@ -18,15 +18,15 @@ tag: 然后是《Effective C++》,侯捷老师译的,这本书主要讲解了编写 C++ 代码需要注意的一些条款,和《Effective Java》 属于同一个系列。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/ccc-2.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/ccc-2.jpg) 第三本是《STL源码剖析》这本书,侯捷老师写的,这本书讲了C++的底层实现,包括各种容器(vector、list、heap、deque、Red Black tree、hash table、set/map)的实现、各种常见算法(排序、查找、排列组合、数据移动与复制技术)的实现等。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/ccc-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/ccc-3.png) 第四本《深度探索C++对象模型》,侯捷老师译的,这本书讲解了 C++ 面向对象特性的底层实现机制,读起来虽然有点晦涩,但读完后就会搞明白“代码跑起来的时候实际发生了什么”。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/ccc-4.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/ccc-4.jpg) 既然有三本书都是侯捷老师的,那再刷一刷侯捷老师的视频,岂不是效果更佳?不过,由于涉及到版权,国内的 B 站、A 站都下架了相关的视频资源,包括: @@ -36,7 +36,7 @@ tag: - 《C++ 11 新特性》 - 《C++ 程序的生前死后》 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/ccc-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/ccc-5.png) 我从网上 down 了一份,保存到了本地。需要的小伙伴请长按识别/扫描下方的二维码关注作者的原创公众号「**沉默王二**」回复关键字「**cpp**」就可以拉取到了。 @@ -48,7 +48,7 @@ tag: - 码云地址:[https://gitee.com/itwanger/JavaBooks](https://gitee.com/itwanger/JavaBooks#c-1) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/cpp-books.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/cpp-books.jpg) 简单罗列一下: diff --git a/docs/xuexiluxian/go.md b/docs/xuexiluxian/go.md index 1dbfc17cb..33df92b3e 100644 --- a/docs/xuexiluxian/go.md +++ b/docs/xuexiluxian/go.md @@ -10,7 +10,7 @@ tag: 大家好,我是二哥呀!最近又有一个读者来咨询我的建议:**应届生,要求 Java 进去后转 Go,问我咋样?** -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-1.png) 据我自己的印象,前前后后有十个读者来问过我这个问题了。一方面 Java 就业岗位确实多,薪资也给力;另外一方面,Go 确实很有潜力。 @@ -22,7 +22,7 @@ tag: Go 语言诞生于 2009 年,发展到现在,已经 12 岁了(应该没算错吧😭)。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-2.png) 很多明星级的开源产品,比如说 Kubernetes、Docker等,都是基于 Go 语言编写的。再加上近些年来微服务架构和云原生技术的普及,也大大的带火了 Go 这门编程语言,使其在 C/C++ 和 Java 中杀出了一条血路。 @@ -34,17 +34,17 @@ Google 和字节两家大厂对 Go 的大力投入,在一定程度上又加速 目前,使用 Go 语言的公司越来越多了,阿里、百度、腾讯、小米这些互联网大厂也在积极拥抱。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-3.png) 这使得 Go 工程师的薪资待遇也得到了很大程度上提高。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-4.png) 当然了,目前基本上除了大厂,很少有其他中小型公司用 Go,因为 Go 的生态还比不上 Java,没有 Java 那么丰富健全,中小公司的投入产出比比较低。大厂有人有钱,愿意在 Go 上投入成本。 另外,Go 也没有像外界吹捧的那么牛叉,就编程语言的排行榜上来看,Go 还是个滴滴(Python、C/C++、Java、C# 这些仍然是前排),有待发展。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-5.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-5.jpg) ### 二、为什么会要求 Java 转 Go 呢? @@ -62,13 +62,13 @@ Google 和字节两家大厂对 Go 的大力投入,在一定程度上又加速 ### 三、Go 语言该怎么学习呢? -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-6.png) 不管怎么说,Go 语言的确是值得去学习的。如果公司确实需要转 Go 岗,也完全没有必要抗拒。 这里给大家推荐一个 Go 语言的学习路线图,如果你想要成为一名Go语言的开发者的话,可以沿着这张图里面的路径去学习。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-7.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-7.jpg) 接下来给大家推荐一些 Go 语言的学习资料,全部免费开源,是我肝了两个大夜精挑细选出来的。 @@ -78,7 +78,7 @@ Google 和字节两家大厂对 Go 的大力投入,在一定程度上又加速 **第一套视频:《Go 编程基础》**,主要面向 Go 语言新手级别的学习者。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-8.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-8.jpg) >课程地址:[https://github.com/unknwon/go-fundamental-programming](https://github.com/unknwon/go-fundamental-programming) @@ -86,7 +86,7 @@ Google 和字节两家大厂对 Go 的大力投入,在一定程度上又加速 虽然尚硅谷是一家培训机构,但在 B 站上公开的一些课程还真的是挺香的,学 Java 的同学可能很大一部分都看过宋红康老师的课,YYDS! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-9.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-9.jpg) >视频地址:[https://www.bilibili.com/video/BV1ME411Y71o](https://www.bilibili.com/video/BV1ME411Y71o) @@ -96,13 +96,13 @@ Google 和字节两家大厂对 Go 的大力投入,在一定程度上又加速 点击右侧的菜单可以快速浏览教程的所有内容,非常方便。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-10.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-10.jpg) >教程地址:[http://tour.studygolang.com/welcome/1](http://tour.studygolang.com/welcome/1) **第二套教程:LeetCode-Go**,一本 LeetCode 的刷题笔记,代码是用 Go 语言实现的,收录了超过 500 道题的题解思路和代码,代码方案都是效率超高的“标准答案”: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-11.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-11.jpg) >教程地址:[https://books.halfrost.com/leetcode/](https://books.halfrost.com/leetcode/) @@ -115,7 +115,7 @@ Google 和字节两家大厂对 Go 的大力投入,在一定程度上又加速 几乎覆盖了 Go 语言从编译到运行的方方面面,读完后能对 Go 语言有更加整体和深刻的认识。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-12.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-12.jpg) >在线地址:[https://draveness.me/golang/](https://draveness.me/golang/) @@ -125,11 +125,11 @@ Google 和字节两家大厂对 Go 的大力投入,在一定程度上又加速 不过遗憾的是,在线阅读地址挂了,我还提交了一个 issue: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-13.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-13.jpg) 不过幸好我备份了一份离线版 PDF,里面的内容还是非常完整的: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-14.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-14.jpg) 需要的小伙伴请长按识别/扫描下方的二维码关注作者的原创公众号「**沉默王二**」回复关键字「**go**」就可以拉取到了。 @@ -137,7 +137,7 @@ Google 和字节两家大厂对 Go 的大力投入,在一定程度上又加速 **第五套教程:Go标准库文档**,可以查询每个API的具体使用方式,这也是 Go 开发者的必备手册。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-15.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-15.jpg) >在线地址:[https://studygolang.com/static/pkgdoc/main.html](https://studygolang.com/static/pkgdoc/main.html) @@ -145,19 +145,19 @@ Google 和字节两家大厂对 Go 的大力投入,在一定程度上又加速 **第一本:Go语言实战**,这本书关注于 Go 语言的规范和实现,涉及的内容包括语法、Go 的类型系统、并发、通道和测试等主题。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-16.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-16.jpg) **第二本:《Go 语言学习笔记》**,上卷专注于语言规范相关细节,下卷专注于对运行时源码做出深度剖析,诸如内存分配、垃圾回收和并发调度等。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-17.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-17.jpg) **第三本:《Go Web 编程》**,这本书以一个网络论坛作为例子,讲解了如何使用请求处理器、多路复用器、模板引擎、存储系统等核心组件去构建一个 Go 的 Web 应用。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-18.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-18.jpg) 下面是我根据一位拿到大厂 offer 的师弟菜饼提供的学习资料整理出来的书单,相信对你学习 go 语言会很有帮助的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-4b3b67f3-ebe0-4311-8c7c-7e87f89ccdc7.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-4b3b67f3-ebe0-4311-8c7c-7e87f89ccdc7.jpg) 需要的小伙伴请长按识别/扫描下方的二维码关注作者的原创公众号「**沉默王二**」回复关键字「**go**」就可以拉取到了。 @@ -171,19 +171,19 @@ Google 和字节两家大厂对 Go 的大力投入,在一定程度上又加速 **第二个项目:seaweedfs**,GitHub 上星标 13k+,这是一个自带文件浏览器的网盘服务,支持文件浏览、生成分享链接、批量上传、创建文件夹等功能、用户系统。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-20.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-20.jpg) >地址:[https://github.com/filebrowser/filebrowser](https://github.com/filebrowser/filebrowser) **第三个项目:go-admin**,GitHub 上星标 5k+,基于 Go 语言的一个数据可视化与管理平台,使开发者能在极简短的时间里,用极简短的代码量搭建起一个后台管理。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-21.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-21.jpg) >地址:[https://github.com/GoAdminGroup/go-admin](https://github.com/GoAdminGroup/go-admin) **第四个项目:7天用Go从零实现分布式缓存GeeCache**,每天完成的部分都是可以独立运行和测试的,就像搭积木一样,最终组合在一起就是一个完整的分布式缓存系统。每天的代码在 100 行左右。。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/go-22.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/go-22.jpg) >地址:[https://geektutu.com/post/geecache.html](https://geektutu.com/post/geecache.html) diff --git a/docs/xuexiluxian/java/jvm.md b/docs/xuexiluxian/java/jvm.md index 96ac710e2..154c52818 100644 --- a/docs/xuexiluxian/java/jvm.md +++ b/docs/xuexiluxian/java/jvm.md @@ -11,7 +11,7 @@ tag: 2020 年的时候,通读了一遍周志明老师的《深入理解 Java 虚拟机:JVM 高级特性与最佳实践》第三版,读完之后受益匪浅,这让我对 Java 虚拟机有了一个更完整的认识。毫无疑问,《深入理解 Java 虚拟机》是 JVM 书籍中最好的一本书了,国产技术书的天花板。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/jvm-7036a048-4034-4965-92a6-e35d0211ba71.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/jvm-7036a048-4034-4965-92a6-e35d0211ba71.png) 在金三银四/金九银十的跳槽季中,很多小伙伴都会忍不住蠢蠢欲动,其中 JVM 更是面试中不可或缺的一部分,所以我花了几天的时间整理了一条 JVM 的学习路线,希望能帮助到大家。 @@ -31,14 +31,14 @@ tag: 明白了学习 JVM 的重要性,那我们就开搞吧! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/jvm-4cbbdc72-3bdd-4b14-9d8d-ecd7764afb11.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/jvm-4cbbdc72-3bdd-4b14-9d8d-ecd7764afb11.png) ### 二、JVM 学习路线图 这是我最近整理的一张关于 JVM 的思维导图,大的方向可以分为三个部分:字节码与类的加载、内存与垃圾回收、性能监控和调优。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/jvm-b4ec034b-9f20-40b9-a2a3-e77afffd2abf.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/jvm-b4ec034b-9f20-40b9-a2a3-e77afffd2abf.png) 字节码与类的加载包括: @@ -72,7 +72,7 @@ tag: 进去直接找「Java 核心」里面的 Java 虚拟机就对了。我按照前面的思维导图整理了 19 篇文章,全部都是硬核级别的,跟着学就对了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/jvm-cc84fead-278b-46aa-a8ab-a3b07acefc12.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/jvm-cc84fead-278b-46aa-a8ab-a3b07acefc12.png) **2)视频** @@ -82,7 +82,7 @@ tag: - 字节码与类的加载篇 - 性能监控和调优篇 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/jvm-59c829ce-7fe3-45a5-b074-35dacb08941e.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/jvm-59c829ce-7fe3-45a5-b074-35dacb08941e.png) >视频地址:[https://www.bilibili.com/video/BV1PJ411n7xZ](https://www.bilibili.com/video/BV1PJ411n7xZ) @@ -100,7 +100,7 @@ tag: - 说说垃圾回收算法 - 说说 JVM 内存结构 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/jvm-be0910d8-7669-46cb-b8b9-2399162e723d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/jvm-be0910d8-7669-46cb-b8b9-2399162e723d.png) >视频地址:[https://www.bilibili.com/video/BV1iJ411d7jS](https://www.bilibili.com/video/BV1iJ411d7jS) @@ -108,7 +108,7 @@ tag: 纸质书只推荐一本周志明老师的神书《[深入理解 Java 虚拟机](https://book.douban.com/subject/34907497/)》,基本上学习 JVM 的小伙伴人手一本。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/jvm-3f6065c5-2fbf-4107-804e-393b32a4f9ab.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/jvm-3f6065c5-2fbf-4107-804e-393b32a4f9ab.png) 这是一部从工作原理和工程实践两个维度深入剖析JVM的著作,是计算机领域公认的经典。 @@ -124,7 +124,7 @@ tag: 推荐 doocs 社区的 [JVM 底层原理最全知识总结](https://doocs.github.io/jvm/),算是《深入理解 Java 虚拟机》这本书的一个精简知识点梳理。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/jvm-f2e2e5c4-c193-4af5-b1bc-fea8df2006af.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/jvm-f2e2e5c4-c193-4af5-b1bc-fea8df2006af.png) >GitHub 地址:[https://github.com/doocs/jvm](https://github.com/doocs/jvm) @@ -139,7 +139,7 @@ tag: 为了方便大家的学习,我也将其整理成了 PDF,内容包含了 Java 内存区域、垃圾收集算法、经典垃圾收集器、虚拟机类加载机制、程序编译和代码优化,手绘图也非常的漂亮。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/jvm-2beabbe4-2ddd-4180-8690-1bc3224e6b41.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/jvm-2beabbe4-2ddd-4180-8690-1bc3224e6b41.png) 需要的小伙伴可以微信搜索「**沉默王二**」回复关键字「**java**」就可以拉取到了。 @@ -152,7 +152,7 @@ tag: 这里给大家推荐两份 Java 虚拟机方面的八股文,一份来自读者[三分恶](https://mp.weixin.qq.com/s/1jhBZrAb7bnvkgN1TgAUpw),一份来自读者小牛,先截图给大家看一下 Java 虚拟机的理解版八股文,图文并茂,非常容易消化和吸收。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/jvm-a11893df-e518-4bdc-a166-884b168a8cf0.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/jvm-a11893df-e518-4bdc-a166-884b168a8cf0.png) 为了方便大家的阅读和背诵,我已经将其整理到了二哥的小破站《Java 程序员进阶之路》上,面渣逆袭 Java 虚拟机篇: @@ -201,7 +201,7 @@ JVM 相关的知识已经成为面试必考的科目了,但老实讲,JVM 相 给大家截图展示一下里面都有哪些优质的 PDF: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/java-books.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/java-books.jpg) **Java 虚拟机虽然难学,但如果你能坚持学下去,内功自然而然就提升了一大截**。 diff --git a/docs/xuexiluxian/java/thread.md b/docs/xuexiluxian/java/thread.md index 823bf8811..7521f3ae2 100644 --- a/docs/xuexiluxian/java/thread.md +++ b/docs/xuexiluxian/java/thread.md @@ -13,7 +13,7 @@ tag: 今天这篇文章就来给大家盘点一下 Java 并发到底该如何从入门到精通,请及时用鸡毛掸子把收藏夹里的灰清理一下。在阅读过程中,如果有所帮助,麻烦点赞/收藏和转发,算是对我码字的这份坚持的亿点点鼓励。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java-thread-1.gif) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java-thread-1.gif) ### 一、为什么要学 Java 并发? @@ -24,7 +24,7 @@ tag: 当然了,Java 并发涉及到东西实在是不少,包括操作系统的知识,Java 虚拟机的一些知识,Java 线程模型的知识,多线程相关的关键字,比如说 synchronized、volatile 等,还有锁的知识、JDK 提供的工具类等等,学起来还是非常容易令人头大的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java-thread-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java-thread-2.png) 因此,我们需要一些高效的学习路线图,以及一些优质的学习资源,从而减少我们学习Java 并发编程所投入的时间和精力。 @@ -32,7 +32,7 @@ tag: 这是我最近整理的一张关于 Java 并发编程的思维导图,大的方向可以分为三个部分:线程基础、理论基础、工具类 JUC。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java-thread-map.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java-thread-map.png) 线程基础部分包括: @@ -68,13 +68,13 @@ tag: 进去直接找 Java 核心里面的 Java 并发编程就对了。我按照前面的思维导图整理了 27 篇文章,全部都是硬核级别的,跟着学就对了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java-thread-map-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java-thread-map-1.png) **2)视频** 懂的都懂,看视频到 B 站。黑马的《Java并发编程》评价还不错,300 多个小节,我觉得讲的比较好的有三部分:synchronized优化原理、AQS和线程池。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java-thread-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java-thread-4.png) >视频地址:[https://www.bilibili.com/video/BV16J411h7Rd](https://www.bilibili.com/video/BV16J411h7Rd) @@ -82,7 +82,7 @@ tag: 纸质书只推荐一本《[Java 并发编程实战](https://book.douban.com/subject/10484692/)》,豆瓣评分 9.0。不过这本书确实有点老了,基本上是按照 Java 6 来讲解的,希望出版社能早点出 2.0 版。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java-thread-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java-thread-5.png) 《Java 并发编程实战》这本书从总体上来看,分两条主线: @@ -103,7 +103,7 @@ tag: 推荐 RedSpider社区的[深入浅出 Java 多线程](http://concurrent.redspider.group/RedSpider.html),比Java 并发编程实战更通俗易懂一些,因为里面穿插了很多精美的手绘图。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java-thread-6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java-thread-6.png) >GitHub地址:[https://github.com/RedSpider1/concurrent](https://github.com/RedSpider1/concurrent) @@ -129,7 +129,7 @@ tag: 这里给大家推荐两份 Java 并发编程方面的八股文,一份来自[三分恶](https://mp.weixin.qq.com/s/1jhBZrAb7bnvkgN1TgAUpw),一份来自小牛,先截图给大家看一下 Java 并发方面都有哪些高频的面试题。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java-thread-7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java-thread-7.png) 为了方便大家的阅读和背诵,我已经将其整理到了二哥的小破站《Java 程序员进阶之路》上,面渣逆袭 Java 并发篇: @@ -141,7 +141,7 @@ Java 并发编程八股文(背诵版): 这两份八股文的质量都非常高,来看一下AQS了解多少小节下的内容,图文并茂,非常容易消化和吸收。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java-thread-8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java-thread-8.png) 诚实点说,如果能把这两份八股文背会的话,简历上就真的敢写“精通”Java 并发了。 @@ -156,7 +156,7 @@ Java 提供的并发组件,大致可以分为两类: 想要学好 Java 并发编程,就必须得对下图中提到的基础概念进行充分的理解。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java-thread-9.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java-thread-9.jpg) 在我看来,并发编程主要是用来解决这两个痛点的: @@ -204,7 +204,7 @@ Java 提供的并发组件,大致可以分为两类: 给大家截图展示一下里面都有哪些优质的 PDF: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/java-books.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/java-books.jpg) **Java 并发编程虽然难学,会涉及到操作系统、CPU、内存等偏基础方面的内容,但如果你能坚持学下去,内功自然而然就提升了一大截**。 diff --git a/docs/xuexiluxian/java/yitiaolong.md b/docs/xuexiluxian/java/yitiaolong.md index 03dbfb90f..5ba07c862 100644 --- a/docs/xuexiluxian/java/yitiaolong.md +++ b/docs/xuexiluxian/java/yitiaolong.md @@ -22,7 +22,7 @@ tag: 对于科班的同学来说,我建议在学习 Java 这门编程语言之前,学一下 C 语言。我上大学那会,教材用的是《Java 编程思想》,但说真的,这本书对初学者并不友好。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/yitiaolong-d5e5b52c-1744-468c-ab9c-f77c24415394.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/yitiaolong-d5e5b52c-1744-468c-ab9c-f77c24415394.jpg) 编程语言都是相通的,C语言作为 Java 语言的母胎,**还是非常值得科班去学习一下打打基础的**。其实对于我们程序员来说,学的是计算机科学,而不是编程语言,语言只是工具,没有优劣。像我,就学过 Java、C 语言、Ruby、JavaScript 等等这些编程语言,他们之间确实有很多相似之处。如果学过 JavaScript,能很容易搞懂 Java 的 Lambda 表达式。 @@ -30,14 +30,14 @@ C 语言的特殊性就在于它可能是唯一一门最适合来学习一系列 当然了,由于 C 语言的抽象程度更高,学起来也需要花费一番功夫。对于科班的初学者来说,我推荐翁恺教授的 C 语言程序设计。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/yitiaolong-82aefde9-8a3f-4f28-aad5-02e39a6b9d4b.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/yitiaolong-82aefde9-8a3f-4f28-aad5-02e39a6b9d4b.png) >B 站地址:[https://www.bilibili.com/video/BV19W411B7w1](https://www.bilibili.com/video/BV19W411B7w1) 喜欢看书的同学我只推荐一本,《**阮一峰老师的 C语言入门教程**》,我第一时间就拜读了一遍,受益匪浅!可以说目前我见到的最好的 C语言入门教程了,没有之一! -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/yitiaolong-3507eb06-8424-4b8e-b20d-046268508c9d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/yitiaolong-3507eb06-8424-4b8e-b20d-046268508c9d.png) 我第一时间就整理了一份 PDF 版的,需要的小伙伴可以长按识别/扫描下方二维码,关注后回复 「**阮一峰**」 下载这份 PDF 吧: @@ -60,7 +60,7 @@ JDK 是 Java Development ToolKit 的简称,也就是 Java 开发工具包。JD Intellij IDEA,不用说,是编写 Java 程序的最佳 IDE,初学者选择社区版就完全够用了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/yitiaolong-3fb56f9d-942a-4439-8bab-0f19bd59ef58.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/yitiaolong-3fb56f9d-942a-4439-8bab-0f19bd59ef58.png) 初次使用的话,建议阅读一下《IntelliJ IDEA 简体中文专题教程》,GitHub 上已开源。 @@ -81,13 +81,13 @@ Maven 是一个项目管理和自动化构建工具,基于项目对象模型 我这里给大家贴一张《Java 程序员进阶之路》的导航地图,大类分为 Java 核心、Java 企业级开发、数据库、计算机基础、求职面试、学习资源等,基本上你要的 Java 学习资源,这里都有,没有的,后面我也会补充上。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/yitiaolong-c80191bf-ab08-4a28-898f-ea7ffff3f966.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/yitiaolong-c80191bf-ab08-4a28-898f-ea7ffff3f966.png) Java 是一门面向对象的编程语言,所以三大特性:封装、继承、多态是必须要掌握的,然后是异常处理、IO、集合、并发编程和 Java 虚拟机。只要这些内容掌握了,可以说 Java 语言本身的核心知识就全部掌握了。 这些知识该怎么学呢?当然是直接上二哥的 Java 程序员进阶之路了,内容非常的全面和硬核,截图给大家鉴赏一下。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/yitiaolong-a5436c4a-6464-4065-ae0d-50755c4184df.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/yitiaolong-a5436c4a-6464-4065-ae0d-50755c4184df.png) 记住我们的网址: @@ -97,10 +97,10 @@ Java 是一门面向对象的编程语言,所以三大特性:封装、继承 这其中的难点是并发编程和 JVM,显然这两部分的内容学起来并不容易,但却最能考验一名 Java 后端工程师的功底了。 -![Java 并发编程核心知识点](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/yitiaolong-dac18663-f729-41d6-8253-2473b2174b9e.png) +![Java 并发编程核心知识点](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/yitiaolong-dac18663-f729-41d6-8253-2473b2174b9e.png) -![JVM 核心知识点](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/yitiaolong-35df7e61-99c1-4efc-8f9f-702cc0f08904.png) +![JVM 核心知识点](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/yitiaolong-35df7e61-99c1-4efc-8f9f-702cc0f08904.png) 因为是自学,所以强烈建议大家把教程上的代码都敲一遍,尤其是在初学阶段。由于不像工作后,有大量的项目可以实践,所以不能得到及时的反馈,记笔记就显得特别的重要。另外,一定要分门别类保存好自己的 demo,以便以后可以快速得找得到,尤其是一些小套路,会很有用,积累得多了,可能就变成了自己的工具库。 @@ -110,7 +110,7 @@ Java 是一门面向对象的编程语言,所以三大特性:封装、继承 关于 Spring Boot 的实战内容,二哥也在紧锣密鼓的准备[编程喵](https://github.com/itwanger/coding-more)这个实战项目,可以先给大家展示一下后端用到的技术栈,都是非常主流的技术。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/zhishixingqiu/readme-8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/zhishixingqiu/readme-8.png) 如果说你已经掌握了 Spring、SpringMVC、MyBatis、Spring Boot 等内容,就有能力进行一些真正有用的应用项目开发了,比如说学生管理系统、商城系统、博客系统、秒杀系统等等。 @@ -177,7 +177,7 @@ JSP 在实际开发中,主要是作为 MVC 模型中的V(View)层出现的 B 站上浙江大学的一个数据结构课非常不错,很系统很经典。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/yitiaolong-e2812c94-5945-41ad-adcd-4dbb02fae8d5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/yitiaolong-e2812c94-5945-41ad-adcd-4dbb02fae8d5.png) >视频地址:[https://www.bilibili.com/video/BV1JW411i731](https://www.bilibili.com/video/BV1JW411i731) @@ -193,7 +193,7 @@ B 站上浙江大学的一个数据结构课非常不错,很系统很经典。 学习设计模式的话,推荐好朋友小傅哥的《重学 Java 设计模式》。我之前推荐的 Refactoring Guru 网站,也非常的 nice。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/yitiaolong-3b9ab6b9-67f2-4810-bb39-9e1f48ffe3da.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/yitiaolong-3b9ab6b9-67f2-4810-bb39-9e1f48ffe3da.png) >在线阅读地址:[https://refactoring.guru](https://refactoring.guru) @@ -219,7 +219,7 @@ B 站上浙江大学的一个数据结构课非常不错,很系统很经典。 有个同学说过一句话,给我的印象特别深刻,就是“有啥解决不了的?只要你肯阅读源码。”羊哥出过一个视频,详细地介绍了如何阅读 JDK 源码,推荐给大家。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/yitiaolong-130bcb8a-38bb-448f-8dc1-4e2af22153d2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/yitiaolong-130bcb8a-38bb-448f-8dc1-4e2af22153d2.png) >视频地址:[https://www.bilibili.com/video/BV1V7411U78L](https://www.bilibili.com/video/BV1V7411U78L) @@ -230,14 +230,14 @@ B 站上浙江大学的一个数据结构课非常不错,很系统很经典。 推荐哈工大的《操作系统》实验课,包括操作系统基础篇、操作系统之进程与线程、操作系统之内存管理、操作系统之外设与文件系统 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/yitiaolong-f5e91f0d-4b30-4693-beab-b879bac237b4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/yitiaolong-f5e91f0d-4b30-4693-beab-b879bac237b4.png) >视频地址:[https://www.bilibili.com/video/BV1js411b7vg](https://www.bilibili.com/video/BV1js411b7vg) 如果想深入学习计算机组成原理的话,推荐北京大学的《计算机组成》公开课,整体评价非常高。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/yitiaolong-6cc5a0d4-7a06-4aca-a9b0-2e8b270d36ba.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/yitiaolong-6cc5a0d4-7a06-4aca-a9b0-2e8b270d36ba.png) >视频地址:[https://www.coursera.org/learn/jisuanji-zucheng](https://www.coursera.org/learn/jisuanji-zucheng) @@ -246,7 +246,7 @@ B 站上浙江大学的一个数据结构课非常不错,很系统很经典。 计算机网络方面的视频我推荐湖科大教书匠的《计算机网络微课堂》,制作得非常用心,是一部不可多得的佳作。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/yitiaolong-0686fbbf-723e-4a0b-8082-3b3577050758.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/yitiaolong-0686fbbf-723e-4a0b-8082-3b3577050758.png) >视频地址:[https://www.bilibili.com/video/BV1c4411d7jb](https://www.bilibili.com/video/BV1c4411d7jb) @@ -281,7 +281,7 @@ B 站上浙江大学的一个数据结构课非常不错,很系统很经典。 最后说一句哈,大家学习 Java,是为了什么,我想大多数不是为了兴趣,是吧?是为了找工作,那么终极的一步,我们需要在找工作之前刷一波面试题,然后找工作的机会也会提升很多。Java 程序员进阶之路上也为大家精心准备了面渣逆袭篇。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/yitiaolong-314e94ce-aa9d-4f09-831b-d1395c4fc07a.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/yitiaolong-314e94ce-aa9d-4f09-831b-d1395c4fc07a.png) 通过上面这幅图就能感受得到,非常全面,非常精彩。 @@ -297,7 +297,7 @@ B 站上浙江大学的一个数据结构课非常不错,很系统很经典。 给大家截图展示一下里面都有哪些优质的 PDF: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/java/java-books.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/java/java-books.jpg) 一家之言,必然有不尽善尽美的地方,但只要能对大家的学习有些许帮助,我也就很满足了! diff --git a/docs/xuexiluxian/os.md b/docs/xuexiluxian/os.md index 75279aa56..bbd0b09b6 100644 --- a/docs/xuexiluxian/os.md +++ b/docs/xuexiluxian/os.md @@ -28,7 +28,7 @@ tag: 国内的推荐哈工大的《操作系统》实验课,以下四部分最为重要。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/os-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/os-1.png) 1)操作系统基础篇 @@ -67,14 +67,14 @@ Linux 内核主要由 5 个模块构成,分别是: 它们之间的依赖关系见下图所示。其实连线代表它们之间的依赖关系,虚线代表 linux-0.11 中还未实现的部分。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/os-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/os-2.png) 由上图可以看得出,所有模块都与进程调度模块存在依赖关系,因为它们都需要进程调度程序来挂起或重新运行它们的进程。 linux-0.11 的学习推荐《Linux 内核完全注释》,该书对 linux-0.11 的全部代码文件进行了详细全面的注释和说明,能够让大家在尽量短的时间内对 Linux 的工作机制有一个全面而深刻的理解,为进一步学习 Linux 系统打下坚实的基础。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/os-3.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/os-3.jpg) 高清 PDF 版在 GitHub 上的一个开源仓库(JavaBooks)里可以找得到(附 linux-0.11 内核的源码): @@ -114,13 +114,13 @@ linux-0.11 的学习推荐《Linux 内核完全注释》,该书对 linux-0.11 下图是课程表,里面有 PDF 和 video 链接🔗。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/os-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/os-4.png) 为了减轻大家的学习压力,我在哔哩哔哩上找到了这门课的克隆版,但中英文字幕的那种。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/os-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/os-5.png) 哇,有了中英文字幕,学起来就舒服多了。课程地址我贴一下: @@ -132,7 +132,7 @@ linux-0.11 的学习推荐《Linux 内核完全注释》,该书对 linux-0.11 >[https://mit-public-courses-cn-translatio.gitbook.io/mit6-s081/](https://mit-public-courses-cn-translatio.gitbook.io/mit6-s081/) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/os-6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/os-6.png) 这门课的一部分会讲操作系统的基本概念,一部分是 lab,几乎每周都会有一些编程实验。 @@ -144,7 +144,7 @@ linux-0.11 的学习推荐《Linux 内核完全注释》,该书对 linux-0.11 >[https://fanxiao.tech/posts/MIT-6S081-notes/#11-processes-and-memory](https://fanxiao.tech/posts/MIT-6S081-notes/#11-processes-and-memory) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/os-7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/os-7.png) 学习 6.S081 这门课的目的有三个: @@ -159,7 +159,7 @@ linux-0.11 的学习推荐《Linux 内核完全注释》,该书对 linux-0.11 **第一份,给操作系统捋条线**,看图感受一下吧。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/os-8.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/os-8.png) 需要的小伙伴请长按识别/扫描下方的二维码关注作者的原创公众号「**沉默王二**」回复关键字「**os**」就可以拉取到了。 @@ -168,7 +168,7 @@ linux-0.11 的学习推荐《Linux 内核完全注释》,该书对 linux-0.11 **第二份,操作系统核心知识点**,同样看图感受一下吧。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/os-9.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/os-9.png) 需要的小伙伴同样可以通过上面的方式获取到。 diff --git a/docs/xuexiluxian/python.md b/docs/xuexiluxian/python.md index 9305cc884..f7be29557 100644 --- a/docs/xuexiluxian/python.md +++ b/docs/xuexiluxian/python.md @@ -10,7 +10,7 @@ tag: 先上一张 Python 知识图谱,直观的了解一下 Python 的基本构成与实际应用。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/python-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/python-1.png) **1)视频篇** @@ -24,26 +24,26 @@ B 站也就是哔哩哔哩,是国内最大的二次元视频网站之一。作 >[https://www.bilibili.com/video/av4050443](https://www.bilibili.com/video/av4050443) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/python-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/python-2.png) 02、Python从入门到精通教程,1165.8万播放。虽然是培训机构出品的,但视频质量还过得去,也比较像大学老师的授课风格,况且还可以白票。前 100 集在讲 Linux 基础,没兴趣的可以直接跳过。 >[https://www.bilibili.com/video/av14184325](https://www.bilibili.com/video/av14184325) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/python-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/python-3.png) 03、Python编程从入门到实践,84.4万播放。虽然播放量一般,内容也一般,但如果想听萝莉小姐姐的声音的话,可以尝试下,听完整个人都治愈了。 >[https://www.bilibili.com/video/av35698354](https://www.bilibili.com/video/av35698354) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/python-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/python-4.png) **2)教程篇** 01、Python 3.10.0 文档,官方文档,毫无疑问是需要学习的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/python-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/python-5.png) >[https://docs.python.org/zh-cn/3/](https://docs.python.org/zh-cn/3/) @@ -51,12 +51,12 @@ B 站也就是哔哩哔哩,是国内最大的二次元视频网站之一。作 >[https://github.com/jackfrued/Python-100-Days](https://github.com/jackfrued/Python-100-Days) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/python-6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/python-6.png) 03、Python Cookbook,比较适合那些想深入理解 Python 语言机制和现代编程风格的有经验的 Python 程序员。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/python-7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/python-7.png) >[https://python3-cookbook.readthedocs.io/zh_CN/latest/index.html](https://python3-cookbook.readthedocs.io/zh_CN/latest/index.html) @@ -67,7 +67,7 @@ B 站也就是哔哩哔哩,是国内最大的二次元视频网站之一。作 - 码云地址:[https://gitee.com/itwanger/JavaBooks](https://gitee.com/itwanger/JavaBooks#python) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/python-books.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/python-books.jpg) 简单罗列一下: diff --git a/docs/xuexiluxian/qianduan.md b/docs/xuexiluxian/qianduan.md index acd727818..16aad8345 100644 --- a/docs/xuexiluxian/qianduan.md +++ b/docs/xuexiluxian/qianduan.md @@ -12,7 +12,7 @@ tag: 最近总有一些读者私信问我:“能推荐一些前端的学习资料吗?”那作为一名正儿八经的全栈型程序员,必须得给大家整一波了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/qianduan-b6d3ccb4-bd13-447e-b88d-f0016b57da49.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/qianduan-b6d3ccb4-bd13-447e-b88d-f0016b57da49.png) ### 语言基础 @@ -38,7 +38,7 @@ tag: 6)📚《JavaScript高级程序设计(第3版)》,这是一本实体书,豆瓣评分高达 9.3 分,是前端程序员进阶必看的书籍。据网友说,他参加阿里面试时的一小半问题都出自于此书。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/qianduan-ea8e074a-9bc4-4780-a4b1-acf855df7bc1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/qianduan-ea8e074a-9bc4-4780-a4b1-acf855df7bc1.png) 7)TypeScript 中文版,TypeScript 是 JavaScript 的超集,也可以编译成普通的 JavaScript 代码,这份中文手册紧跟官方的每个细节,是非常好的一份入门教程。 @@ -98,7 +98,7 @@ tag: 1)📚《高性能网站建设指南》,这本书介绍了网站性能问题的现状、产生的原因,以及改善或解决性能问题的原则、技术技巧和最佳实践 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/qianduan-92d3c42c-c453-473f-aefc-9137c93a939d.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/qianduan-92d3c42c-c453-473f-aefc-9137c93a939d.png) 2)深入了解前端监控原理,这篇文章主要讲了如何进行数据采集和数据上报。 @@ -121,7 +121,7 @@ tag: 2)📚《图解HTTP》,前端工程师对网络协议如果能掌握的比较透彻的话,对整体前后端的沟通是非常有帮助的。像 TCP/IP网络协议、三次握手和四次挥手、DNS的作用、CDN的作用和原理、HTTP、HTTPS、WebSocket 等等这些知识点,我认为都是一名合格的前端工程师应该掌握的。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/qianduan-ae981ec4-ea98-4bb1-a1eb-d8a21a9648cb.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/qianduan-ae981ec4-ea98-4bb1-a1eb-d8a21a9648cb.png) 3)JavaScript 实现的算法和数据结构,附详细解释和刷题指南。大部分前端工程师对数据结构与算法这部分知识都有些欠缺,如果想要突破更高的天花板,这部分知识是必不可少的,非常有用! @@ -134,7 +134,7 @@ tag: - 码云地址:[https://gitee.com/itwanger/JavaBooks](https://gitee.com/itwanger/JavaBooks#javascript) -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/xuexiluxian/qianduan-books.jpg) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/xuexiluxian/qianduan-books.jpg) 简单罗列一下: diff --git a/docs/zhishixingqiu/java-mianshi-zhinan.md b/docs/zhishixingqiu/java-mianshi-zhinan.md index e76acd9c9..b3bc56e48 100644 --- a/docs/zhishixingqiu/java-mianshi-zhinan.md +++ b/docs/zhishixingqiu/java-mianshi-zhinan.md @@ -17,34 +17,34 @@ tag: ## 一、内容概览 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/zhuanlan/java-mianshi-zhinan-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/zhuanlan/java-mianshi-zhinan-1.png) ### 01、面试准备篇 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/zhuanlan/java-mianshi-zhinan-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/zhuanlan/java-mianshi-zhinan-2.png) 如果你能认真按照我这份攻略来准备面试,那绝对事半功倍,面试取得的战果绝对超出你的预期。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/zhuanlan/java-mianshi-zhinan-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/zhuanlan/java-mianshi-zhinan-3.png) ### 02、技术面试篇 技术面试篇里也会给大家带来面试当中常见的问题解答,帮助你的面试当中获得更优异的表现。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/zhuanlan/java-mianshi-zhinan-4.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/zhuanlan/java-mianshi-zhinan-4.png) ### 03、职场修炼篇 当你参加工作后,该如何提高编程能力、提高工作表现、提高个人硬实力呢?职场修炼篇里也会给大家带来硬核的职场建议,十年多的职场经验倾囊相授。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/zhuanlan/java-mianshi-zhinan-5.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/zhuanlan/java-mianshi-zhinan-5.png) ## 二、星球其他资源 **1. 免费下载海量编程学习资料**,包括星主二哥的原创学习资料,数千本个方面的计算机经典电子书,为此,二哥还特意开通了 CSDN 的付费下载会员,可以说,进入星球后,几乎不用再从其他地方费劲找资源下载了。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/zhuanlan/java-mianshi-zhinan-6.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/zhuanlan/java-mianshi-zhinan-6.png) @@ -53,7 +53,7 @@ tag: **2. 编程喵喵🐱实战项目手把手教,带你成为一名公司不可或缺的技术干将**。我们来看一下后端的技术栈和前端的技术栈,主流技术一网打尽。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/zhuanlan/java-mianshi-zhinan-7.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/zhuanlan/java-mianshi-zhinan-7.png) 3. 一对一免费提问交流(有问题你尽管问,回答绝对走心干货,从此不再迷茫彷徨)。 @@ -77,7 +77,7 @@ tag: 透露一个消息,星主二哥去年一共在朋友圈和公众号送出去了近 300 本纸质书(技术书定价一般都是 100 元走上),今年计划全部从**星球内部送,所以幸运的小伙伴直接可以白嫖星球一整年**。 -![这是一部分的记录表](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/zhishixingqiu/readme-10.png) +![这是一部分的记录表](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/zhishixingqiu/readme-10.png) 进入星球后,可以为自己制定一个目标,比如说多长时间内要加入某某心仪的公司,或者达成某某值得炫耀的成就(一定要是还算有点挑战性的目标)。 diff --git a/docs/zookeeper/jibenjieshao.md b/docs/zookeeper/jibenjieshao.md index 88fa3dd20..22693faed 100644 --- a/docs/zookeeper/jibenjieshao.md +++ b/docs/zookeeper/jibenjieshao.md @@ -33,7 +33,7 @@ ZooKeeper 中的数据模型是一种树形结构,非常像电脑中的文件 * ZooKeeper 树中的每一层级用斜杠`(/)`分隔开,且只能用绝对路径(如`get /work/task`)的方式查询 ZooKeeper 节点,而不能使用相对路径。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/zookeeper/shujumoxing-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/zookeeper/shujumoxing-1.png) **「为什么 ZooKeeper 不能采用相对路径查找节点呢?」** @@ -181,7 +181,7 @@ getData(String path, Watcher watcher, Stat stat) 触发通知的条件: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/zookeeper/watch-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/zookeeper/watch-1.png) 上图中列出了客户端在不同会话状态下,相应的在服务器节点所能支持的事件类型。 @@ -336,7 +336,7 @@ ZooKeeper 底层实现的原理,核心的一点就是过期队列这个数据 将会话按照不同的过期时间段分别维护到过期队列之后,在 ZooKeeper 服务运行的过程中,具体的执行过程如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/zookeeper/huihuajizhi-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/zookeeper/huihuajizhi-1.png) 首先,ZooKeeper 服务会开启一个线程专门用来检索过期队列,找出要过期的 bucket,而 ZooKeeper 每次只会让一个 bucket 的会话过期,每当要进行会话过期操作时,ZooKeeper 会唤醒一个处于休眠状态的线程进行会话过期操作,之后会按照上面介绍的操作检索过期队列,取出过期的会话后会执行过期操作。 @@ -773,7 +773,7 @@ ZAB 协议算法(Zookeeper Atomic Broadcast ,Zookeeper 原子广播协议 以 Fast Leader Election 选举的实现方式来讲,如下图所示,一个选票的整体结果可以分为一下六个部分: -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/zookeeper/zab-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/zookeeper/zab-1.png) * logicClock:用来记录服务器的投票轮次。logicClock 会从 1 开始计数,每当该台服务经过一轮投票后,logicClock 的数值就会加 1 。 @@ -800,7 +800,7 @@ ZAB 协议算法(Zookeeper Atomic Broadcast ,Zookeeper 原子广播协议 Follow 服务器进行选票对比的过程,如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/zookeeper/zab-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/zookeeper/zab-2.png) 首先,会对比 logicClock 服务器的投票轮次,当 logicClock 相同时,表明两张选票处于相同的投票阶段,并进入下一阶段,否则跳过。 @@ -817,7 +817,7 @@ Follow 服务器进行选票对比的过程,如下图所示。 ZooKeeper 集群使用原子广播协议进行消息发送,该协议的底层实现过程与二阶段提交过程非常相似,如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/zookeeper/zab-3.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/zookeeper/zab-3.png) 当要在集群中的其他角色服务器进行数据同步的时候,Leader 服务器将该操作过程封装成一个 Proposal 提交事务,并将其发送给集群中其他需要进行数据同步的服务器。 @@ -886,7 +886,7 @@ PurgeTxnLog 方式与 crontab 相比,使用起来更加容易而且也更加 使用子节点,每当有线程来请求锁的时候,便在锁的节点下创建一个子节点,子节点类型必须维护一个顺序,对子节点的自增序号进行排序,默认总是最小的子节点对应的线程获得锁,释放锁时删除对应子节点便可 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/zookeeper/fenbushisuo-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/zookeeper/fenbushisuo-1.png) **「死锁风险:」** @@ -913,7 +913,7 @@ PurgeTxnLog 方式与 crontab 相比,使用起来更加容易而且也更加 这样做还让锁的分配具有公平性,锁定的分配遵循先到先得的原则。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/zookeeper/fenbushisuo-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/zookeeper/fenbushisuo-2.png) **「用 ZooKeeper 实现分布式锁的算法流程,根节点为 /lock:」** @@ -1026,12 +1026,12 @@ PurgeTxnLog 方式与 crontab 相比,使用起来更加容易而且也更加 在它下面创建 servers_host1、servers_host2、servers_host3等临时节点来存储集群中的服务器运行状态信息。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/zookeeper/fuzaijunheng-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/zookeeper/fuzaijunheng-1.png) 整个实现的过程如下图所示。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/zookeeper/fuzaijunheng-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/zookeeper/fuzaijunheng-2.png) * 首先,在接收到客户端的请求后,通过 getData 方法获取服务端 Severs 节点下的服务器列表,其中每个节点信息都存储有当前服务器的连接数。 @@ -1078,7 +1078,7 @@ Dubbo 是阿里巴巴开发的一套开源的技术框架,是一款高性能 该操作是通过 ZooKeeper 服务器在 /consumers 节点路径下创建一个子数据节点,然后再在请求会话中发起对 /providers 节点的 watch 监控 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/zookeeper/shiyonganli-1.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/zookeeper/shiyonganli-1.png) **「Kafka与ZooKeeper」** @@ -1095,7 +1095,7 @@ Dubbo 是阿里巴巴开发的一套开源的技术框架,是一款高性能 为了能够读取这些以分布式方式存储的分区信息,Kafka 会将这些分区信息在 Broker 服务器中的对应关系存储在 ZooKeeper 数据模型的 topic 节点上,每一个 topic 在 ZooKeeper 数据节点上都会以 `/brokers/topics/[topic]` 的形式存在。 -![](https://cdn.jsdelivr.net/gh/itwanger/toBeBetterJavaer/images/zookeeper/shiyonganli-2.png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/images/zookeeper/shiyonganli-2.png)