在这个知识爆炸、科技日新月异的时代,技术的变化远比我们想象的要快很多,这就对工程师的要求就提高了很多,特别是对于那些在技术上有所追求的工程师而言。对于一些互联网大厂,学习能力也成了面试中重点考察的内容。如何快速学习、掌握一门新的技术,如何提高自己的学习效率,对于有一定工作经验的人来说,可能每个人都有一个自己的学习方法论,但是我们也需要去学习借鉴别人(特别是那些有一定技术影响力的技术大咖)的经验,来不断更新和完善自己的方法轮。今天这篇《高效学习》,就是与大家一起探讨技术学习的方法论,本文的内容主要来自耗子叔的《左耳听风 —— 高效学习篇》,中间会穿插个人的一些经验,算是对这个系列的一个总结。如果想看原文内容,欢迎订阅耗子叔的这个专栏,这个专栏质量还是非常高的,耗子叔推荐了很多优秀的学习资源(通过文章末尾处的二维码链接购买)。

端正学习态度

对于大多数人来说,我们并不是那种天赋异禀的天才,所以那些速成的学习方法并不适合我们,因为,对于非天才的我们来说,学习是不可能速成的,学习本来就是一件【逆人性】的事,就像锻炼身体一样,需要人持续付出,会让人感到痛苦,并随时想找理由放弃,实际上,痛苦是成长的必经阶段

大部分人都认为自己热爱学习,但是有多少能真正付出实践、并一直坚持下去,能做到实践和坚持的人,一般运气都不会太差。如果我们去研究一下古今中外的成功人士,就会发现,他们基本上都是非常自律的,也都是非常热爱学习的,他们可以沉得下心来不断学习,在学习中不断地思考、探索和实践。懒,是人类的天性,如果不能克服自己 DNA 中的弱点,不能端正自己的态度,不能自律,不能坚持,不能举一反三,不能不断追问等,那么,无论多好的方法,你都不可能学好。所以,有正确的态度非常重要

当然只做到上面说的,并不一定能保证能够实现所谓的成功,但是完全可以让你在某个领域做到足够优秀。

主动学习和被动学习

下面这张图,大部分人应该都见过,这张图又称为学习金字塔:

学习效率

人的学习,可以分为【被动学习】和【主动学习】两个层次:

  • 被动学习:如听讲、阅读、视听、演示,学习内容的平均留存率为 5%、10%、20% 和 30%;
  • 主动学习:如通过讨论、实践、教授给他人,会将原来被动学习的内容留存率从 5% 提升到 50%、75%、90%。

关于这个,我是深有体会的,如果我们只是看书或听一下别人的分享,不去实践,可能不到半个月,能记住 10% 的内容就不错了,我认为最好的学习方法是 实践,总结,教授给别人(要让别人听明白,教授的过程要有深度的讨论,而不是 PPT 走一遍)

过去一年多,很幸运的是,遇到了几个热爱学习的小伙伴,我们经常周末一起组织分享,每次分享只涉及很少的一块内容,分享过程中我们以讨论为主,这对分享者的能力锻炼有很好的效果(通过讨论听众也能收获很多),首先他需要自己能够理解这个问题,其次他需要把自己的理解给别人讲清楚,还需要回答其他人提出的问题(这些问题可能是分享者压根没注意的问题)。我也一直想在团队内部推广这种学习方法(这种方法人数太多的话就不太适合了),但是在团队内部去推,效果没有想象中得那么好,而且在团队内部反而很难坚持下去(大家的时间都比较有限,如果占据了别人的工作时间,别人可能需要加班才能完成自己的工作,所以大家兴趣并没有那么高昂)。相反,如果能找几个愿意一起学习的小伙伴一同学习、成长,这样反而效果好很多,如果你能找到这样的一群小伙伴,我是非常推荐这种学习方式,把自己学习的内容分享给其他人(大家一起学习、讨论这种学习效果,考虑问题的深度要比自己独自学习高出很多)。

浅度学习和深度学习

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

