为了让广大工程师们更好的提升个人技术能力和思想,我将开通一个大专栏《成长记》系列,会包含Java架构和大数据从底层技术到源码原理的分享,敬请关注!
无论作为Java程序员还是大数据工程师的你,工作时间久了,可能很多底层和基础技术已经还给你的大学老师或者80%已经丢在脑后了。比如你可能已经不记得网络模型有几层?TCP三次握手,四次挥手的流程你可能也说不上来了?JDK集合和并发包的源码你可能压根没有看过,又或者很早之前你看过,但不记得了。甚至并发包下很多东西都没有用过。你也可能正在准备跳槽或者面试,被各种基础虐过,又或者你看某个技术框架的源码晦涩难懂,无从入手……
或多或少你可能都遇见过这样的场景,所以在这个成长记系列中,就是让你解决这些问题。我不仅会教你学会这些底层和基础知识,解决上面这些问题,成长记更重要的核心是:除了给你带来能力、知识、方法的成长,更重要的是观念和心态的成长。所以你可以在每一个专栏中学习到很多观念、思想,这将潜移默化的提升你的认知,让你得到的不仅仅是 “鱼”,更是会得到“渔”。
作为《成长记》的第一个专栏,《JDK成长记》这篇专栏主要让你掌握以下三点:
- JDK的源码中集合和并发包下的各种类的底层原理,跳槽面试遇见这块知识不再心虚。
- 在使用JDK集合和并发时,能更得心应手,能更好的解决遇见的各种线上问题。
- 更主要的是让你学会基本的阅读源码的思想。
作为程序员的我们,其实建造了整个社会计算机技术的底层系统。但是,除此之外,你更要建立自己思想观念的底层系统,这个底层系统不仅会对你技术能力的提升和职业发展有帮助,更会帮助你的生活,乃至整个人生变得更美好。而每个人的底层系统都不尽相同,所以都需要不断的修正、不断的夯实。这个是《成长记》给你们的另一种成长。跟随《成长记》的各个专栏,相信你将学到的不仅仅是知识和技能,还会有更多的思想和观念。
好了,接下来,就让我们进入JDK源码的大门吧!
作为Java工程师,ArrayList你应该非常熟悉。当你还是一个菜鸟Java工程师的时候,面试时你可能经常被问到:你能说说ArrayList的基本原理和优缺点吗?它默认容量大小是多少呢?怎么进行扩容的呢?诸如此类,不知道各位当初是怎么回答的。或者可能你已经是一名老鸟的工程师了,是不是也常忘记给ArrayList设置容量大小。又或者老鸟的你可能要跳槽了,所以让我们一起回顾下ArrayList的基础知识吧!
ArrayList基本原理
一句话讲,在JDK中,ArrayList底层基于一个Object[]数组来维护数据。
ArrayList优缺点
缺点:
- 容量受限时,需要进行数组扩容,进行元素拷贝会影响性能
- 频繁删除和往中间插入元素时,产生元素挪动,也会进行元素拷贝,影响性能
- 不是线程安全的
优点:
- 随机访问某个元素,性能很好
建议:
维护顺序插入的数据,遍历或者索引访问很方便,预估数据大小,避免频繁扩容。不适合频繁的删除和中间插入等操作和多线程操作。
你回顾了ArrayList的基本的原理和优缺点后,各位最好可以自己跟别人茶余饭后的聊一聊,问下他,这个问题,或者你给他讲讲,来巩固下这个基础知识。
接下来,你就可以开始看ArrayList的源码了,在看之前,你要先了解下看源码的最基本的一条思想。
当你打开源码,肯定不是直接就开始从头开始看一遍,这样会一头雾水,所以肯定是有思路和方法可寻的。
在这里,首先你要明白,看核心源码和精读源码的方式不太一样。看核心源码,也就是核心部分的源码,一般也就是10-30%。你应该都是从一些入口开始看起,但是如果你要精读源码,除了在阅读核心源码的基础上,你还需要将源码拆解开来,研究每一个组件的各个作用,之后再从入口开始,一行一行都读懂。
这其实体现了一个很重要的思想,无论做什么事,先摸脉络,后看细节。就比如瞎子摸象一样,你每个部位摸了一遍,但是没有全局的认识,这就很搞笑了。所以一般你应该先看核心源码再精读源码。
除了上面的思路,读源码其实还有很多技巧和方法,这个见仁见智,在这个成长记中相信你也会学到很多。但是有一点我要跟大家说的是,你可能在网上看到有很多diss这个看源码方式,那个看源码方式,也有各个博客,论坛各种五花八门的方式看源码。所以很多时候,你一定要透过现象看本质,其实看源码的方法和技巧,没有优劣之分,这要围绕你的目标和场景来具体决定,选择合适的方式才是最好的。你想下,很多事情都是不是这样的?大家不能一概而论,这个思想很重要,你要学会要站到一定高度看问题,别总是陷入问题本身。
根据我的经验和了解到的各个阅读源码的方法,你可以在本章结尾看到总结。
看源码前,按照之前提到,你应该先看脉络,再看细节。
比如,当你看ArrayList的源码,可以先看看它有哪些核心成员变量,刚开始,你肯定不知道哪些是核心的变量,所以简单过一下即可,根据名字,类型,大概看看即可,看不懂也没关系。之后看下有哪些方法,根据名字大概猜猜是做什么的。做这些主要让你了解基本的脉络,对源码有个大体映象,千万不要在这里细究源码。
在Intellij或Eclipse下,如果是使用eclipse快捷键的话,你可以通过Ctrl+O看到这个类的大体情况:
这里,你主要有哪些方法和成员变量、内部类就可以了。
比如,你可以看到ArrayList源码中,有几个静态成员,从名字上看像是容量大小相关的,还有size和elementData等成员变量,有几个构造函数,
有一些ensureCapacity,calculateCapacity等私有方法,感觉也是和容量大小有关的。因为Capacity就是容量,大小的意思。还有很多你应该常用的方法add(),set(),get()等等。
最后下面,还有几个内部类SubList和Itr和ListItr什么的,如下图所示:
好了,其实你看到这里就可以了,起码对ArrayList的源码有了个大概认识,这就是先脉络后细节的思想体现。
之前我们提到过,看核心源码一般是从入口开始,也就常说的自顶而下。
首先你要有个代码Demo。之后从它的入口开始看起,代码清单如下:
import java.util.List;
public class ArrayListDemo {
public static void main(String[] args) {
List list = new ArrayList<>();
}
}
这个很简单的代码,入口是main函数,第一行就是创建了一个ArrayList,里面的元素都是String类型,使用默认无参的构造函数,你点击构造函数,进入源码来看看:
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;private int size;
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
可以看到,有一个成员变量叫elementData的Object[]数组,这个印证了我们之前提到过的ArrayList底层基本原理是数组。而且你记不记得之前,在看ArrayList源码脉络的时候是不是已经看到过了这个变量呢?
默认无参的构造函数让elementData指向了和空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA一样的地址。
如果各位如果有印象的话,DEFAULTCAPACITY_EMPTY_ELEMENTDATA这个变量也是你之前看到过的静态成员变量。
上面还列了一个成员变量size,你可以连蒙带猜一下,它应该是描述数组大小的变量。而且size是int类型,大家都知道int的默认值是0。
所以小结一下,你知道了new ArrayList<>()这个动作,底层是使用了Object[]数组,但是这个数组默认是空的,大小为0。
上面我们主要通过看脉络,连蒙带猜的思想,看了一个简单的不能再简单的源码。不知道你有没有感觉到这两个思想。有了初步的感受。接下来我还要引入一个非常关键的方法:画图。
其实在我看来,没有什么算法不能用画图和举例理解的,起码一般大多数算法都可以理解的,小灰算法图解微信公众号就是最好的例子。在阅读源码的时候也是,画图能更好的理解源码,能让我们站在高处,高屋建瓴的摸清楚源码的脉络,也能梳理出来主要思路,让我们去更好的阅读源码。所以大家不能忽视了,再简单的场景,也要养成这个画图这个思路习惯。
上面ArrayList的无参构造函数的源码,基本上如下图所示:
好了,今天知识就分享到这里,下一节我们来基本的使用一下ArrayList,调用它的几个常用方法,再来看看它常用方法的源码和原理。前几节的节奏我会尽量慢一些,大家不要着急,后面会越来越精彩!
继续往后看有彩蛋哟~
首先说源码的获取,一般以下几种
- 下载源码导入Intellij等IDE,来阅读代码
- 开启Maven自动下载源码的功能
- 自己从官方(如Github)或者Maven仓库手动下载源码
这几种都可以获取到源码,各有各的适用场景,比如如果你想写些注释,就需要导入到IDE才行,比如你想Debug源码,不需要把源码导入到IDE中,适用2,3的方法都可以。又比如有些代码Maven仓库根本没有,只能从Github或官方下载了。
其次,我们聊下,阅读静态源码还是动态源码?这个取决于源码的复杂程度、你阅读源码的能力等。如果是一些简单的源码或者你有很多阅读源码的经验了,一般阅读静态源码就足够了。但是如果是小白,或者真的源码比较复杂,又或者你需要具体看看执行过程中的某个变量和返回值,你肯定需要debug进行动态源码阅读了。
阅读源码的技巧,说白了就是一些思想,常见的有:
- 自顶而下或自底而上
- 抓大放小
- 先脉络,后细节
- 连猜带蒙
- 结合功能场景逐个分析
方法就更多了,主要有:
- 看注释
- 加注释
- 画图
- Debug
- 观察日志或增加日志
- ……
上面这些,之后我都会带大家使用到的,并且你会学会在合适的场景使用上,你只要持续关注成长记就可以了。
最后,还要说一点,有人说阅读源码,需要很多技术基础,的确没错。但是也分是什么样的源码,如果只是简单的源码,需要的基础知识会很少,比如JDK的源码。但是如果比较难的源码,Zookeeper、HDFS、Kafka、Dubbo这些源码,就需要掌握Netty,NIO,网络,Java集合和并发等基础才能看得更好。当然如果没有这些基础也不是不能看的,就是理解可能不会深,对源码最后可能只是初步了解。所以还是那句话,不要一概而论。
本文由博客一文多发平台 OpenWrite 发布!