《左耳听风》学习笔记-高效学习

Posted by Zhb on April 4, 2022

高效学习

高效学习:端正学习态度

​ 学习是一件“逆人性”的事,就像锻炼身体一样, 需要人持续付出,会让人感到痛苦,并随时想找理由放弃

大部分人都认为自己爱学习,但是:

  • 他们都是只有意识没有行动,他们是动力不足的人。
  • 他们都不知道自己该学什么,他们缺乏方向和目标。
  • 他们都不具备自主学习的能力,没有正确的方法和技能。
  • 更要命的是,他们缺乏实践和坚持。

​ 如果你不能克服自己DNA中的弱点,不能端正自己的态度,不能自律,不能坚持,不能举一反三,不能不断追问等,那么,无论有多好的方法,你都不可能学好。所以,有正确的态度很重要。

主动学习和被动学习

人的学习分为「被动学习」和「主动学习」两个层次。

  • 被动学习:如听讲、阅读、视听、演示,学习内容的平均留存率为5%、10%、20%和30%。

  • 主动学习:如通过讨论、实践、教授给他人,会将原来被动学习的内容留存率从5%提升到50%、75%和90%。

​ 你听别人讲,或是自己看书,或是让别人演示给你,这些都不能让你真正获得学习能力,因为你是在被别人灌输,在听别人说。只有你开始自己思考,开始自己总结和归纳,开始找人交流讨论,开始践行,并开始对外输出,你才会掌握到真正的学习能力。

所以,学习不是努力读更多的书,盲目追求阅读的速度和数量,这会让人产生低层次的勤奋和成长的感觉,这只是在使蛮力。要思辨,要践行,要总结和归纳,否则,你只是在机械地重复某件事,而不会有质的成长的。

浅度学习和深度学习

老实说,对于当前这个社会:

  • 大多数人的信息渠道都被微信朋友圈、微博、知乎、今日头条、抖音占据着。这些信息渠道中有营养的信息少之又少。

  • 大多数公司都是实行类似于996这样的加班文化,在透支和消耗着下一代年轻人,让他们成长不起来。

  • 因为国内互联网访问不通畅,加上英文水平受限,所以,大多数人根本没法获取到国外的第一手信息。

  • 快餐文化盛行,绝大多数人都急于速成,心态比较浮燥,对事物不求甚解。

所以,你看,在这种环境下,你根本不需要努力的。你只需要踏实一点,像以前那样看书,看英文资料,你只需要正常学习,根本不用努力,就可以超过你身边的绝大多数人。

怎样进行深度学习呢?下面几点是关键。

  • 高质量的信息源和第一手的知识
  • 把知识连成地图,将自己的理解反述出来
  • 不断地反思和思辨,与不同年龄段的人讨论
  • 举一反三,并践行之,把知识转换成技能

换言之,学习有三个步骤。

  • 知识采集。信息源是非常重要的, 获取信息源头、破解表面信息的内在本质、多方数据印证,是这个步骤的关键。

  • 知识缝合。所谓缝合就是把信息组织起来,成为结构体的知识。这里, 连接记忆,逻辑推理,知识梳理 是很重要的三部分。

  • 技能转换。通过 举一反三、实践和练习,以及 传授教导,把知识转化成自己的技能。这种技能可以让你进入更高的阶层。

学习是为了找到方法

学习不仅仅是为了找到答案,而更是为了找到方法

只有掌握解题的思路和方法,你才算得上拥有解决问题的能力。所有的练习,所有的答案,其实都是在引导你去寻找一种“以不变应万变”的方法或能力。在这种能力下,你不需要知道答案,因为你可以用这种方法很快找到答案,找到解,甚至可以通过这样的方式找到最优解或最优雅的答案。

所以, 学习是为了找到通往答案的路径和方法,是为了拥有无师自通的能力

学习是为了找到原理

学习不仅仅是为了知道,而更是为了思考和理解。在学习的过程中,我们不是为了知道某个事的表面是什么,而是要通过表象去探索其内在的本质和原理。真正的学习,从来都不是很轻松的,而是那种你知道得越多,你的问题就会越多,你的问题越多,你就会思考得越多,你思考得越多,你就会越觉得自己知道得越少,于是你就会想要了解更多。如此循环,是这么一种螺旋上升上下求索的状态。