在知识的领域其实也有阶层之分(类似于富人和穷人在财富方面的阶层之分,阶层的跨越非常难,但不是没有可能),那么长期在底层知识阶层的人,需要等着高层的人来喂养,他们长期陷入各种谣言和不准确的信息环境中,于是就导致错误和幼稚的认知,并习惯于哪些不费劲儿的轻度学习方式,从而一点点地丧失了深度学习的独立思考能力,从而再也没有能力打破知识阶层的限制,被困在认知底层翻不了身(就像我们经常说的,美国那些在穷人区生活的人们,他们在没有受到很好教育的前提下想突破自己的阶层,真的很难)。

对于知识的学习,我们应该如何进行深度学习呢?下面几点是关键:

  1. 高质量的信息源和第一手的知识
  2. 把知识连成地图,将自己的理解反述出来
  3. 不断地反思和思辩,与不同年龄段的人讨论:讨论、交流很多情况下,比自己看书、看代码收获要多很多;
  4. 举一反三,并践行之,把知识转换成技能

学习有三个步骤:

  1. 知识采集:信息源是非常重要的,获取信息源头、破解表面信息的内在本质、多方数据印证,是这个步骤的关键;
  2. 知识缝合:所谓缝合就是把信息组织起来,成为结构体的知识,这里,连接记忆,逻辑推理,知识梳理 是很重要的三部分;
  3. 技能转换:通过 举一反三、实践和练习,以及教授传导,把知识转换成自己的技能,这种技能可以让你进入更高的阶层;

学习的目的

学习目的是什么呢?

  1. 学习是为了找到方法:学习不仅仅是为了找到答案,而更是为了找到方法,掌握了通往答案的路径和方法之后,便拥有了无师自通的能力;
  2. 学习是为了找到原理:学习不仅仅是为了知道,而更是为了思考和理解(真正的学习,从来都不是轻松的,而是那种你知道得越多,你的问题就会越多,你的问题越多,你就会思考得越多,你思考得越多,你就会觉得自己直到越少,于是你就会想要了解更多,这是一种螺旋式上升上下求索的状态),一旦掌握了这些本质的东西,你就会发现,整个复杂多变的世界在变得越来越简单;
  3. 学习是为了了解自己:学习不仅仅是为了开拓眼界,而更是为了找到自己的未知,为了了解自己,开拓眼界的目的就是为了发现自己的不足和上升空间,从而才能让自己成长;
  4. 学习是为了改变自己:学习不仅仅是为了成长,而更是为了改变自己(改变自己的思考方式和思维方式,改变自己与生俱来的那些垃圾和低效的算法)。

源头、原理和知识地图

挑选知识和信息源

对于计算机知识来说,学习英文是是否能够成长的关键,如果我们能用 Google 英文关键词就可以找到自己想要的知识,那么我们只是算得上能跟得上这个时代,但如果能在社区里跟社区里的大牛交流得到答案,这样才算是领先于这个时代。

信息源应该有以下几个特质:

  1. 第一手的资料,不是被别人理解过、消化过的二手资料,尤其对于知识性的东西来说,更是这样;
  2. 应该是有佐证、有数据、有引用的,或是有权威人士或大公司生产系统背书的资料,应该是被时间和实践检验过的,或是小心求证过的,不是拍脑袋野路子或是道听途说的资料;
  3. 应该是加入了一些自己的经验和思考,可以引发人深思的,是所谓信息的密集很大的文章。

耗子叔比较推荐 Medium 上的文章,这个上面的文章质量比较高。

注重基础和原理

基础知识和原理性的东西是无比重要的,无论是 JVM 还是 Node,或者是 Python 解释器里干了什么,它都无法逾越底层操作系统 API 对 『物理世界』的限制。

比如,当学习一门新的语言时,除了看每个语言都有的 if-else、for/while-loop、function 等东西外,还需要重点看的就是:

  • 出错处理是怎么玩的?
  • 内存管理是怎么玩的?
  • 数据封装和扩展是怎么玩的?
  • 多态和泛型是怎么搞的?
  • 运行时识别和反射是怎么玩的?
  • 并发编程是怎么玩的?

