1. 程式人生 > >如何閱讀JDK原始碼

如何閱讀JDK原始碼

JDK原始碼閱讀筆記:

https://github.com/kangjianwei/LearningJDK

如何閱讀原始碼,是每個程式設計師需要面臨的一項挑戰。

為什麼需要閱讀原始碼?從實用性的角度來看,主要有三個目的:第一,解決手頭的新問題或者新需求;第二,真正理解一部分理論的落地實現;第三,應對面試。

端正心態

在準備投身到閱讀原始碼的事業之前,首先需要端正一下心態:

  1. 閱讀原始碼是一場持久戰,淺嘗輒止的話,頂多能寫幾篇水文,吸點粉糊弄下新手,對自身實力的提高沒有顯著作用。
  2. 閱讀原始碼是錦上添花,不是雪中送炭。如果你進入一個全新的領域,首要的任務是讀文件而不是讀原始碼。
  3. 閱讀原始碼不一定會讓你成為大神。大神有兩個方向:專家型和應用型,無論你的發展偏向哪一方,大量實操才是最根本的保障。
  4. 閱讀原始碼需要理論先行。比如閱讀Socket通訊的程式碼卻不知道TCP/UDP協議,那就像是無頭蒼蠅在亂撞而已。

在此,我更推薦把原始碼閱讀當成是一項興趣愛好去做,就好比有的人通過打遊戲看直播放鬆,有的人通過重新整理聞追熱劇放鬆,還有的人通過找朋友吹牛逼放鬆...不同的人會選擇不同的勞逸結合方式,我更喜歡不寫程式碼的時候,通過看別人的程式碼來放鬆。

作為一名Javaer,朝夕相處的JDK自然是你遇到的第一處寶藏之地。從閱讀JDK程式碼出發,可以深入理解Java的一些新老特性,並學習部分設計模式的應用,以及為將來閱讀更龐大的框架打下紮實的理論基礎與頑強的心理基礎。

選擇工具

工欲善其事必先利其器,起步之前,需要先選擇一款原始碼閱讀工具。在工具的選擇上,同行的建議很多,我大致將其分為四類:

  • 文字型工具(該分類可能會有爭議,不過這不是重點...)
    例如Nodepad++、EditPlus、UEStudio、Sublime、VsCode、Vim等

  • 專家型工具
    例如Source Insight、Understand、OpenGrok(也是很多線上工具的基石)等

  • 線上工具(好幾個線上網站已經掛了)
    例如openjdk、SearchCode等

  • IDE
    例如eclipse/myeclipse、IDEA等

從個人喜好講,我推薦IDEA和UEStudio(搭配UltraFinder)配合使用。

IDEA作為強大的Java生產工具,用來閱讀Java原始碼顯然再合適不過。而UEStudio可以作為臨時檢視Java檔案或者檢視JDK中部分C++程式碼時的選擇,再搭配UltraFinder,實現跨檔案的任意符號搜尋,很實用。

關於閱讀環境的搭建,參見我在https://github.com/kangjianwei/LearningJDK中的描述即可。

閱讀順序

JDK的專案歷經了十幾個大版本,算上開源社群的貢獻,經手的人可能也達到上千人。對於這種龐大的專案,一次性讀完肯定是不可能,必須先找到一個恰當的入口,分模組來一點點啃完。

可能的一種閱讀順序是:

  1. 基本型別的包裝類(Character放在最後)

  2. String、StringBuffer、StringBuilder、StringJoiner、StringTokenizer(補充正則表示式的知識)

  3. CharacterIterator、StringCharacterIterator、CharsetProvider、CharsetEncoder、CharsetDecoder(較難)

  4. java.util.function下的函式表示式

  5. java.nio下的各種Buffer實現

  6. java.lang.ref和jdk.internal.ref下的各種引用:軟引用/弱引用/虛引用

  7. Unsafe的實現(JDK9之後有兩個同名類,一個引用了另一個,建議放在一起閱讀)

  8. java.util.stream下的流式程式設計的實現(很難)

  9. Thread和ThreadLocal

  10. Math、Random、BigInteger、BigDecimal

  11. java.lang.reflect下反射的實現(先掌握JDK 9之後引入的模組系統)

  12. ClassLoader的實現

  13. javax.lang.model下Java語言模型的實現(可以參考Java官方語法文件)

  14. 註解(需要徹底掌握)

  15. Timer、ResourceBundle、Properties

  16. 時間日期型別(尤其是Java8新增的部分)

  17. java.lang.reflect.Proxy, JDK預設的動態代理

  18. java.util.concurrent併發包。先讀原子類,再讀鎖的實現類,最後閱讀那些併發工具的實現(很難)

  19. 集合框架,主要是三大類:List、Set、Map(先讀非執行緒安全的實現,再讀執行緒安全的實現)

  20. 網路程式設計(主要閱讀Socket通訊部分,後續可以閱讀HttpClient的實現)

  21. IO/NIO/BIO(很難)

  22. Files、Path等檔案操作工具類

  23. sql、xml處理類/介面

  24. ......