但是,这种循环,会在你理解了某个关键知识点后一下子把所有的知识全部融会贯通,让你赫然开朗,此时的那种感觉是非常美妙而难以言语的。在学习的过程中,我们要不断地问自己,这个技术出现的初衷是什么?是要解决什么样的问题?为什么那个问题要用这种方法解?为什么不能用别的方法解?为什么不能简单一些?……

这些问题都会驱使你像一个侦探一样去探索背后的事实和真相,并在不断的思考中一点一点地理解整个事情的内在本质、逻辑和原理。 一旦理解和掌握了这些本质的东西,你就会发现,整个复杂多变的世界在变得越来越简单。你就好像找到了所有问题的最终答案似的,一通百通了

学习是为了了解自己

学习不仅仅是为了开拓眼界,而更是为了找到自己的未知,为了了解自己。英文中有句话叫:You do not know what you do not know,可以翻译为:你不知道你不知道的东西。也就是说,你永远不会去学习你不知道其存在的东西。就好像你永远Google不出来你不知道的事,因为对于你不知道的事,你不知道用什么样的关键词,你不知道关键词,你就找不到你想要的知识。

这个世界上有很多东西是你不知道的,所以,学习可以让你知道自己不知道的东西。只有当我们知道有自己不知道的东西,我们才会知道我们要学什么。所以,我们要多走出去,与不同的人交流,与比自己聪明的人共事,你才会知道自己的短板和缺失,才会反过来审视和分析自己,从而明白如何提升自己。

学习是为了改变自己

学习不仅仅是为了成长,而更是为了改变自己。很多时候,我们觉得学习是为了自己的成长,但是其实,学习是为了改变自己,然后才能获得成长。为什么这么说呢?我们知道,人都是有直觉的,但如果人的直觉真的靠谱,那么我们就不需要学习了。而学习就是为了告诉我们,我们的很多直觉或是思维方式是不对的,不好的,不科学的。只有做出了改变后,我们才能够获得更好的成长。

当你有一天知道了逻辑中的充要条件或是因果关系后,你会发现使用这样的方式来思考问题时,你比以往更接近问题的真相。 学习是为了改变自己的思考方式,改变自己的思维方式,改变自己与生俱来的那些垃圾和低效的算法。总之,学习让我们改变自己,行动和践行,反思和改善,从而获得成长

高效学习:源头、原理和知识地图

挑选知识和信息源

英文对于我们来说至关重要,尤其是对于计算机知识来说。如果你觉得用百度搜中文关键词就可以找到自己想要的知识,那么你一定远远落后于这个时代了。如果你用Google英文关键词可以找到自己想要的知识,那么你算是能跟得上这个时代。如果你能在社区里跟社区里的大牛交流得到答案,那么你算是领先于这个时代了。

所以,我认为你的信息源要有下面几个特质。

  • 应该是第一手资料,不是被别人理解过、消化过的二手资料。尤其对于知识性的东西来说,更是这样。应该是原汁原味的,不应该是被添油加醋的。

  • 应该是有佐证、有数据、有引用的,或是有权威人士或大公司生产系统背书的资料。应该是被时间和实践检验过的,或是小心求证过的,不是拍脑袋野路子或是道听途说出来的资料。

  • 应该是加入了一些自己的经验和思考,可以引发人深思的,是所谓信息的密集很大的文章。

顺便说一句,我发现Medium 上的文章质量比较高,很多文章都Google到了Medium上。

注重基础和原理

​ 我在很多的场合都提到过,基础知识和原理性的东西是无比重要的。这些基础知识就好像地基一样,只要足够扎实,就要可以盖出很高很高的楼。正所谓“勿在浮沙筑高台”。我说过,很多人并不是学得不够快,而他们的基础真的不行。基础不行,会影响你对事物的理解,甚至会让你不能理解为什么是这样。当你对事物的出现有不理解的东西时,通常来说,是因为你的基础知识没有跟上。

举个例子,如果你学习过底层的Socket编程,了解多路复用和各种I/O模型的话(select, poll, epoll, aio, windows completion port, libevent等),那么,对于Node.js、Java NIO、Nginx、C++的ACE框架等这些中间件或是编程框架,你就会发现,无论表现形式是什么样的,其底层原理都是一个样的。

