其实阅读Java源码本身并没有那么难,只要我们踏出第一步,后面反而比我们想象的要简单。可能很多Java初学者并不知道从哪里下手,所以本篇博客将挑选一个源码较为简单的工具类—— java.util.Objects
作为切入点,走入Java源码的世界。
相关说明
- IDE:
IDEA
- Java Version:
1.8.0_161
Objects
Objects
位于 java.util
包下,看名称就能知道是一个工具类,除去私有的构造方法外,只有12个静态方法供我们使用,为了方便阅读,这里我已经去掉注释:
1 | package java.util; |
看上去很熟悉,并不会很陌生对吧,其实阅读源码也没有想象的那么难。
构造方法
这个工具类首先引入眼帘的便是其私有的构造方法:
1 | private Objects() { |
如果你有阅读上面的源码就会发现,Objects
提供的方法除此外皆为静态方法,当我们使用这些方法的时候无需显示实例化对象,而是直接通过 类名.方法名()
来调用其提供的方法,而私有其构造方法便是以防我们无意义创建对象浪费空间,所以这里抛出一个断言异常—— "No java.util.Objects instances for you!"
。
说到构造方法私有化想必大家有点耳熟,没错,我们在学习单例模式的时候便将对象的构造方法私有化,不允许用户去 new
该对象,而是通过我们提供的方法去获取已经创建好的实例。
equals
接下来我们正式开始介绍 Objects
最为常用的方法 equals
:
1 | public static boolean equals(Object a, Object b) { |
这也很简单对吧,我们也写过这样的代码,判断两个对象是否相等,代码就一行,翻译一下就是判断参数列表中的两个对象是否为同一个对象(引用是否指向同一内存地址),或者在参数 a
不为 null
的情况下,调用 a
的 equals
方法,而a的 equals 方法要看其具体的实现,如果是Object
(所有对象的父类),则:
1 | public boolean equals(Object obj) { |
equals of String
但这不能满足我们日常使用需求,实际开发过程中,假如 Student
的 id
相同,那么我们就可以认为 equals
为 true
,所以Object
的equals
方法并不能满足这种情况,所以有必要的时候大家都会重写一下equals
方法(重写 equals
方法的同时最好重写一下 hashCode
),这里就介绍一下String
的equals
实现(为了方便阅读,我添加了些许注释):
1 | public boolean equals(Object anObject) { |
可能大家对最后一段 while
那里有点疑惑,其实我们完全可以用 for
来替代 while
:
1 | int n = value.length; |
其实第一次看到 String
的 equals
方法就差点笑出声,感觉这个作者写的小心翼翼(提高性能),判断字符串是否相等经历如下步骤:
- 判断两个对象是否相等,对象都想相等了,我们还用判断吗?
- 什么?对象不相等,那么只能遍历
char[]
一个个字符去判断了。。。不对!如果数组长度不一样那肯定也不相等,所以我们在循环前线判断一下字符串底层数组的长度吧。 - oops!连底层数组长度都一样,这下没办法了,老老实实去一个个判断吧,还好只要判断相同位置上的字符是否相等就行,一个
for
循环搞定!
说好的阅读 Objects
源码,去花费大量篇章去介绍 String
的 equsla
方法了,赶紧回到正题。
其实Objects.equals() 实现还是通过调用其 参数的自身实现,不过在这之前贴心的帮我们做了非空判断以及一些优化—— a == b
,没错,就是这个小小的 a == b
,为什么说是优化呢?首先是避免空指针异常,其次是通过String的equals方法我们知道,判断两个字符串是否相等的原理是一个一个去对比其底层char数组里面的元素,但是如果a b
是同一对象,我们判断其内存地址便可,比对底层数组里面的元素未免显得有些浪费性能了。
其他
篇幅有限,其他方法无法一个个扩展开来去讲,就稍微介绍一下吧:
deepEquals
是通过Arrays.deepEquals0() 来支持两个数组是否相等(顺序),如果对象不是数组,则返回false;hashCode
调用了对象自身的hashCode
实现hash
调用了Arrays.hashCode
的实现,来计算数组的hash
值toString
是调用了String.valueOf
方法,这点和Object
的实现不一样(Object
为getClass().getName() + "@" + Integer.toHexString(hashCode())
)- oString(Object o, String nullDefault) 如果对象本身不为空则调用其toString方法,为空就使用其默认值nullDefault
compare(T a, T b, Comparator<? super T> c)
方法,如果我们了解过Java8
的新特性就会知道这么一个词——行为参数话,就拿Student
对象来举例,compare
两个Student
,我们可以compare
的属性有很多,比如年龄,身高,成绩等等。这样就导致一个compare
方法我们可能要重载多次,但是通过行为参数化,我们将比较的这个行为(比较年龄?比较身高?)当作参数传入,就只用写一个compare
方法,比如这里面的最后一个参数——Comparator<? super T> c
,这是一个函数式接口,是我们进行对象比较的具体行为,可以参考我之前写的 行为参数化 这篇文章- 以及一些针对
null
封装的一些方法,自行阅读即可。
Java的源码解读是我很早就想做的一件事,但由于自身水平所限,写出来的文章达不到我理想的效果,请多见谅。