所以,最关键的是,这些基础知识和原理性的东西和技术,都是经历过长时间的考验的,这些基础技术也有很多人类历史上的智慧结晶,会给你很多启示和帮助(基础知识虽然很枯燥不实用、工作上用不到,学习这些知识是为了学得更快,基础打牢,学什么都快,而学得快就会学得多,学得多,就会思考得多,思考得多,就会学得更快…)。

使用知识图

耗子叔在这里介绍一个知识图的学习方式,通过这种方式可以让我们从一个技术最重要的主干的地方开始出发遍历所有的技术细节,以 C++ 为例,分为三部分:

  1. C++ 是用来解决 C 语言问题的,那么 C 语言有什么问题呢?指针、宏、错误处理、数据拷贝…C++是用什么技术来解决这些问题的?
  2. C++ 的面向对象特性:封装、继承、多态。封装,让我想起了构造函数、析构函数等。析构函数让我想起了初始化列表,想到了默认构造函数,想到了拷贝构造函数,想到了 new…多态,让我想到了虚函数,想到了 RTTI,RTTI 让我想起了 dynamic_casttypeid 等;
  3. C++ 的泛型编程,我想到了 templete,想到了操作符重载,想到了函数对象,想到了 STL,想到数据容器,想到了 iterator,想到了通用算法等等。

有了这样一颗知识树之后,当出现一些不知道的知识点时,可以往这棵知识树上挂,而这样一来,也使得我们的学习更为系统和全面。

深度、归纳和坚持实践

系统地学习

在系统性地学习一项技术时,耗子叔总结了一个学习模板,模板内容如下:

  1. 这个技术出现的背景、初衷和要达到什么样的目标或是要解决什么样的问题,这是这个技术的成因和目标(设计理念),也是这个技术的灵魂;
  2. 这个技术的优势和劣势分别是什么,或者说,这个技术的 tradeoff 是什么,任何技术都有其好坏,在解决一个问题的时候,也会带来新的问题,一般来说,任何设计都有 tradeoff,所以,需要知道这个技术的优势和劣势,以及带来的挑战;
  3. 这个技术的适用场景,要注意没有一个技术是普适的,每个技术都其特别适合的场景,所谓的场景一般分为两个:一个是业务场景,一个是技术场景;
  4. 技术的组成部分和关键点,这是技术的核心思想,也是这个技术的灵魂所在,学习技术的核心部分是快速掌握的关键;
  5. 技术的底层原理和关键实现,任何一个技术都有其底层的关键基础技术,学习这些关键的底层技术,可以让我们未来很快地掌握其他技术;
  6. 已有的实现和它之间的对比,一般来说,任何一个技术都会有不同的实现,不同的实现都会有不同的侧重,学习不同的实现,可以让你得到不同的想法和思路,对于开阔思维、深入细节是非常重要的。

举一反三

重点是如何才能让自己拥有举一反三的能力,在这方面,耗子叔对自己训练如下:

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

举一反三的能力,可以分解为:

  1. 联想能力:这种能力的锻炼需要你平时就在不停地思考同一个事物的不同的用法,或是联想与之有关的别的事物。对于软件开发和技术学习也一样;
  2. 抽象能力:抽象能力是举一反三的基本技能。平时你解决问题的时候,如果你能对这个问题进行抽象,你就可以获得更多的表现形式。抽象能力需要找到解决问题的通用模型,比如数学就是对现实世界的一种抽象。只要我们能把现实世界的各种问题建立成数据模型(如,建立各种维度的向量),我们就可以用数学来求解,这也是机器学习的本质;
  3. 自省能力:所谓自省能力就是自己找自己的难看。当你得到一个解的时候,要站在自己的对立面来找这个解的漏洞。有点像左右手互博。这种自己和自己辩论的能力又叫思辨能力。将自己分裂成正反方,左右方,甚至多方,站在不同的立场上来和自己辩论,从而做到不漏过一个 case,从而获得完整全面的问题分析能力。

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

总结和归纳

