【转】看源代码那些事
原文网址:http://blog.csdn.net/zengraoli/article/details/8909734
1. 前言
很多人问我如何看源代码?是不是我在看源代码这方面特别有天赋?
其实不是的,我也只是个普通人,跟大伙没啥分别,
只不过我没有别的特别爱好,一有空时,不是写自己的代码就是看别人的代码,
我在看源代码时比较有耐心,纯粹就是兴趣驱动,或者说是一种好奇心。
当然,我不会随随便便拿起一个开源项目就看,而是经过一定了解后才决定看它的源代码的,
一旦决定要看了,我至少要把这个开源项目80%以上的代码看完,并不是那种肤浅的看,
而是仔细研究每一行代码。
2. 我看过这些开源项目
按时间先后顺序:
2007:
OpenJDK Javac1.7
2008:
Erlang编译器 (看得最少的一个,只看了一半源代码)
2009:
Tomcat6
Junit4
Ibatis2.3
OSCache2.4
Ehcache1.6
Mongodb-mongo-java-driver1.2
Velocity1.6
2010:
MySQL JDBC Driver (mysql-connector-java-5.1.13)
PostgreSQL JDBC Driver (postgresql-jdbc-8.4-701)
Netty3.2
Tomcat7
2011:
Jetty8
目前主要关注这4个:
OpenJDK Javac1.7
Netty4.0
Tomcat7
Jetty8
我现在每隔1到7天就会看这4个开源项目的源代码库中有没有更新,我装了TortoiseHg,
TortoiseGit, TortoiseSVN,因为看Javac1.7的源代码更新用TortoiseHg比较方便,而看Netty4.0要用TortoiseGit,最后两者用TortoiseSVN。
3. 我对看源代码的人进行了分类
分5种人:
1) 解决问题型
这种类型的人通常是在工作学习中碰到了一个很费解或很棘手的问题,文档也看了,google也找了,同事、同学也问过了,但是问题还是无法解决,于是不得不把源代码下下来,然后一边看一边debug,直到问题解决。
2) 三分钟热度型
可能是看到别人也在看或者在论坛上看到某些人说XXX设计得很好,性能也不错,或者看到某些人在论坛上发了些分析源代码的文章,再加上自己一开始也兴趣满满,然后也跟风了,看了10来个类的代码,啊,发现太痛苦,方法之间调来调去的,太绕了,头快炸了,给自己找个理由,这代码写得太垃圾了,妈的,不看了。
3) 一知半解型
网上经常看到有人在写分析源代码的文章,一上来就是一陀陀的源代码,然后告诉你这做了什么,那做了什么
就加了点中文注释,有时这中文注释还不如源代码中的英文注释好理解,然后过了一段时间,发现文章不更新了,也没有后续了。
4) 真才实学型
像原作者一样思考,能轻松说出此开源项目的核心架构,精确理解80%以上的源代码,能找出bug并能提交相应patch
5) 创新型
对此开源项目的优缺点了然于心,能够提取其精华为我所用,想出更好的方案解决现有问题,超越原作者。
4. 我看源代码的经验
仅供参考,不要随意模仿,每个人都应该找到适合自己的方式,唯一重要的是以下3点:
1) 时间
最好能有一大段时间集中精力去看,比如你要看Tomcat7,要有3个月的时间每天花3到8小时去不停的看,时间拖拉得越久,会看了前面忘了后面。
2) 兴趣
看代码不要有任何功利性,你要对它有兴趣,充满好奇心,去理解它做了什么,怎么做的。
3) 耐心
这一点说起来容易,真正要做到是极其困难的,比如当你某些类连看了三次时还看不懂,不要先想别人写的代码是否垃圾,先想想你在这方面的背景知识是否足够,比如在看Tomcat实现http协议相关的代码时,一边看代码一边看http协议是最有效的方式,再比如你想看编译器相关的代码,最起码在看之前,你要对<<编译原理>>这门课中的内容有基本的了解。如果背景知识有了还看不懂怎么办,看不懂的代码先放下,有些if分支只有你把整个项目都大致了解了一下后才能理解的,要反复的看,看三次仅仅是个入门级别。
先简单说一下我的经历:
小学到初中我还算是个好学生,在班里经常拿第一,但是从小学6年级到初三这4年比较特殊,遇到了一个很垃圾很垃圾很垃圾的数学老师,所以这4年我的数学基本上就是自学的了,我的自学能力从小到现在都是非常的强。
当然,这不能归功于这位垃圾老师,我自小就很叛逆,不喜欢这禽兽的教学方式,没有此垃圾,也许我的生活会是另一番景色。
我不像正常人那样上完初中上高中,然后再上大学,而是上完初中后就直接到一所师范大学上中专+自考大专,2001年就毕业了。
中专是学会计统计类的,自考大专是计算机应用,大专一共才12门课,没有英语,数学方面只有高数,而且还是第一册,核心专业课方面只有pascal、c、8086汇编语言、FoxPro、模拟电路、数据结构、操作系统、软件工程、计算机接口与技术。
只要不是硬件类的专业课程我就学得非常好,硬件类课程就只是听老师讲,当时连硬盘长什么样都不知道。
我2001年毕业时才19岁,然后就出来找工作了,说这些只是想说我从学校得到的教育并不多,现在回想起来从学校学的C语言、汇编语言、数据结构、操作系统为我以后的自学提供了一些帮助。
直到2006年,我辞职了,之前我己经工作了4年,都只是做应用软件项目,所以数学基本上没用过,英语也用得少之又少,英语其实比现在的高中生水平还差,所以要看英文的技术文章也是看不懂的。
2006年我本来是要复习考研究生的,我从3月份开始连背了三个月的英语,每天花4到8小时背新概念英语的课文,
一、二册全部背完,第三册背了前42课,第四册背了前10课,我以为这样的水平足够应付考研英语了,结果拿试卷一做,阅读理解至少有2/5的单词认不得,不得不去背考研英语的单词。
数学基本上忘光了,到网上下初中和高中的新课标课本下来看,高数、线性代数、概论全都自学,还买了相关的数学书来看(像几何、离散数学等等)。
天天做那些垃圾数学题,还要背马哲、毛概,专业课倒是小儿科。
一直到10月份,各校出来招生简章了,想报10大高校,结果别人不鸟你专科生,你专科生没有报名资格, 好吧,换二线的可以了吧,结果还是差不多,不是要本科,就是要发表啥论文才能报,最后,很不情愿的报了个很平庸的所谓211大学。
此后越发觉得天天做题背单词背书实在是件极其无聊的事,再加上报考这件事,严重打击积极性,一直想放弃考研但是又一直坚持着,直到11月23日,那天去书店看书,翻到一本讲编译器实现的书,也就是所谓的"虎书",当时并不知道编译器是什么,因为我在学校时没学过编译原理,在书店连看了一小时,觉得很有趣,就买了回来,接着就把考研的书全丢到一个角落里了。
接下来你应该懂的,我疯狂的买书,"龙书","鲸书"啥的我都买了,只要是有关编译器的,不管是国内还是国外我都买,因为我并不是一看就全懂的,也是因为"虎书"刚开始一两章还好理解,后面的我当时就看不懂了,我有个习惯,就是实在看不懂的书,我就会换一本,确认一下是我自己的原因还是书本身的问题,现在我只会说"虎书"翻译得并不好,"龙书"更好理解,当然一开始就看"龙书"也并不是那么好理解,所以我当时甚至连形式语言和自动机相关的书我都买来看了。
我连看了3个多月,当时觉得看书不过瘾,就想找个实际的编译器来玩,我下了GCC和LCC,当时刚好sun公司又把javac开源了。
因为工作中只用到php和java,c语言已经4年没用过了,加上javac比前两者要小很多,所以从2007年二月份开始第一次看javac的源代码,也是第一次看别人的源代码。
javac的源代码不多,8万行都不到,花了我3个月的时间,平均每天至少花7小时看代码。
因为我当时只会java1.4,java1.5之后出来的很多东西都不懂,所以也是一边看javac的源代码一边学新语法。
2007年5月份时我还在JavaEye上发了第一篇有关javac的文章,得了个精华,引起了一点小轰动,这里有证据:http://www.iteye.com/topic/84833内容貌似当年被我删除了,想不起来是什么原因了,zhh2007就是我本人以前的id。
当时看代码的方法是非常原始的,但是直到现在我还在用这种方法,只不过现在有时会用eclipse来看,我是这么做的:
看原代码用 EditPlus,找到第一个入口类:
com\\sun\\tools\\javac\\Main
自己再写一个Debug类,按执行流程看到一个方法时,就加入类似下面的代码块:
public static int compile(String[] args) {
try {//我加上的
DEBUG.P(Main.class,"compile(1)");
DEBUG.PA("args", args);
com.sun.tools.javac.main.Main compiler =
new com.sun.tools.javac.main.Main("javac");
return compiler.compile(args);
}finally{//我加上的
DEBUG.P(0,Main.class,"compile(1)");
}
}
然后一定要重编译源代码,再运行,保证自己加的代码被执行到了,我会把输出的debug结果重定向到一个文件中,然后一边看源代码一边看输出结果,一些变量或表达式自己想看结果也会加DEBUG.P(...), 碰到一些代码行数很多的类,我甚至会把每个方法copy出来放到一个新的java文件中,然后打开多个EditPlus,每个EditPlus看一个方法,而且是按照方法的调用顺序打开EditPlus看的。
可能看到这很多人会觉得这种方式好土,我也不能说好不好,从今年开始,因为我的电脑装了Eclipse了,所以Jetty8的源代码我是用Eclipse看的,也不再打DEBUG输出,也不再copy方法,而是直接用eclipse的debug跟踪功能,但是我现在提出质疑了,Jetty8的代码量只是Tomcat6的1/3,我用Eclipse这种方式来研究源代码并不能节省我的总时间,我用最原始的方式研究Tomcat6也只花了3个月,现在研究Jetty8已经用了我两个月的时间,而且看Tomcat6和Jetty8是两个类似的东西,按理说先看Tomcat6后看Jetty8应该花的时间更少才对。
我总结了一下,为什么最初的方式好,那是因为我那样不断折腾源代码的过程中已经间接让我记住了代码的布局,我在敲那些重复的DEBUG.P代码时我把局部变量、表达式、字段都输出了一次,这有助于我的记忆。
而Eclipse的debug功能只是在不停的按F5-F6-F7-F8,打断点,方法调用太深时很难理清前后关系,有时看了一星期,连哪个类在哪个目录都不知道,因为debug时,跟到相关的类eclipse会自动打开那个类,不用你从目录中找。
Eclipse的代码折叠粒度又不能折叠到块级别,不能折叠if、while,一旦方法的行数很多,看起来会很累,分不清这个方法的层次结构,而EditPlus在看方法时就看得很舒服,因为他能折叠if、while,看完了一个while我可以把他折叠起来,有多个if-else时只要折叠一下就不会超过一屏。
你能打开20个EditPlus,但是你不能同时打开10个Eclipse,除非你电脑的内存牛X到不行。
有一点很重要,不管是哪一种方式,一定要把环境搭建好,你自已要能够编译源代码,并且多写些例子去验证源代码中的执行流程,我一般不会去看源有代码中的例子或测试用例的,只有想不出时才去看(特别是看编译器时,一些用例你很难想到)。
我为什么要看这么多源代码?
除了个人兴趣之外,现在想起来其实还有个很好笑的原因:
2007下半年和2008一整年,这段时间很多人说Java快死了,Ruby/Rails、Erlang很火,我经常上JavaEye,免不了也受影响,所以在2008年时还学习了Ruby/Rails、Erlang,连Erlang的编译器都玩了一下,之后发现并不是那样滴,只不过是一些大佬在鼓吹而已,再加上2008年家里出了点事,所以过得很郁闷,一有闲情就跟JavaEye的大佬们"打架",想来也是种乐趣,算是种排解压力的方式。
当然,与此同时,也会从技术的角度思考Java出了什么问题,所以从2009年开始就专心研究技术了,还把douyu的原型鼓捣出来了。
2009年研究的那些开源项目其实都是很顺其自然的事,我要做一个http服务器,Tomcat已经做好了,我想知道他怎么做的,我就去看,看这些项目就是为了想知道他们做了什么,怎么做的,哪里做得不好,我能不能比他们做得更好。
现在,看代码已经是我的一种习惯了,从毕业那年到2008年我买了几万块钱的书,以至于我来杭州后都没有把桂林的房子退了,因为书多,桂林那房子除了回去能住个几天外,现在是租给书住的了。
从2009年到现在,两年多时间我没有再买书,都是看源代码学新东西,如果是一个全新的领域,最多也就是在网上找点入门资料,然后再看源代码,包括下半年我准备研究HotSpot,已经是C/C++的领域了,我没有任何学习压力,只是一个时间问题。
一个人的自我学习能力非常的重要,Java相关的和Java之外的所有东西我都是自学的,我没有特别问过什么人,也没参加过培训,实在不懂的地方就查资料买书看。
如果看代码看不懂,这几点一定要明白:
1) 相关背景知识是否具备
2) 要有耐心,多看几遍
3) 不要指望别人告诉你答案,别人没跟你说也不要说别人高高在上不理你,因为这不是别人的义务
4) 经过对比之后再说别人的代码烂