前言:

如何阅读源码,是每个程序员需要面临的一项挑战,为什么需要阅读源码?从实用性的角度来看,主要有三个目的:

第一,解决手头的新问题或者新需求;

第二,真正理解一部分理论的落地实现;

第三,应对面试。

端正心态

在准备投身到阅读源码的事业之前,首先需要端正一下心态:

  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语言模型那块的代码时很有用。

分清主次。类与类之间呈网状结构,在阅读某个类的时候,不可避免地需要先去阅读它引用的其他类。但是,如果它引用的类很复杂,则建议先放一放,做个标记,回头再读。不过,如果在阅读多个类时,其调用链最终都指向了同一个类,那么这个类就必须先拿下了。

业务为先。如果一个类太过庞大,则先将其中的方法按功能归类,捋清大致的执行流程,接下来再逐个功能地去攻克。

不求甚解。有些方法不需要搞清楚实现过程,只需要了解它的作用。比如一些特定领域的算法,对某些规则的解析等。

以点带面。如果看懂了某一个方法,就要搜索该方法的所有应用之处,验证自己的想法是否正确,并在应用之处写下注释。哪怕理解的有误差也没事,回头有新的理解再批量修改。对于字段的阅读与理解,也建议采取此种方式。

勇于试错。很多接口方法的描述很抽象,在不同的实现类中意义相差很大。此时先弄懂一个类的实现,然后拿着在这个类中的理解去解读另外的实现类,如果解读有误,再逐渐修复。不要指望一次性就能正确地理解某个方法的作用,理解错误,不妨碍继续前进。

留意注释。大部分公开的方法上都有相应的注释,这是快速理解这个方法的重要途径。注释建议拿到谷歌翻译下去阅读,当然,如果能流利阅读英文就更好了。不过,很多时候,注释是令人沮丧的:看完之后完全不知道他在说啥。这个也很正常,因为有些注释中会涉及到很多行业术语或通用解决方案的描述,如果之前没有这些理论背景,大概率是读不懂注释的。原生注释不是万能的,有时候甚至很鸡肋:你不理解这个方法之前,也不理解他的注释,等你理解了这个方法,才会觉得这些注释说得对。因此,我建议留意注释,但别依赖注释,有时候搜索其他网友的理解,再结合自己的阅读,会来的更舒服一些。

勤做笔记。有一点灵感,就需要记录一下,最好是直接记录在源码对应的位置,而且能详细就别简略,好记性终究抵不过烂笔头。

循序渐进。在头脑清醒的时候,打开源码读一读,感觉读不懂的时候,就不要继续死磕了,应该放下干点别的,或者改天再读。我读完一个类,时间跨度可能会超过一个月,这是个不断补充和完善的过程,不可能一次性就搞定。有时候眼看就读懂了,但就是差一点点关键性的理解,这个时候人就容易急,急就容易燥,燥就容易慌,慌就容易乱,乱就容易砸鼠标。所以,一旦觉得遇到瓶颈,那就及时终止吧,因为你可能需要放松大脑,以及补充一些缺失的基础理论了。

最后,感谢您耐心看完了文章!