无论是JVM还是Node,或者是Python解释器里干了什么,它都无法逾越底层操作系统API对“物理世界”的限制。而当你了解了这个底层物理世界以后,无论那些技术玩成什么花样,它们都无法超出你的掌控(这种感觉是很爽的)。

再举一个例子,当学了足够多的语言,并有了丰富的实践后,你开始对编程语言的各种编程范式或是控制流有了原理上的了解,这时再学一门新语言的话,你会发现自己学得飞快。

最最关键的是,这些基础知识和原理性的东西和技术,都是经历过长时间的考验的,所以,这些基础技术也有很多人类历史上的智慧结晶,会给你很多启示和帮助。比如:TCP协议的状态机,可以让你明白,如果你要设计一个异步通信协议,状态机是一件多么重要的事,还有TCP拥塞控制中的方式,让你知道,设计一个以响应时间来限流的中件间是什么样的。

当学习算法和数据结构到一定程度的时候,你就会知道,算法不仅对于优化程序很重要,而且,会让你知道,该如何设计数据结构和算法来让程序变得更为健壮和优雅。

有时候,学习就像拉弓蓄力一样,学习基础知识感觉很枯燥很不实用,工作上用不到,然而学习这些知识是为了未来可以学得更快。基础打牢,学什么都快,而学得快就会学得多,学得多,就会思考得多,对比得多,结果是学得更快……这种感觉,对于想速成的人来说,很难体会。

这里我想再次强调一下,请一定要注重基础知识和原理上的学习!

使用知识图

先讲一个故事,2000年我从昆明到上海,开始沪飘的岁月。刚到上海,找不到好工作,只能大量地学习和看书。结果呢,书太多了,根本看不过来。我想要更多地掌握知识,结果我发现以死记硬背的方式根本就是在使蛮力学习,我很难在很短的时间内学习很多的知识。

于是我自己发明了一种叫“联想记忆法”的方法,比如,在学习C++的时候,面对《C++ Primer》这种厚得不行的书,我就使用联想记忆法。

我把C++分成三部分。

  • 第一部分是C++是用来解决C语言的问题的,那么C语言有什么问题呢?指针、宏、错误处理、数据拷贝…… C++用什么技术来解决这些问题呢?

  • 第二部分是C++的面向对象特性:封装、继承、多态。封装,让我想到了构造函数、析构函数等。构造函数让我想到了初始化列表,想到了默认构造函数,想到了拷贝构造函数,想到了new……多态,让我想到了虚函数,想到了RTTI,RTTI让我想到了dynamic_cast 和 typeid等。

  • 第三部分是C++的泛型编程。我想到了template,想到了操作符重载,想到了函数对象,想到STL,想到数据容器,想到了iterator,想到了通用算法,等等。

于是,我通过“顺藤摸瓜”的方式,从知识树的主干开始做广度或是深度遍历,于是我就得到了一整棵的知识树。这种“顺藤摸瓜”的记忆方式让我记住了很多知识。 最重要的是,当出现一些我不知道的知识点时,我就会往这棵知识树上挂,而这样一来,也使得我的学习更为系统和全面

这种画知识图的方式可以让你从一个技术最重要最主干的地方出发开始遍历所有的技术细节,也就是画地图的方式。如果你不想在知识的海洋中迷路,你需要有一份地图,所以, 学习并不是为了要记忆那些知识点,而是为了要找到一个知识的地图,你在这个地图上能通过关键路径找到你想要的答案

高效学习:深度,归纳和坚持实践

系统地学习

当然学习一门技术时,我都有一个学习模板。只有把这个学习模板中的内容都填实了,我才罢休。这个模板如下。

  1. 这个技术出现的背景、初衷和要达到什么样的目标或是要解决什么样的问题

  2. 这个技术的优势和劣势分别是什么,或者说,这个技术的trade-off(要什么和不要什么)是什么

  3. 这个技术适用的场景。所谓场景一般分别两个,一个是业务场景,一个是技术场景。

  4. 技术的组成部分和关键点。这是技术的核心思想和核心组件了,也是这个技术的灵魂所在了。学习技术的核心部分是快速掌握的关键。

  5. 技术的底层原理和关键实现。任何一个技术都有其底层的关键基础技术,这些关键技术很有可能也是其它技术的关键基础技术。所以,学习这些关键的基础底层技术,可以让你未来很快地掌握其它技术。可以参看我在CoolShell上写的Docker底层技术那一系列文章。

  6. 已有的实现和它之间的对比。一般来说,任何一个技术都会有不同的实现,不同的实现都会有不同的侧重。学习不同的实现,可以让你得到不同的想法和思路,对于开阔思维,深入细节是非常重要的。

