jdk源码分析-java.lang.Boolean
hi,我是阿昌
,今天学习分析一下jdk中的包装类Boolean;
针对java开发人员,这是一个十分十分十分十分常用的类,所有的项目中都会使用到这个类,我这里学习的代码是针对jdk1.8中的java.lang.Boolean
,看看Arthur van Hoff
大佬是如何编写代码的。
核心代码:
首先先针对整个类剔除掉所有的方法和静态变量,那Boolean
仅剩下如下核心代码:
1 | public final class Boolean implements java.io.Serializable,Comparable<Boolean>{ |
说到包装类,那上面其实就真的是只包装类一下基础类型boolean
的一个class;
那看到这个成员变量value是被final修饰的,他那跟java中的String一样,是一个immutable class(不可变类),意思就是经构造函数执行完毕后,这个变量就不会再改变了,也不允许改。
构造函数:
Boolean他有2个构造函数,如下
1 | public Boolean(boolean value) { |
另外注意到Boolean
类实际上只有两种不同状态的实例:
一个包装true
,一个包装false
,Boolean
又是immutable class,那在内存中相同状态的Boolean
实例完全可以共享,所以没有必用new
创建很多实例。因此Boolean class还提供两个静态变量:
1 | public static final Boolean TRUE = new Boolean(true); |
这两个变量在Class Loader装载时就被实例化,并且申明为final
,不能再指向其他实例。
提供这两个静态变量是为了让开发人员直接使用这两个变量而不是每次都new
一个Boolean
,这样既节省内存又避免了创建一个新实例的时间开销。
因此,用A场景
1 | Boolean flag = Boolean.TRUE; |
比B场景
1 | Boolean b = new Boolean(true); |
要好得多。
A场景会共用在Class Loader装载时就被实例化的变量,而下面B场景,每次都会new都会有新实例创建,而导致内存/时间开销
那如果一定要使用A场景来创建Boolean
,该咋办呢?
那其实B场景入参是一个基础类型来创建,那Boolean
也提供了一个静态工厂方法来直接调用获取Boolean
1 | public static Boolean valueOf(boolean b) { |
那这个静态工厂方法返回的是两个静态成员变量,也就是上面TRUE
和FALSE
之一,而不是通过new来返回。
虽然Boolean
非常简单,占用的内存也很少,但是一个复杂的类用new
创建实例的开销可能非常大,而且,使用工厂方法可以方便的实现缓存实例,这对开发人员是透明的。所以,能用工厂方法就不要使用new
。
和Boolean
只有两种状态不同,Integer
也是immutable class,但是状态上亿种,不可能用静态实例缓存所有状态。不过,SUN的工程师还是作了一点优化,Integer
类缓存了-128
到127
这256个状态的Integer
,如果使用Integer.valueOf(int i)
,传入的int
范围正好在此内,就返回静态实例。
hashCode():
hashCode()
方法很怪哈哈,两种Boolean
的hash code分别是1231
和1237
。那也可能Boolean.java的开发大佬对这两个数字有特别偏好:
1 | public static int hashCode(boolean value) { |
equals():
equals()
方法也很简单,先试判断是否Boolean类型,然后就试取到obj的成员变量值直接判断当前对象是否内存地址一致,那上面其实说了,Boolean
使用的静态成员变量TRUE
和FALSE
,来避免重复创建内存开销,那走的如果是这个入口创建,那对比时肯定内存地址是一样的
1 | public boolean equals(Object obj) { |
那针对上面如果传的是一个NULL
,那因为obj==null
,下一行的if (obj instanceof Type)
就肯定返回false
,因为null instanceof AnyType
永远是false
。
总结:
看了Boolean
的实现原理那可以学习到,针对如果一个类只有有限
的几种状态,考虑用几个final
的静态变量来表示不同状态的实例,然后通过静态变量,或工厂静态方法来获取,可节省内存的开销。
例如针对星期,编写一个Weekday
类,状态只有7个,就不要让用户写new Weekday(1)
来代表星期一,而是直接提供Weekday.MONDAY
静态变量,或Weekday.findOf(1)
来获取即可。
其实如上可以优化,针对Boolean不在提供public的构造方法,直接private私有,让用户只能通过上面的方法获得静态变量的引用或者静态成员变量获取,可以避免直接用构造函数new出来节省内存开销。然后equals()就可以优化为跟Object类的equals()方法
1 | public boolean equals(Object obj) { |