我们把学到的东西用自己的语言和理解重新组织并表达出来,本质上是对信息进行消化和再加工的过程,这个过程可能会有信息损失,但也可能会有新信息加入,本质上是信息重构的过程。我们积累的知识越多,在知识间进行联系和区辨的能力就越强,对知识进行总结和归纳也就越轻松。而想要提高总结归纳的能力,首先要多阅读,多积累素材,扩大自己的知识面,多和别人讨论,多思辨,从而见多识广。

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

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

实践出真知

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

坚持不懈

坚持本来也是一件反人性的事情,关于坚持的问题,大家应该都见过很多相似的文章,总之,坚持是一件看似简单、但是完成率非常低的事情。如果想要让自己能够坚持下去,最好能够让自己处于一个正反馈的循环中,比如,学习一个技术之后,与大家去分享自己的经验,或者整理出一篇博客让其他学习,都是一种很好的学习方法。

如何学习和阅读代码

读书还是读代码?

关于书/文档和代码的关系:

  • 代码:What、How & Details;
  • 书/文档:What、How & Why;

代码是具体的实现,但是并不能告诉你为什么?书和文档是人对人说的话,代码是人对机器说的话

  1. 如果想知道为什么要这么搞,应该去看书、看文档:特别当我们想了解一种思想、一种方法、一种原理、一种经验时,书和文档是最佳的方式、更有效率一些;
  2. 如果想知道是怎么实现的,实现的细节,应该去看代码:对于具体的实现,比如:某协程的实现、某模块的性能、某个算法的实现,这时候最好的方式就是去读代码;

至于从代码中收获大还是从书中收获大,不同的场景、不同的目的下,会有不同的答案,我个人对这部分的想法是:

  1. 工作的前几年,更多的时候应该关注代码、关注细节的实现、多写代码(当然不是说完全不看书,书是必须要看的,特别是当有了相关实战经验之后再去看书看,效果会更好),这个阶段,Google、Stack Overflow、Github 将会是最好的学习渠道,如果在过程中,还能获得一些技术影响力,那将再好不过了;
  2. 有一定经验之后,这时候需要更多的【理性认识】,在这个阶段,我们的想法不再是实现某个功能,可能是想做出更牛逼的东西来,这时候应该多读那些大牛的书、与大牛交流、关注国际顶级会议的论文,应该让自己往技术 leader 这个方向发展。

如何阅读源代码

关于如何阅读源代码,耗子叔分享了一些干货,我这里简单总结一下

首先是阅读代码之前,最好先有以下了解:

  1. 基础知识:相关的语言和基础技术的知识;
  2. 软件功能:需要知道这个软件是做什么的、有哪些特性、哪些配置项,最好能够读一遍用户手册,然后让软件跑起来,自己先用一下感受一下;
  3. 相关文档:读一下相关的内部文档;
  4. 代码的组织结构:先简单看下源码的组织结构。