基本上来说,如果你按照我上面所提的这6大点来学习一门技术,你一定会学习到技术的精髓,而且学习的高度在一开始就超过很多人了。如果你能这样坚持2-3年,我相信你一定会在某个领域成为炙手可热的佼佼者。

举一反三

如何才能有举一反三的能力?

我认为,人与人最大的差别就是举一反三的能力。

我觉得一个人的举一反三能力,可以分解成如下三种基本能力。

  1. 联想能力。这种能力的锻炼需要你平时就在不停地思考同一个事物的不同的用法,或是联想与之有关的其他事物。对于软件开发和技术学习也一样。

  2. 抽象能力。抽象能力是举一反三的基本技能。平时你解决问题的时候,如果你能对这个问题进行抽象,你就可以获得更多的表现形式。抽象能力需要找到解决问题的通用模型,比如数学就是对现实世界的一种抽象。只要我们能把现实世界的各种问题建立成数据模型(如,建立各种维度的向量),我们就可以用数学来求解,这也是机器学习的本质。

  3. 自省能力。所谓自省能力就是自己找自己的难看。当你得到一个解的时候,要站在自己的对立面来找这个解的漏洞。有点像左右手互博。这种自己和自己辩论的能力又叫思辨能力。将自己分裂成正反方,左右方,甚至多方,站在不同的立场上来和自己辩论,从而做到不漏过一个case,从而获得完整全面的问题分析能力。

在这方面,我对自己的训练如下。

  1. 对于一个场景,制造出各种不同的问题或难题。
  2. 对于一个问题,努力寻找尽可能多的解,并比较这些解的优劣。
  3. 对于一个解,努力寻找各种不同的测试案例,以图让其健壮。

老实说,要获得这三种能力,除了你要很喜欢思考和找其它人来辩论或讨论以外,还要看你自己是否真的善于思考,是否有好奇心,是否喜欢打破沙锅问到底,是否喜欢关注细节,做事是否认真,是否严谨……

这一系列的能力最终能构建出你强大的思考力,而这个思考力会直接转换成你的求知和学习能力。

总结和归纳

对自己的知识进行总结和归纳是提高学习能力的一个非常重要的手段。这是把一个复杂问题用简单的语言来描述的能力。就像我小时候上学时,老师让我们写文章的中心思想一样。这种总结和归纳能力会让你更好地掌握和使用知识。

也就是说,我们把学到的东西用自己的语言和理解重新组织并表达出来,本质上是对信息进行消化和再加工的过程,这个过程可能会有信息损失,但也可能会有新信息加入,本质上是信息重构的过程。

我们积累的知识越多,在知识间进行联系和区辨的能力就越强,对知识进行总结和归纳也就越轻松。而想要提高总结归纳的能力,首先要多阅读,多积累素材,扩大自己的知识面,多和别人讨论,多思辨,从而见多识广。

不过,我们需要注意的是,如果只学了部分知识或者还没有学透,就开始对知识进行总结归纳,那么总结归纳出来的知识结构也只能是混乱和幼稚的。因此, 学习的开始阶段,可以不急于总结归纳,不急于下判断,做结论,而应该保留部分知识的不确定性,保持对知识的开放状态。当对整个知识的理解更深入,自己站的位置更高以后,总结和归纳才会更有条理。总结归纳更多是在复习中对知识的回顾和重组,而不是一边学习一边就总结归纳。

我们来总结一下做总结归纳的方法: 把你看到和学习到的信息,归整好,排列好,关联好,总之把信息碎片给结构化掉,然后在结构化的信息中,找到规律,找到相通之处,找到共同之处,进行简化、归纳和总结,最终形成一种套路,一种模式,一种通用方法

要训练自己这方面的能力,你需要多看一些经典的方法论图书,看看别人是怎样总结和归纳知识的。你可以在一开始模仿并把自己的理解的知识给写出来,写博客会是一种很好的方式。另外一种更好的方式是讲一遍给别人听。总之,你需要把你总结归纳的知识公开出来,给别人看,接受别人的批评和反馈,这样你才能成长得更快。