注意,這裡說的順序只是一個大致的方向,並不代表需要絕對按照這個名單來。

在閱讀某一個程式碼時,往往會牽涉到很多別的程式碼,這個時候就會產生很多閱讀分支,分支的走向,並不在上述名單之內。

善用技巧

閱讀程式碼的技巧,因人而異。就像一千位讀者,就有一千部哈姆雷特,每個人對這件事的看法並不相同。在此,我只談下個人的一些經驗。

  • 理論先行。閱讀某一個模組時,先搜尋它的理論支撐,甚至可以先看別人的閱讀經驗,有了一個大致的了輪廓之後,自己再去實踐。

  • 必須試用。面對一個新的類,最好是先搜尋一下它的基本用法,寫成一個小的示例,並從這個示例中用到的方法入手,去分析這個類。

  • 巧用除錯。關於IDEA中debug的使用方式,超出了本文的講述範圍。值的注意的是,除了需要學習常用的執行時除錯,還需要學習編譯時除錯,這個在閱讀Java語言模型那塊的程式碼時很有用。

  • 分清主次。類與類之間呈網狀結構,在閱讀某個類的時候,不可避免地需要先去閱讀它引用的其他類。但是,如果它引用的類很複雜,則建議先放一放,做個標記,回頭再讀。不過,如果在閱讀多個類時,其呼叫鏈最終都指向了同一個類,那麼這個類就必須先拿下了。

  • 業務為先。如果一個類太過龐大,則先將其中的方法按功能歸類,捋清大致的執行流程,接下來再逐個功能地去攻克。

  • 不求甚解。有些方法不需要搞清楚實現過程,只需要瞭解它的作用。比如一些特定領域的演算法,對某些規則的解析等。

  • 以點帶面。如果看懂了某一個方法,就要搜尋該方法的所有應用之處,驗證自己的想法是否正確,並在應用之處寫下注釋。哪怕理解的有誤差也沒事,回頭有新的理解再批量修改。對於欄位的閱讀與理解,也建議採取此種方式。

  • 勇於試錯。很多介面方法的描述很抽象,在不同的實現類中意義相差很大。此時先弄懂一個類的實現,然後拿著在這個類中的理解去解讀另外的實現類,如果解讀有誤,再逐漸修復。不要指望一次性就能正確地理解某個方法的作用,理解錯誤,不妨礙繼續前進。

  • 留意註釋。大部分公開的方法上都有相應的註釋,這是快速理解這個方法的重要途徑。註釋建議拿到谷歌翻譯下去閱讀,當然,如果能流利閱讀英文就更好了。不過,很多時候,註釋是令人沮喪的:看完之後完全不知道他在說啥。這個也很正常,因為有些註釋中會涉及到很多行業術語或通用解決方案的描述,如果之前沒有這些理論背景,大概率是讀不懂註釋的。原生註釋不是萬能的,有時候甚至很雞肋:你不理解這個方法之前,也不理解他的註釋,等你理解了這個方法,才會覺得這些註釋說得對。因此,我建議留意註釋,但別依賴註釋,有時候搜尋其他網友的理解,再結合自己的閱讀,會來的更舒服一些。

  • 勤做筆記。有一點靈感,就需要記錄一下,最好是直接記錄在原始碼對應的位置,而且能詳細就別簡略,好記性終究抵不過爛筆頭。

  • 循序漸進。在頭腦清醒的時候,開啟原始碼讀一讀,感覺讀不懂的時候,就不要繼續死磕了,應該放下乾點別的,或者改天再讀。我讀完一個類,時間跨度可能會超過一個月,這是個不斷補充和完善的過程,不可能一次性就搞定。有時候眼看就讀懂了,但就是差一點點關鍵性的理解,這個時候人就容易急,急就容易燥,燥就容易慌,慌就容易亂,亂就容易砸滑鼠。所以,一旦覺得遇到瓶頸,那就及時終止吧,因為你可能需要放鬆大腦,以及補充一些缺失的基礎理論了。