接下来,就是详细地看代码的实现,这里耗子叔分享了一个源代码阅读的经验:

  1. 接口抽象定义:任何代码都会有很多接口或抽象定义,其描述了代码需要处理的数据结构或者业务实体,以及它们之间的关系,理清楚这些关系是非常重要的;
  2. 模块粘合层:我们的代码有很多都是用来粘合代码的,比如中间件(middleware)、Promises 模式、回调(Callback)、代理委托、依赖注入等。这些代码模块间的粘合技术是非常重要的,因为它们会把本来平铺直述的代码给分裂开来,让你不容易看明白它们的关系;
  3. 业务流程:这是代码运行的过程。一开始,我们不要进入细节,但需要在高层搞清楚整个业务的流程是什么样的,在这个流程中,数据是怎么被传递和处理的。一般来说,我们需要画程序流程图或者时序处理图
  4. 具体实现:了解上述的三个方面的内容,相信你对整个代码的框架和逻辑已经有了总体认识。这个时候,你就可以深入细节,开始阅读具体实现的代码了。对于代码的具体实现,一般来说,你需要知道下面一些事实,这样有助于你在阅读代码时找到重点。
    • 代码逻辑:代码有两种逻辑,一种是业务逻辑,这种逻辑是真正的业务处理逻辑;另一种是控制逻辑,这种逻辑只是用控制程序流转的,不是业务逻辑。比如:flag 之类的控制变量,多线程处理的代码,异步控制的代码,远程通讯的代码,对象序列化反序列化的代码等。这两种逻辑你要分开,很多代码之所以混乱就是把这两种逻辑混在一起了;
    • 出错处理:根据 2:8 原则,20% 的代码是正常的逻辑,80% 的代码是在处理各种错误,所以,你在读代码的时候,完全可以把处理错误的代码全部删除掉,这样就会留下比较干净和简单的正常逻辑的代码。排除干扰因素,可以更高效地读代码;
    • 数据处理:只要你认真观察,就会发现,我们好多代码就是在那里倒腾数据。比如 DAO、DTO,比如 JSON、XML,这些代码冗长无聊,不是主要逻辑,可以不理;
    • 重要的算法:一般来说,我们的代码里会有很多重要的算法,我说的并不一定是什么排序或是搜索算法,可能会是一些其它的核心算法,比如一些索引表的算法,全局唯一 ID 的算法,信息推荐的算法、统计算法、通读算法(如 Gossip)等。这些比较核心的算法可能会非常难读,但它们往往是最有技术含量的部分;
    • 底层交互:有一些代码是和底层系统的交互,一般来说是和操作系统或是 JVM 的交互。因此,读这些代码通常需要一定的底层技术知识,不然,很难读懂;
  5. 运行时调试:很多时候,代码只有运行起来了,才能知道具体发生了什么事,所以,我们让代码运行进来,然后用日志也好,debug 设置断点跟踪也好。实际看一下代码的运行过程,是了解代码的一种很好的方式。

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

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

面对枯燥和量大的知识

知识很多,在学习的时候要抓住本质,关注本质和原理,这些才是不容易改变的,是经得住时间考验的。带着问题去学习也是一种非常好的学习方式,耗子叔根据自己经验在专栏中分享以下几个 tips:

  1. 认真阅读文档:使用前之前看文档,跟遇到问题之后再看一遍使用文档,收获可能会完全不一样;
  2. 用不同的方式学习同一个东西:比如,看书、听课、写博客、讲课等;
  3. 不要被打断:被打断简直是学习天敌,保持自己注意力的集中;
  4. 总结压缩信息:面对太多的信息时,用一个自己的【压缩算法】抓住问题的关键点;
  5. 把未知关联到已知:把新学的知识关联到已知的事物上来;
  6. 用教的方式学习:这种方式对自己的能力会是一个极大的提升;
  7. 学以致用:把学到的东西用起来,在实践中深化自己的学习效果;
  8. 不要记忆:聪明的人不会记忆知识的,他们会找方法,那些可以推导出知识或答案的方法;
  9. 多犯错误:犯错会让你学到更多,通过错误总结教训。

这里有一个 TED 的演讲,TED演讲:只需20个小时,你就能学会任何事情!,保证自己全身心投入、不受外界打扰的情况下,只要20个小时,我们就能达到这里 如何学习开源项目-第三步,当然这20个小时要求是一个非常专注的20个小时,我还没有尝试过这种学习方法,近期准备尝试一次这种学习方法,到时候会写一篇文章来总结一下自己的经验。

最后,以矮大紧的一句话作为结束:【时代变来变去,确实有一些新的东西,但是在这样一个时代里,有一样东西没有变,就是有这样一群人,然后我们都读了一点书,受过不错的教育,然后对自己的心灵能长出什么东西,虽然不知道具体会长什么东西,但是拒绝全部种玉米、拒绝全部长土豆,希望心里有一亩田,有一天能长出一朵不知道是什么的花。(—来自《晓说》)】(这段话好像跟文章的主题没有什么关系,但不知为何突然想到了这段话,这里就列了出来)。

《极客时间-左耳听风》专栏,这里订阅有优惠


参考:

  • 极客时间-左耳听风-《高效学习》系列整理;