如果你在Coolshell上看过我写的《 TCP的那些事儿》,你就能知道我对《TCP/IP详解》这本这么厚的书以及一些日常工作经验的总结,我写成了两篇比较简单的博客。你需要像我一样扩大自己的知识面,然后学会写博客,就能慢慢地拥有这种能力了。这种将信息删减、精炼和归纳的方法,可以让你的学习能力得到快速的提升。当你这么做的时候,一方面是在锻炼你抓重点的能力,另一方面是在锻炼你化繁为简的能力。这两种能力都是让你高效学习的能力。

最后,还想说一下,一般来说,拥有这样能力的人,都需要有在更高的维度上思考问题的能力。比如一些名人的金句,就是这种能力的体现。这种能力需要你非常深入的思考,需要你的阅历和经验,当然,和聪明人在一起也是提升这种能力的最有效的选择。

实践出真知

所谓实践出真知,也就是学以致用,不然只是纸上谈兵,误国误民。只有实践过,你才能对学到的东西有更深的体会。就像我看 《Effective C++》和《More Effective C++》这两本书一样,一开始看的时候,我被作者的那种翻来覆去不断找到答案又否定自己的求知精神所折服。但是,作者的这种思维方式只有在我有了很多的实践和经验(错误)后,才能够真正地体会为什么是这样的。

这两书不厚,但是,我看了十多年,书中的很多章节我都可以背出来,但是我想得到的不是这些知识,而是这种思维方式,这需要我去做很多的编程工作才能真正明白,才会有斯科特·迈耶斯(Scott Meyers)那样的思维方式,这才是最宝贵的。

另外,实践出真知也就是英文中的 Eat your own dog food。吃自己的狗粮,你才能够有最真实的体会。那些大公司里的开发人员,写完代码,自己不测试,自己也不运维,我实在不知道他们怎么可能明白什么是好的设计,好的软件?不吃自己的狗粮,不养自己的孩子,他们就不会有痛苦,没有痛苦,就不会想改进,没有改进的诉求也就不会有学习的动力,没有学习,就不会进步,没有进步就只会开发很烂的软件……不断地恶性循环下去。

实践是很累很痛苦的事,但只有痛苦才会让人反思,而反思则是学习和改变自己的动力。Grow up through the pain, 是非常有道理的。

坚持不懈

坚持不懈是一句正确的废话。前段时间,我在我的读者群中发起了一个名为ARTS的活动。每人每周写一个ARTS:Algorithm 是一道算法题,Review是读一篇英文文章,Technique/Tips是分享一个小技术,Share是分享一个观点。我希望大家可以坚持一年,但是我也相信,能够坚持下来的人一定很少,绝大多数人都是虎头蛇尾的,但是我依然相信会有人坚持下来的。

坚持是一件反人性的事,所以,它才难能可贵,也更有价值。我从2003年写blog到今天15年了,看书学习写代码,我都会一点一点的坚持。人不怕笨,怕的是懒,怕的是找到各种理由放弃。

这里,我想鼓励一下你。现在很多国外的在线视频课都是3-5分钟一节课,一共20节课,总时长不到两个小时。然而,你会发现,能坚持看完的不到千分之一。当年Leetcode只有151道题的时候,一共有十几万人上来做题,但全部做完的只有十几个,万分之一。所以,只要你能坚持,就可以超过这个世界上绝大多数人。想一想,如果全中国有100万个程序员,只要你能坚持学习技术2-3年,你就可以超过至少99万人了(可能还更多)。

当然,坚持也不是要苦苦地坚持,有循环有成就感的坚持才是真正可以持续的。所以, 一方面你要把你的坚持形成成果晒出来,让别人来给你点赞,另一方面,你还要把坚持变成一种习惯,就像吃饭喝水一样,你感觉不到太多的成本付出。只有做到这两点,你才能够真正坚持

高效学习:如何学习和阅读代码

读文档还是读代码

杰夫·阿特伍德(Jeff Atwood)说过这么一句话:“ Code Tells You How, Comments Tell You Why”。我把其扩展一下:

  • 代 码 => What, How & Details
  • 文档/书 => What, How & Why