《大牛到底是如何阅读JDK源码的?》一起来学习一下的更多相关文章

  1. 如何阅读jdk源码?

    简介 这篇文章主要讲述jdk本身的源码该如何阅读,关于各种框架的源码阅读我们后面再一起探讨. 笔者认为阅读源码主要包括下面几个步骤. 设定目标 凡事皆有目的,阅读源码也是一样. 从大的方面来说,我们阅 ...

  2. 如何阅读JDK源码

    JDK源码阅读笔记: https://github.com/kangjianwei/LearningJDK 如何阅读源码,是每个程序员需要面临的一项挑战. 为什么需要阅读源码?从实用性的角度来看,主要 ...

  3. 如何有效的阅读JDK源码

    阅读Java源码的前提条件: 1.技术基础 在阅读源码之前,我们要有一定程度的技术基础的支持. 假如你从来都没有学过Java,也没有其它编程语言的基础,上来就啃<Core Java>,那样 ...

  4. 使用NetBeans、Eclipse阅读JDK源码

    下面说明在Netbeans.Eclipse环境下怎么查看JDK源码: Netbeans: 在"工具->java平台->源"里添加下路径,如果你安装jdk的时候选择安装了 ...

  5. 关于阅读JDK源码的准备

    说明:本篇是给自己看的. 笑 最近突然有冲动 想研究下JDK的源码,搜索了一番,基本上推荐从集合开始,精华部分包括:集合.IO.多线程.网络编程. 虚拟机部分先放一放吧,感觉现在不适合我这种半路出家的 ...

  6. 阅读jdk源码的流程(从今天开始要阅读jdk源码)

    1.java.lang 2.java.util 3.java.util.concurrent 4.java.util.concurrent.atomic 5.java.lang.reflect 6.j ...

  7. JDK源码阅读顺序

      很多java开发的小伙伴都会阅读jdk源码,然而确不知道应该从哪读起.以下为小编整理的通常所需阅读的源码范围. 标题为包名,后面序号为优先级1-4,优先级递减 1.java.lang 1) Obj ...

  8. JDK源码学习--String篇(二) 关于String采用final修饰的思考

    JDK源码学习String篇中,有一处错误,String类用final[不能被改变的]修饰,而我却写成静态的,感谢CTO-淼淼的指正. 风一样的码农提出的String为何采用final的设计,阅读JD ...

  9. Eclipse用法和技巧二十三:查看JDK源码

    使用java开发,如果能阅读JDK的经典代码,对自己的水平提高是很有帮助的.笔者在实际工作中总结了两种阅读JDK源码的方式.第一种下载android源代码,直接在android源码代码中,这里的代码虽 ...

随机推荐

  1. 【译】.NET Core 3.0 发布自包含单体可执行程序

    .NET Core 提供的发布应用程序选项 self-contained 是共享应用程序的好方法,因为应用程序的发布目录包含所有组件.运行时和框架.您只需要告诉使用者应用程序的入口 exe 文件,就可 ...

  2. 【题解】【合并序列(水题)P1628】

    原题链接 这道题目如果连字符串的基本操作都没学建议不要做. 学了的很简单就可以切,所以感觉没什么难度- 主要讲一下在AC基础上的优化(可能算不上剪枝) 很明显,这道题我们要找的是前缀,那么在字符串数组 ...

  3. [leetcode] 11. Container With Most Water (medium)

    原题链接 以Y坐标长度作为木桶边界,以X坐标差为桶底,找出可装多少水. 思路: 前后遍历. Runtime: 5 ms, faster than 95.28% of Java class Soluti ...

  4. LiteDB源码解析系列(3)索引原理详解

    在这一章,我们将了解LiteDB里面几个基本数据结构包括索引结构和数据块结构,我也会试着说明前辈数据之巅在博客中遇到的问题,最后对比mysql进一步深入了解LiteDB的索引原理. 1.LiteDB的 ...

  5. SPC 数据分析工具

    趁着公司在做QMS软件,自己实现一个简易版,类似minitab的工具. 环境:.net framework 4.0 目前提供功能: 数据存储,载入 计量型控制图:单值移动极差图.均值极差图.均值标准差 ...

  6. python 文件读写总结

    这是个人在项目中抽取的代码,自己写的utils的通用模块,使用的框架是tronado,包括了文件的读写操作,api格式的统一函数,如有特别需要可以联系我或者自己扩展,刚学python不久,仅供参考,例 ...

  7. Codeforces Round #554 (Div. 2) C. Neko does Maths (数论 GCD(a,b) = GCD(a,b-a))

    传送门 •题意 给出两个正整数 a,b: 求解 k ,使得 LCM(a+k,b+k) 最小,如果有多个 k 使得 LCM() 最小,输出最小的k: •思路 时隔很久,又重新做这个题 温故果然可以知新❤ ...

  8. HTTP_4_返回结果的HTTP状态码

    状态码:返回请求结果. 状态码种类繁多,以下总结常用的状态码:     类别 信息性状态码 1XX 服务器接受请求,继续处理       成功状态码 200 OK 请求处理成功,并返回资源(响应报文中 ...

  9. textarea的拖拽怎么解决

    textarea文本域在页面中是可以拖动的,即时你给了固定的宽度和高度,但这在我们页面布局中,使我们不需要的,因为可拖拽很多时候会影响我们页面的布局和整体的美观度. css3提供了一个resize属性 ...

  10. Java连载10-数据类型取值范围&转义字符

    一.数据类型取值范围 二.八种数据类型在成员变量中的默认值 (1)成员变量,没有赋值,编译不会报错,系统会自动给赋值 byte\int\short\long默认值为0:float\double默认值为 ...