JDK源碼閱讀筆記:html
如何閱讀源碼,是每一個程序員須要面臨的一項挑戰。git
爲何須要閱讀源碼?從實用性的角度來看,主要有三個目的:第一,解決手頭的新問題或者新需求;第二,真正理解一部分理論的落地實現;第三,應對面試。程序員
在準備投身到閱讀源碼的事業以前,首先須要端正一下心態:github
在此,我更推薦把源碼閱讀當成是一項興趣愛好去作,就比如有的人經過打遊戲看直播放鬆,有的人經過刷新聞追熱劇放鬆,還有的人經過找朋友吹牛逼放鬆...不一樣的人會選擇不一樣的勞逸結合方式,我更喜歡不寫代碼的時候,經過看別人的代碼來放鬆。面試
做爲一名Javaer,朝夕相處的JDK天然是你遇到的第一處寶藏之地。從閱讀JDK代碼出發,能夠深刻理解Java的一些新老特性,並學習部分設計模式的應用,以及爲未來閱讀更龐大的框架打下紮實的理論基礎與頑強的心理基礎。正則表達式
工欲善其事必先利其器,起步以前,須要先選擇一款源碼閱讀工具。在工具的選擇上,同行的建議不少,我大體將其分爲四類:算法
文本型工具(該分類可能會有爭議,不過這不是重點...)
例如Nodepad++、EditPlus、UEStudio、Sublime、VsCode、Vim等sql
專家型工具
例如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的項目歷經了十幾個大版本,算上開源社區的貢獻,經手的人可能也達到上千人。對於這種龐大的項目,一次性讀完確定是不可能,必須先找到一個恰當的入口,分模塊來一點點啃完。
可能的一種閱讀順序是:
基本類型的包裝類(Character放在最後)
String、StringBuffer、StringBuilder、StringJoiner、StringTokenizer(補充正則表達式的知識)
CharacterIterator、StringCharacterIterator、CharsetProvider、CharsetEncoder、CharsetDecoder(較難)
java.util.function下的函數表達式
java.nio下的各類Buffer實現
java.lang.ref和jdk.internal.ref下的各類引用:軟引用/弱引用/虛引用
Unsafe的實現(JDK9以後有兩個同名類,一個引用了另外一個,建議放在一塊兒閱讀)
java.util.stream下的流式編程的實現(很難)
Thread和ThreadLocal
Math、Random、BigInteger、BigDecimal
java.lang.reflect下反射的實現(先掌握JDK 9以後引入的模塊系統)
ClassLoader的實現
javax.lang.model下Java語言模型的實現(能夠參考Java官方語法文檔)
註解(須要完全掌握)
Timer、ResourceBundle、Properties
時間日期類型(尤爲是Java8新增的部分)
java.lang.reflect.Proxy, JDK默認的動態代理
java.util.concurrent併發包。先讀原子類,再讀鎖的實現類,最後閱讀那些併發工具的實現(很難)
集合框架,主要是三大類:List、Set、Map(先讀非線程安全的實現,再讀線程安全的實現)
網絡編程(主要閱讀Socket通訊部分,後續能夠閱讀HttpClient的實現)
IO,包含BIO/NIO/AIO(很難)
Files、Path等文件操做工具類
sql、xml處理類/接口
......
注意,這裏說的順序只是一個大體的方向,並不表明須要絕對按照這個名單來。
在閱讀某一個代碼時,每每會牽涉到不少別的代碼,這個時候就會產生不少閱讀分支,分支的走向,並不在上述名單以內。
閱讀代碼的技巧,因人而異。就像一千位讀者,就有一千部哈姆雷特,每一個人對這件事的見解並不相同。在此,我只談下我的的一些經驗。
理論先行。閱讀某一個模塊時,先搜索它的理論支撐,甚至能夠先看別人的閱讀經驗,有了一個大體的了輪廓以後,本身再去實踐。
必須試用。面對一個新的類,最好是先搜索一下它的基本用法,寫成一個小的示例,並從這個示例中用到的方法入手,去分析這個類。
巧用調試。關於IDEA中debug的使用方式,超出了本文的講述範圍。值的注意的是,除了須要學習經常使用的運行時調試,還須要學習編譯時調試,這個在閱讀Java語言模型那塊的代碼時頗有用。
分清主次。類與類之間呈網狀結構,在閱讀某個類的時候,不可避免地須要先去閱讀它引用的其餘類。可是,若是它引用的類很複雜,則建議先放一放,作個標記,回頭再讀。不過,若是在閱讀多個類時,其調用鏈最終都指向了同一個類,那麼這個類就必須先拿下了。
業務爲先。若是一個類太過龐大,則先將其中的方法按功能歸類,捋清大體的執行流程,接下來再逐個功能地去攻克。
不求甚解。有些方法不須要搞清楚實現過程,只須要了解它的做用。好比一些特定領域的算法,對某些規則的解析等。
以點帶面。若是看懂了某一個方法,就要搜索該方法的全部應用之處,驗證本身的想法是否正確,並在應用之處寫下注釋。哪怕理解的有偏差也沒事,回頭有新的理解再批量修改。對於字段的閱讀與理解,也建議採起此種方式。
敢於試錯。不少接口方法的描述很抽象,在不一樣的實現類中意義相差很大。此時先弄懂一個類的實現,而後拿着在這個類中的理解去解讀另外的實現類,若是解讀有誤,再逐漸修復。不要期望一次性就能正確地理解某個方法的做用,理解錯誤,不妨礙繼續前進。
留意註釋。大部分公開的方法上都有相應的註釋,這是快速理解這個方法的重要途徑。註釋建議拿到谷歌翻譯下去閱讀,固然,若是能流利閱讀英文就更好了。不過,不少時候,註釋是使人沮喪的:看完以後徹底不知道他在說啥。這個也很正常,由於有些註釋中會涉及到不少行業術語或通用解決方案的描述,若是以前沒有這些理論背景,大機率是讀不懂註釋的。原生註釋不是萬能的,有時候甚至很雞肋:你不理解這個方法以前,也不理解他的註釋,等你理解了這個方法,纔會以爲這些註釋說得對。所以,我建議留意註釋,但別依賴註釋,有時候搜索其餘網友的理解,再結合本身的閱讀,會來的更舒服一些。
勤作筆記。有一點靈感,就須要記錄一下,最好是直接記錄在源碼對應的位置,並且能詳細就別簡略,好記性終究抵不過爛筆頭。
按部就班。在頭腦清醒的時候,打開源碼讀一讀,感受讀不懂的時候,就不要繼續死磕了,應該放下乾點別的,或者改天再讀。我讀完一個類,時間跨度可能會超過一個月,這是個不斷補充和完善的過程,不可能一次性就搞定。有時候眼看就讀懂了,但就是差一點點關鍵性的理解,這個時候人就容易急,急就容易燥,燥就容易慌,慌就容易亂,亂就容易砸鼠標。因此,一旦以爲遇到瓶頸,那就及時終止吧,由於你可能須要放鬆大腦,以及補充一些缺失的基礎理論了。