可见, 代码并不会告诉你 Why,看代码只能靠猜测或推导来估计Why,是揣测,不准确,所以会有很多误解。 而且,我们每个人都知道,Why是能让人一通百通的东西,也是能让人醍醐灌顶的东西

但是, 代码会告诉你细节,这是书和文档不能给你的。 细节是魔鬼,细节决定成败。当然,我们也要承认,这些代码细节给人带来的快感毕竟不如知道Why后的快感大(至少对我是这样的)。

书和文档是人对人说的话,代码是人对机器说的话(注:代码中有一部份逻辑是控制流程的逻辑,不是业务逻辑)。所以:

  1. 如果你想知道人为什么要这么搞,那么应该去看书(像Effective C++、Code Complete、Design Pattern、Thinking in Java等), 看文档

  2. 如果你要知道让机器干了什么?那你应该看代码!(就像Linus去看zlib的代码来找性能问题。)

因此,我认为都比较重要,关键看你的目的是什么了。

  • 如果你想了解一种思想,一种方法,一种原理,一种思路,一种经验,恐怕,读书和读文档会更有效率一些,因为其中会有作者的思路描述。像Effective C++之类的书,里面有很多对不同用法和设计的推敲,TCP/IP详解里面也会有对TCP算法好坏的比较……这些思维方式能让你对技术的把握力更强,而光看代码很难达到这种级别。(现在你知道什么样的书是好书了吧 ;-))

  • 如果你想了解的就是具体细节,比如某协程的实现,某个模块的性能,某个算法的实现,那么你还是要去读代码的,因为代码中会有更具体的处理细节(尤其是对于一些edge case或是代码技巧方面的内容)。

另外,看看下面的几个现象,你可以自己比较一下。

  • 很多时候,我们去读代码,那是因为没有文档,或是文档写得太差。

  • 很多时候, 在Google、Stack Overflow、GitHub过后,你会发现,你掌握的知识就是一块一块的碎片,既不系统,也不结构化,更别说融会贯通了。你会觉得自己需要好好地读一本书,系统地掌握知识。你的这种感觉一定很强烈吧

  • 很多时候,在读别人代码的时候,你会因为基础知识或是原理不懂,或是你在不知道为什么的情况下,要么完全读不懂代码,要么会误解代码。比如,如果你没有C语言和TCP原理方面的基础知识,就根本读不懂Linux下TCP的相关代码。我们因为误解代码用意而去修改代码造成的故障还少吗?

  • 很多时候,看到一个算法或是一个设计时,比如Paxos,你是不是会想去看一下这个算法的实现代码是什么样的?思考一下如何才能实现得好?(但是如果你没看过Paxos的算法思想,我不认为你光看代码实现,就能收获Paxos的思想。)

  • 很多时候, 当你写代码的时候,你能感觉得到自己写的代码有点别扭,怎么写都别扭,这个时候,你也会有想去看别人的代码是怎么实现的冲动

谈一谈人的学习过程吧。从学习的过程中,我们来分析一下看代码和看书这两个活动。人对新事物的学习过程基本都是从“感性认识”到“理性认识”的。

  • 如果你是个新手,那应该多读代码,多动手写代码,因为你需要的是“感性认识”,这个时候“理性认识”你体会不到。一是因为,你没有切身的感受,即便告诉你Why你也体会不到。另一方面,这个阶段,你要的不是做漂亮,而是做出来。所以,在 新手阶段,你会喜欢GitHub这样的东西

  • 如果你是个老手,你有多年的“感性认识”了,那么你的成长需要更多的“理性认识”。因为这个阶段,一方面,你会不满足于做出来,你会想去做更牛更漂亮的东西;另一方面,你知道的越多,你的问题也越多,你迫切地需要知道Why!这时,你需要大量地找牛人交流(读牛人的书,是一种特殊的人与人的交流),所以, 这个阶段,你会喜欢读好的书和文章

然而,对于计算机行业这个技术创新能力超强、技术种类繁多的行业来说,我们每个人都既是新手,也是老手。

如何阅读源代码

很多人问过我,如何读代码。因为我在外企里工作的时间较长,所以,我经常接手一些国外团队写的代码。我发现,虽然老外写的代码比国人好一点儿(有Code Review),但依然有文档缺失、代码注释不清、代码风格混乱等一些问题,这些都是阅读代码的障碍。这里,我把我的一些阅读源代码的经验分享给你,希望对你有用。

