《深入理解Java虛擬機器》第 3 版裡面到底多了哪些知識點?本文竟然得到了本書作者的認可!
這是why的第 47 篇原創文章
荒腔走板
大家好,我是 why。老規矩,先是簡短的荒腔走板聊聊生活。
上面的圖是前幾天拍的,那天晚上下班後,剛剛走進小區就看到了這一輪彎月和旁邊那一顆特別特別亮的星星。
不知道為什麼,一瞬間,我感覺一下回到了小時候的夏天。
腦海裡浮現出了一些畫面:一個大鐵勺子舀一勺透心涼的井水、經過水缸冰鎮的西瓜、樹蔭下賣櫻桃的老爺爺、小河邊嬉笑著抓螃蟹的同伴、提著一杯涼白開走在上學路上的自己,還有傍晚在頂樓納涼的鄰居們。
我小時候是在筒子樓長大的,一棟樓 7 層高,每一層住 10 來戶人家。
那個時候沒有誰家裡有冰箱,但是家家戶戶都有一個大水缸。夏天,買一個西瓜回來,衝乾淨後扔在大水缸裡面,晚上吃的時候,甭說多帶勁了。
冰箱都沒有,更別說空調了。所以到了夏天非常熱,僅僅是靠一個吊扇是帶來不了絲毫涼意的。大家就會不約而同的帶著涼蓆、枕頭、薄毯和蒲扇去頂樓睡一晚上。
而樓上樓下的鄰居大多都熟識,大家就在頂樓上相互走動,坐在涼蓆上拉家常。
我特別喜歡在頂樓睡,在頂樓上睡的時候可以看到很多星星,還能聽到窸窸窣窣的蟲叫。
睡的時候爺爺還會給我講故事,看到流星劃過的時候記得許願是爺爺告訴的我的。他還告訴我當一個人降生在這個世上,天上就會點亮一盞燈,這就是我們看到的星星。如果這個人要離開世界了,那麼他的星星就掉落了,這個就是流星。
現在回想起來,我都是帶著笑意的,我喜歡小時候的夏天。
好了,說迴文章。
先說結論
自從《深入理解Java虛擬機器(第 3 版)》發行後,我看到了很多人提出了相同的問題:第三版值不值得買?
第二版和第三版我都看了,所以文字就是站在我個人的角來回答這個問題的。
《深入理解Java虛擬機器(第 3 版)》,我是 19 年 12 月底買的。到現在基本上算是看完了。
現在,我終於有資格來回答類似這樣的問題了:
也可以把之前說的這句話給實現了:
先說結論:個人覺得很有必要。
據我分析,目前大概就是三類情況(前提都是你想看這個書)。
1.沒有看過第二版。這種情況非常簡單了,直接買第三版看就行了。沒啥說的。
2.買了第二版,但是沒翻過幾次,甚至還沒開封的。這種情況,我建議你直接把第二版收藏起來,買一本第三版回來看。不要捨不得還沒翻過幾次的第二版,就把時間“浪費”在第二版上。因為第三版的內容是包含了第二版,且對第二版進行了補充說明的,相當於是第二版 + 番外篇。所以,從時間收益的角度來說,直接看第三版是划算的。
3.之前買了第二版,也看過第二版的。我就屬於這種情況,我的建議是看過第二版了,一定要再看看第三版,你就會知道,作者這本書出到第三版,不是圈錢的,是真的有乾貨,是真的在對知識進行更新。這是一種對技術的敬畏、對自己無愧、對讀者的負責的態度。
這本書 2011 年出的第一版可以說是填補了國內 JVM 的一塊空白。
2013年出的第二版可以說是推動了 JVM 在國內的普及度。
2019 年出的第三版在我看來更多的是反映出技術的更新換代之快速,在提醒著我們:今日不學習,明日變垃圾。
我這麼說吧:要是沒有這本書,也就沒有這麼多 JVM 的面試題。但要是真沒有這本書,你的技術鏈上,會缺少了非常重要的一環。
同時,像書中的前言部分說的一句話:通常情況下,一個程式設計師只要瞭解了必要的 Java 類庫 API、Java 語法,學習適當的第三方開發框架,就已經基本滿足日常開發的需求了。
換言之就是,你如果不想當個普通的程式設計師,你應該瞭解一下書中提到的知識。
你細細的品一下,你是要面試題,還是要當個普通程式設計師,還是要技術的完善度?
第三版我也是期待已久:
說實在的,在第三版沒出來之前,我也不知道有哪些內容。主要是第二版讓我打開了 JVM 這扇大門,學習到了很多硬核知識、內功心法,第三版不論怎麼樣我都要支援一下。
現在看來,第三版並沒有讓我失望,它值得我這樣去支援。
接下來,我就站在我自己的角度,給你詳細的對比一下第二版和第三版的區別。
第三版 VS 第二版
首先,我們來個視覺上的直觀對比:
如上圖所示,第三版的一眼就看出來比第二版厚了不少。具體尺寸是什麼呢。
我給大家量了一下。第二版厚度 2 釐米,而第三版厚度 2.7 釐米,比第二版足足多出來 7 毫米。
第二版 433 頁,第三版 521 頁,足足多出來了 88 頁。
多了 88 頁啊。你知道這意味著什麼嗎?
意味著關於 JVM 的面試題庫,又豐富了近 10 萬字啊。
再看看封面上的文字:
第二版的封面上赫然寫著:基於最新JDK 1.7。
第二版周志明先生是 2011 年到 2012 年間寫出來的,正式出版時間是 2013 年 9 月。而彼時,JDK 版本的最新版本是 1.7。
第三版的出版時間是 2019 年底,封面上說是:根據 JDK 新版本全面升級。
而當時,JDK 版本的最新版本是 13。
我寫文章的此時,已經是 2020 年 5 月。2020 年 3 月 17 日,Oracle 公司正式釋出了 JDK 14。
2011 年到 2020 年,這近 10 年的時間。
首先,JDK 版本的命名規則發生了變化。在 2018 年 3 月 JDK 10 版本釋出後,JDK 的開發版本號棄用了以前的 1.x 的命名規則。
所以,我們也慢慢的從 JDK 1.7 改口變成了 JDK 7。
其次,雖然 2009 年 4 月 Oracle 公司就收購了 Sun 公司,Java 商標正式歸 Oracle 所有。
但是在我的印象中,我讀大學的時候,說到 Java 我更多的還是會想到 Sun 公司。而現在,一說到 Java,我的第一反應已經是從 sun 到變成了 Oracle。
這 10 年間,名字變了,版本號從 7 變成了 14 ,翻了一倍。甚至連所屬公司都變了。
真的是,白雲蒼狗,換了人間。
好了,我們接著說說書裡面的內容吧。
這兩個版本的書都劃分為五大部分:
1.走進 Java
2.自動記憶體管理
3.虛擬機器執行子系統
4.程式編譯與程式碼優化
5.高效併發
所以,我接下來的內容就主要順著這五大部分去講第三版對於第二版這五大部分增加了什麼內容。
1.走進 Java
這一部分的內容主要就是 Java 技術的發展史。
就像我前面說的,第二版和第三版之間差的這近 10 年的時間,Java 已經發生了很多大事件,包括一些吃瓜群眾喜聞樂見的事情。
比如, Android 對於 Java 的侵權案件在 2018 年塵埃落定, Google 賠償了 Oracle 88 億美金。
Google 心裡苦啊。因為其實在 2017 年 Kotlin 就取代 Java 成為 Google 官方的 Android 一級開發語言了。未來取代 Android 的 Fuchsia 系統,更是和 Java 無瓜葛。
88 億美金,心裡苦啊。
還有一件吃瓜的事情就是 2018 年 9 月 25 日,JDK 11 釋出。同時帶來的一個轟動的謠言:Java 要開始收費啦!
一石激起千層浪,一謠言振奮了無數的標題黨。
各大科技自媒體拿著鍵盤就衝進了戰場,各種文章鋪天蓋地的而來。
也許我們當時都是拿不準結論的吃瓜群眾,但是兩年快過去了,我不需要說結果了,因為時間給了你答案。
上面寫的兩個事情都是我在第三版的書裡面看來的,第二版沒有這些內容。
這是第二版這一部分的 Java 技術發展的時間線插圖,其實只寫到了 2011-07-28,之後的內容都是展望:
這是第三版的插圖:
同時這一部分還有一個小節是:展望 Java 技術的未來。
在第二版中,作者站在 2011 年展望的模組化、混合語言、多核並行、豐富語法、64 位虛擬機器......這些東西都如約而至了。
在第三版中,作者是這樣展望的:
可以說,我們站在 2020 年的時間節點上,往前再看 10 年,Java 的未來可期,我們的未來也同樣一定是白雲蒼狗,換了人間。
但是是往好的方向還是不盡人意的方向......這個就不好說。
哈哈哈,好了,下一部分。
2.自動記憶體管理
這一部分可以說是全書最重要、最 JVM 的部分,沒有之一。
我猶記得當年第一次看這書看到這部分的時候首先看到的一句話:
牆外的人想進去,牆裡的人想出來。
我看完之後,屬於牆縫中一臉懵逼的人,不知道是應該進去還是應該出來。
這一部分是我做筆記,劃線最多的一部分,基本上感覺是每句話都是考點,所以看的時候出現了很多類似於下面這種情況,一頁全是劃的線,因為感覺全是面試題:
這一部分是書中的重中之重,所以周志明先生也是在第三版中該部分下足了功夫。
這一部分又分為了 2 到 5 章。都很重要,所以,我們一章章的說。
首先第 2 章《Java 記憶體區域與記憶體溢位異常》,一上來就是王炸,我給你看張圖你就明白了:
這一部分的開篇就丟擲了這個經久不衰的“面試圖”,你想想多少面試題都是從 這個圖出發,然後問到你懷疑人生的?
而這一部分主要是圍繞這幾塊區域來寫,所以,很重要。
第三版相對於第二版而言,對很多地方進行了更加細緻的補充說明,比如下面的兩個版本對於 Java 堆的描述(左邊第三版、右邊第二版,我拍的,盡力了,可以點開看大圖):
可以看到,在第二版中我框起來,並且用黑線標記了的地方,在第三版裡面對於這些名詞和區域劃分進行了相當長的一段補充說明,確實是嚴謹了很多。
其他的區域描述變化不大,就不一一說明了。
但是有個變化是不得不提的,那就是在 JDK 8 裡面已經沒有了永久代的概念了,取而代之的是元空間的說法。
所以,在後面模擬方法區的溢位情況時,第三版專門新增瞭如下內容:
接下來,我們看看第3 章《垃圾收集器與記憶體分配策略》:
這部分,怎麼說呢,簡直哇塞了,重中之重的重中之重。
第二版裡面看到 G1 收集器的時候,裡面很多東西都沒有講明白。看完之後,也就只是落下了 G1 把記憶體“化整為零”的思想,感覺和前面介紹的幾個垃圾回收器有顯著的區別。
然後就剩下一些看不懂但是又感覺很重要的記憶集、卡表、寫屏障這些名詞。
這是可以理解的,周志明先生寫第二版的時候, JDK 7 才剛剛釋出沒多久,G1 收集器還停留在實驗室階段,沒有實際生產環境的資料去支撐。所以很多東西就寫的比較模糊。
而在第三版中,對這一部分的內容我看了、也進行對比了,可以說是完全重新寫了這一部分。
然後第三版中,還濃墨重彩的介紹了 JDK 11 和 JDK 12 中新出現的 ZGC 和 Shenandoah 這兩款低延遲、全併發收集器的詳細原理解析。
同時還簡短的介紹了“標新立異“的不進行垃圾回收的垃圾回收器 Epsilon 。
Shenandoah 和 Epsilon 我就不說了。ZGC 很有可能是下一批次的高頻面試題,因為據官方態度,這就是垃圾收集器未來的發展方向。
這裡就不展開說明了,總之很值得去學習一下。
除了垃圾收集器這部分對於 G1 的內容重寫,新增 ZGC、Shenandoah、Epsilon 內容外。
這一章節還新增了記憶集和卡表(用於解決跨代引用)、三色標記(引出併發標記問題)、增量更新和原始快照(為了解決併發標記問題)、寫屏障(增量更新和原始快照的實現原理)這些理論知識。
有了這些理論知識,對於 HotSpot 的設計的理解就更加容易一點。而這些,在第二版中是沒有的。
好了,看一下兩個版本第三章的目錄對比吧(左邊第三版,右邊第二版):
左邊第三版我用紅框框起來的部分都是新增的內容。
第 4 章《虛擬機器效能監控、故障處理工具》。這一章節是最實用的一小節。
兩個版本都主要介紹了 JDK 自帶的 6 個命令列工具,他們主要是:
jps:虛擬機器程序狀況工具。
jstat:虛擬機器統計資訊監視工具。
jinfo:Java 配置資訊工具。
jmap:Java 記憶體映像工具。
jhat:虛擬機器堆轉儲快照分析工具。
jstack:Java 堆疊跟蹤工具。
第三版和第二版在這一章節的主要差異在於 JDK 視覺化工具的介紹。
除了 JConsole、VisualVM 這兩款工具外,第三版還額外介紹了 JDK 9 中新增的 JHSDB 除錯工具:
然後還介紹了 Java Mission Control 這款可以持續線上的監控工具,工具的介面也是比較炫酷的:
它生成的報告包含下面幾類資訊:
然後還介紹了 HSDIS:JIT 生成程式碼反彙編。
這個工具還是很有用的,我之前寫文章的時候分析程式碼就用到過,加上幾個啟動引數就可以輸出組合語言。
配合 JITWatch 工具,有奇效。可以直接看到 Java 原始碼、位元組碼和即時編譯生成的彙編程式碼,大概就是下面這個圖的效果:
類似於普通人看程式碼,看到的是表層。用這個工具看程式碼,你可以看到程式的奇經八脈。
第 5 章《調優案例分析與實戰》。
這一節中的幾個案例分析,都是需要細細品讀的,能從別人的實戰中學到很多東西,前面的內容掌握了之後,看完案例你也會說一句:害,就這啊,學習了。
最後的對於 Eclipse 執行速度的調優,我的記憶也是很深刻的,因為當年第一次讀到這裡的時候就在我的電腦上實戰了一番,確實是有些效果的。
3.虛擬機器執行子系統
這一部分,首先講了類檔案的結構,無數英雄好漢都是在這個地方被勸退的。
第一次看完之後我也就記住了個 0xCAFEBABE 了。
什麼欄位表集合、方法表集合、屬性表集合、各種位元組碼,統統都是關上書立馬忘記的。
但是後來我發現了,這一部分的內容,沒必要去記得那麼牢吧。只要知道有這麼一個東西,然後當遇到這個知識點的時候知道來這裡翻一下書,就可以了。
換言之,這個地方可以當做工具書來看。
在類檔案結構講解完成後,進入了虛擬機器類載入機制,我相信無數人接觸“雙親委派模型”就是從這裡開始的吧。
這一部分,第三版主要引入了 JDK 9 的模組化系統介紹。
由於 Java 模組化系統的引入,導致對類載入部分產生了巨大的影響。
如果說雙親委派模式在此之前有三次被破壞,分別是:
第一次是由於該模式是 JDK 1.2 之後引入的,導致的歷史遺留問題。
第二次是模型自身缺陷,導致不能載入 SPI 介面。
第三次是為了追求程式碼熱替換(Hot Swap)。
那麼 JDK 9 的模組化就是對於雙親委派模式的第四次破壞。
可以給你對比一下:
你可以先自己品一品。具體的內容,去翻書吧。
4.程式編譯與程式碼優化
這一部分在第三版中著重重新寫了泛型的部分,描述了 Java 與 C# 裡的泛型實現的方法,同時介紹了泛型出現的歷史背景。當然,還有提到泛型就不得不提到的型別擦除。
語法糖,也是我在這本書的這一章節中學到的詞。
前段時間很火的面試題:Java 所有的物件例項都在堆中分配記憶體,對嗎?
在這一章節也有答案:
但是要特殊說明的是,需要注意書中的註腳:
其實你翻回去看書中對 Java 堆的介紹:
**我做筆記的地方,書中寫的是“幾乎”、“不那麼絕對”。你就知道,此事必有蹊蹺。 **
然後這一部分的第 11 章,名稱發生了變化,從晚期(執行期)優化到後端編譯與優化。(左邊第二版,右邊第三版)
同時,可以看到第三版新增了一個小節專門講提前編譯器的,分析了提前編譯的優劣得失。
第三版還多了一個深入理解 Graal 的實戰。
這玩意就厲害了,之前寫文章的時候準備階段也用過這個工具。用作者的話說:寫到這裡,筆者忍不住感慨, Graal 編譯器的出現對學習和研究虛擬機器程式碼編譯技術實在有著不可估量的價值。
5.高效併發
這一部分的內容主要是講 java 記憶體模型(JMM)和執行緒以及執行緒安全和鎖優化部分。
這一部分由於變化不大,我就不做過多的介紹了,其實我覺得這部分的內容和 JVM 已經不是緊密相關了,只是一個擴充內容。
如果需要更多的瞭解多執行緒程式設計的技術,還是需要去閱讀其他的專門描述多執行緒程式設計的書籍。
第三版在這個部分,多介紹了 Java 未來基於協程的新併發模型。
Java 支援協程應該是非常困難的了。比如說個簡單的:協程中遇到傳統的執行緒同步措施應該怎麼去處理?
很難去解決這種棘手的問題。
但是 Java 的解決方案是纖程。具體是什麼我就不說了,有興趣的去翻翻書吧。
其實這塊不知道我覺得影響也不大,因為在我短短的程式設計師職業生涯中,我覺得我應該還不會和纖程打交道。屬於有生之年系列。
如果你知道了呢?
那麼恭喜你,朋友,又學到了一個基本上用不到的知識點。
好了,上面的內容,我從五大部分去分析了第三版的新增內容。
接下來,我送你書中我發現的 5 個彩蛋吧。
五個彩蛋
當然,這五個彩蛋是對我而言的,從我個人的角度出發,對我是彩蛋,每個人的看法都不一樣。
先說明一下,我的第二版是 2016 年 5 月第 13 次印刷。第三版是 2020 年 1 月第 1 次印刷。印刷版本不一樣,可能有細微差別。
彩蛋一
在書的開篇。
第二版說:時至今日,Java 技術體系已經吸引了 900 多萬軟體開發者,這是全球最大的軟體開發團隊。
第三版說:時至今日,Java 技術體系已經吸引了 600 多萬軟體開發者,這是全球最大的軟體開發團隊。
第二版寫於 2011 年,第三版寫於 2019 年,短短 8 年的時間,少了 300 多萬從業者。
作者說他的資料來源於 Java 的廣告詞。我不知道是統計維度的變化導致的 300 多萬的差距,還是真的就是少了這麼多人。
彩蛋二
2.4.3 小節(方法區和執行時常量池溢位):
彩蛋藏在第三版的註腳中。為此,我還專門寫過一篇文章,有興趣的可以去閱讀一下。
《深入理解Java虛擬機器》第2版挖的坑終於在第3版中被R大填平了
彩蛋三
在第 3 章《垃圾收集器與記憶體分配策略》中第二版和第三版中一個描述不一樣(上面是第二版,下面是第三版):
很明顯,上面第二版的描述中對於 Full GC 的描述是錯誤的。
但是巧就巧在我一次面試的過程中,一個面試者說 Full GC 是指老年代 GC,說的那叫一個信誓旦旦。他說在書裡面看到的。
我回來一翻書,發現還真是這樣的,可惜這個地方寫的有點問題,在第三版中進行了重新描述,Full GC 一定是整堆收集的,朋友們。
後面也有人在我的文章中評論過相關問題,我也是用這個彩蛋回答的。
彩蛋四
第五部分,關於執行緒狀態的描述,第二版中有個筆誤,把 6 種狀態寫成了 5 種,第三版中修復了過來(下圖中,上面是第二版,下面是第三版):
但是我發現第三版的狀態圖畫的又有問題了(左邊第三版,右邊第二版):
第二版的狀態圖是沒有問題的,第三版中:New 到 Running 之間和 Running 到 Terminated 之間是雙箭頭,這是不對的。
彩蛋五
第三版,312頁,在講動態分派的知識點的時候有這麼一處示例程式碼:
很明顯,作者想寫的是 guy,手抖打成了 gay 。問題不大,就是有點怪怪的。
一個筆誤帶來的小彩蛋。
我與JVM的故事
然後,分享一下我和 JVM 之間的故事吧。
毫不誇張的說,我剛剛畢業的時候對於 JVM 是完全不瞭解的。僅限於聽說過這個詞。
大三的時候在外實習了一年,寫了一年的 CRUD (增刪改查),做了兩個外包的政府專案,其實所有的需求都不復雜,都是可以通過增刪改查幾個介面的組合實現的。
所以一年以後,我覺得程式設計師好像也不是那麼難,CRUD 寫的那叫一個爐火純青。膨脹到覺得靠著這一身 CRUD 的硬本領,我可以去北京闖一闖。
幸好,2016 年找工作的時候 JVM 的面試題感覺好像還不是特別的普及,至少對於一個剛剛畢業的應屆生而言,所以面試了好幾家公司,都沒有問一個 JVM 相關的試題。
後來,我比較順利的進入了一家公司。加入這家公司的兩個月後,就是長達 7 天的國慶節。
在國慶之前,我買了這本書。國慶七天沒有出門,把第二版擼完了一遍。
啃的是異常的艱難,可以說是囫圇吞棗。很多東西完全是靠死記硬背,開啟書,什麼都知道。一合上書:剛剛看的是啥來著?
我相信很多朋友第一次看這書的時候和我的感覺都差不多。現在回想起來都是很正常的,都說了是內功心法了,僅僅不到一年的工作經驗很難去修煉這種上乘武功的。
只是隨著工作經驗的增加,加上對於不懂的地方我進行了反覆閱讀,最後才算是懵懵懂懂,感覺是吸收了一點點,可以應付一下面試了。
等到後面通讀了幾遍後。你會發現,這書就算你讀了百遍,其意也沒有自見。僅僅是帶你進入了 JVM 的大門而已。
當然,我不是說這本書不好,而是 JVM 本來就是一個很有難度的學科。不是你讀一兩本書就能搞明白的,它僅僅是一個帶你入門的角色。
門裡面的世界,與我而言,深不見底。
比如我之前寫過幾篇 JVM 相關的文章,後來有讀者來問我由文章延伸出來的問題,我也不知道:
我寫的時候接觸到了這部分的知識,但是翻閱了一下相關資料,發現是異常複雜的,所以我也就沒有深入研究下去了。
我舉這個例子的目的是說:JVM 是學無止境的,當然你背幾道面試題也是能夠應付過去的。但是你真的不想真正的瞭解的更多一點嗎?武林祕籍就擺在你眼前,你不想去翻一翻嗎?
其實,從我入行到現在,4 年時間,每年我都能看到“新”的 JVM 的文章出現。而這些“新”的 JVM 文章裡面裝的都是一些老酒。
當然我不是說這樣不好。這樣沒有問題,每年的“新”文章都是別人看了相關知識後自己總結出來的,分享了出來,而我們看到了也是可以溫故而知新的,這樣是雙贏。
只是,我們為什麼不直接從源頭看起呢。源頭說來說去也不外乎就那幾本書而已,其中就包含文章說的這本。
最後分享一下我看這書的一個小小的轉變吧:
2016 年我第一次讀這書的時候,雖然看不懂,但是覺得這簡直就是聖經啊!
2020 年的現在,我有了一點工作經驗,讀了幾次這書後,衍生了其他的知識點後,才發現,這本書其實就是個科普入門級別而已。
沒有不敬的意思,而是 JVM 真的是一門博大精深的學問。而任何一門博大精深的學問都不可能是你看一兩本書就能掌握的,只能說是入門。
而且永遠也別想著讀一遍就能把這本吸收完,說實在的,你能完整的讀完一遍就算不錯了。
當你凝望深淵的時候,深淵也在看著你。當你學習 JVM 的時候,JVM 也在拉著你“越陷越深”。
總之,我還是很推薦購買的。
最後說一句
這篇文章感覺像是這本書的軟文似的,但是沒有關係,我就是這本書的自來水,從中學習、收穫了很多。總體上來說,這是一本非常值得被更多人知道的書。
哦,對了。這篇文章在某平臺首發之後《深入理解Java虛擬機器》的作者周志明先生也看到了,並進行了讚賞。
我的天啊!本人的高光時刻,這是莫大的鼓勵!
點個贊吧,周更很累的,不要白嫖我,需要一點正反饋。
才疏學淺,難免會有紕漏,如果你發現了錯誤的地方,還請你留言給我指出來,我對其加以修改。(我每篇技術文章都有這句話,我是認真的說的。)
感謝您的閱讀,我堅持原創,十分歡迎並感謝您的關注。
我是 why,一個僅僅寫了幾篇文章的程式猿,不是大佬,但是喜歡分享,是一個又暖又有料的四川好男人。
歡迎關注公眾號【why技術】,堅持輸出原創。分享技術、品味生活,願你我共同進步。
本文使用 mdnice 排版