首先,在阅读代码之前,我建议你需要有下面的这些前提再去阅读代码,这样你读起代码来会很顺畅。

  1. 基础知识。相关的语言和基础技术的知识。

  2. 软件功能。你先要知道这个软件完成的是什么样的功能,有哪些特性,哪些配置项。你先要读一遍用户手册,然后让软件跑起来,自己先用一下感受一下。

  3. 相关文档。读一下相关的内部文档,Readme也好,Release Notes也好,Design也好,Wiki也好,这些文档可以让你明白整个软件的方方面面。如果你的软件没有文档,那么,你只能指望这个软件的原作者还在,而且他还乐于交流。

  4. 代码的组织结构。也就是代码目录中每个目录是什么样的功能,每个文档是干什么的。如果你要读的程序是在某种标准的框架下组织的,比如:Java的Spring框架,那么恭喜你,这些代码不难读了。

接下来,你要了解这个软件的代码是由哪些部分构成的,我在这里给你一个列表,供你参考。

  1. 接口抽象定义。任何代码都会有很多接口或抽象定义,其描述了代码需要处理的数据结构或者业务实体,以及它们之间的关系,理清楚这些关系是非常重要的。

  2. 模块粘合层。我们的代码有很多都是用来粘合代码的,比如中间件(middleware)、Promises模式、回调(Callback)、代理委托、依赖注入等。这些代码模块间的粘合技术是非常重要的,因为它们会把本来平铺直述的代码给分裂开来,让你不容易看明白它们的关系。

  3. 业务流程。这是代码运行的过程。一开始,我们不要进入细节,但需要在高层搞清楚整个业务的流程是什么样的,在这个流程中,数据是怎么被传递和处理的。一般来说,我们需要画程序流程图或者时序处理图。

  4. 具体实现。了解上述的三个方面的内容,相信你对整个代码的框架和逻辑已经有了总体认识。这个时候,你就可以深入细节,开始阅读具体实现的代码了。对于代码的具体实现,一般来说,你需要知道下面一些事实,这样有助于你在阅读代码时找到重点。
    • 代码逻辑。代码有两种逻辑,一种是业务逻辑,这种逻辑是真正的业务处理逻辑;另一种是控制逻辑,这种逻辑只是用控制程序流转的,不是业务逻辑。比如:flag之类的控制变量,多线程处理的代码,异步控制的代码,远程通讯的代码,对象序列化反序列化的代码等。这两种逻辑你要分开,很多代码之所以混乱就是把这两种逻辑混在一起了(详情参看《编程范式游记》)。

    • 出错处理。根据二八原则,20%的代码是正常的逻辑,80%的代码是在处理各种错误,所以,你在读代码的时候,完全可以把处理错误的代码全部删除掉,这样就会留下比较干净和简单的正常逻辑的代码。排除干扰因素,可以更高效地读代码。

    • 数据处理。只要你认真观察,就会发现,我们好多代码就是在那里倒腾数据。比如DAO、DTO,比如JSON、XML,这些代码冗长无聊,不是主要逻辑,可以不理。

    • 重要的算法。一般来说,我们的代码里会有很多重要的算法,我说的并不一定是什么排序或是搜索算法,可能会是一些其它的核心算法,比如一些索引表的算法,全局唯一ID的算法、信息推荐的算法、统计算法、通读算法(如Gossip)等。这些比较核心的算法可能会非常难读,但它们往往是最有技术含量的部分。

    • 底层交互。有一些代码是和底层系统的交互,一般来说是和操作系统或是JVM的交互。因此,读这些代码通常需要一定的底层技术知识,不然,很难读懂。

  5. 运行时调试。很多时候,代码只有运行起来了,才能知道具体发生了什么事,所以,我们让代码运行进来,然后用日志也好,debug设置断点跟踪也好。实际看一下代码的运行过程,是了解代码的一种很好的方式。

总结一下,阅读代码的方法如下:

  • 一般采用自顶向下,从总体到细节的“剥洋葱皮”的读法。
  • 画图是必要的,程序流程图,调用时序图,模块组织图……
  • 代码逻辑归一下类,排除杂音,主要逻辑才会更清楚。
  • debug跟踪一下代码是了解代码在执行中发生了什么的最好方式。

对了,阅读代码你需要一个很好的IDE。我记得以前读C和C++代码时,有一个叫source insight的工具就大大提高了我的代码阅读效率。说白了就是可以查看代码间相互的调用reference的工具,这方面Visual Studio做得是非常好的。

高效学习:面对枯燥和量大的知识

如何面对枯燥的知识

如果你发现有些知识太过于枯燥,那么可以通过下面的方法解决。

  1. 这个知识对于你来说太高级了,你可能不知道能用在什么地方。
  2. 人的认知是从感性认识向理性认识转化的,所以,你可能要先去找一下应用场景,学点更实用的,再回来学理论。
  3. 学习需要有反馈,有成就感,带着相关问题去学习会更好。
  4. 当然,找到牛人来给你讲解,也是一个很不错的手段。

如何面对大量的知识

东西太多了,怎么学。我给你的建议是,一点一点学,一口一口吃。你可以使用我前面说过的那些方法,注重基础,画知识图,多问为什么,多动手,然后坚持住,哪怕你每周就学一个知识点,你一年也可以学到50个知识点。只要你在进步,总有一天可以把这些知识学到手的。

当然,你的目的不是学完这些知识,因为学无止境,你永远也学不完,所以你在学习时,一定不要学在表面上,一定要学到本质,学到原理上,那些东西是不容易变的,也是经得住时间考验的。把学习当成投资,这是这个世界上回报最好的投资。

带着问题去学习,带着要解决的东西去学习,带着挑战去学习,于是每当你解决了一个问题,做了一个功能,完成了一个挑战,你就会感到兴奋和有成就感。这样,你也就找到了源源不断的学习驱动力。

把你学习的心得、过程、笔记、代码分享出来,找到和你一同学习的人,因为一个人长跑很辛苦,有人同行就会好很多,就算没有人同行,你的读者,你的观众也会为你鼓掌加油,这些也是让你持续前行的动力。

认真阅读文档

我发现很多技术问题都是出在技术人员不认真读技术手册上。在这里,我想说,用户手册(User Manual)一定要好好地读一读,很多很多提示都在里面了,这是让你可以少掉很多坑的法宝。比如:Unix和Linux的man,Docker和Kubernetes的官方文档,Git的操作文档……你的很多很多问题的答案都在这些文档中。

认真阅读用户手册不但可以让你少掉很多坑,同时,还能让你学习到很多。

其它几个实用的技巧

  1. 用不同的方式来学习同一个东西。比如:通过看书,听课,创建脑图,写博客,讲课,解决实际问题,等等。

  2. 不要被打断。被打断简直就是学习的天敌,所以,你在学习的时候,最好把手机设置成勿扰模式放在一边,然后把电脑上的所有通知也关掉,最好到一个别人找不到你的地方。

  3. 总结压缩信息。当你获得太多的信息时,你需要有一个“压缩算法”。我常用的压缩算法是只关心关键点,所以,你需要使用表格、图示、笔记或者脑图来帮助你压缩信息。

  4. 把未知关联到已知。把你新学的知识点关联到已知的事物上来。比如,你在学习Go语言,你就把一些知识关联到自己已经学过的语言上比如C和Java。通过类比,你会学得更扎实,也会思考得更多。

  5. 用教的方式来学习。你想想,如果你过几天要在公开场合对很多人讲一个技术,那么这个压力会让你学得更好。因为要教给别人,所以,这么高的标准需要你不但要把自己已掌握的东西学好,还要把周边的也一并学了,才可能做到百问不倒。你才敢去教别人,不是么?(试试教6岁的孩子编程,如果你掌握了这种技能,那么你一定是把知识吃得非常透彻了。)

  6. 学以致用。把学到的东西用起来,没有什么比用起来能让你的知识更巩固的了。在实践中,你才会有更为真实的体会,你才会遇到非常细节和非常具体的问题,这些都会让你重新思考,或深化学习。

  7. 不要记忆。聪明的人不会记忆知识的,他们会找方法,那些可以推导出知识或答案的方法。这也是为什么外国人特别喜欢方法论。

  8. 多犯错误。犯错会让你学得到更多,通过错误总结教训,你会比没有犯过错的人体会得更深。但是千万不要犯低级错误,也不要同一个错误犯两次。