闲来无事,开始研究JDK 1.5源码,先找了一个最简单的java.lang.Boolean
开始解剖。
首先我们剔除所有的方法和静态变量,Boolean
的核心代码如下:
public final class Boolean implements java.io.Serializable,Comparable {
private final boolean value;
}
很明显,凡是成员变量都是final
类型的,一定是immutable class,这个Boolean
和String
一样,一旦构造函数执行完毕,实例的状态就不能再改变了。
Boolean
的构造方法有两个:
public Boolean(boolean value) {
this.value = value;
}
public Boolean(String s) {
this(toBoolean(s));
}
另外注意到Boolean
类实际上只有两种不同状态的实例:一个包装true
,一个包装false
,Boolean
又是immutable class,所以在内存中相同状态的Boolean
实例完全可以共享,不必用new
创建很多实例。因此Boolean class还提供两个静态变量:
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
这两个变量在Class Loader装载时就被实例化,并且申明为final
,不能再指向其他实例。
提供这两个静态变量是为了让开发者直接使用这两个变量而不是每次都new
一个Boolean
,这样既节省内存又避免了创建一个新实例的时间开销。
因此,用
Boolean b = Boolean.TRUE;
比
Boolean b = new Boolean(true);
要好得多。
如果遇到下面的情况:
Boolean b = new Boolean(var);
一定要根据一个boolean
变量来创建Boolean
实例怎么办?推荐使用Boolean
提供的静态工厂方法:
Boolean b = Boolean.valueOf(var);
这样就可以避免创建新的实例。看看valueOf()
静态方法:
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
这个静态工厂方法返回的仍然是两个静态变量TRUE
和FALSE
之一,而不是new
一个Boolean
出来。虽然Boolean
非常简单,占用的内存也很少,但是一个复杂的类用new
创建实例的开销可能非常大,而且,使用工厂方法可以方便的实现缓存实例,这对客户端是透明的。所以,能用工厂方法就不要使用new
。
和Boolean
只有两种状态不同,Integer
也是immutable class,但是状态上亿种,不可能用静态实例缓存所有状态。不过,SUN的工程师还是作了一点优化,Integer
类缓存了-128
到127
这256个状态的Integer
,如果使用Integer.valueOf(int i)
,传入的int
范围正好在此内,就返回静态实例。
hashCode()
方法很奇怪,两种Boolean
的hash code分别是1231
和1237
。估计写Boolean.java的人对这两个数字有特别偏好:
public int hashCode() {
return value ? 1231 : 1237;
}
equals()
方法也很简单,只有Boolean
类型的Object
并且value相等才返true
:
public boolean equals(Object obj) {
if (obj instanceof Boolean) {
return value == ((Boolean)obj).booleanValue();
}
return false;
}
很多人写equals()
总是在第一行写一个null
判断:
if (obj==null)
return false;
其实完全没有必要,因为如果obj==null
,下一行的if (obj instanceof Type)
就肯定返回false
,因为null instanceof AnyType
永远是false
。
详细内容请参考《Effective Java》第7条:Obey the general contract when overriding equals。
如果一个类只有有限的几种状态,考虑用几个final
的静态变量来表示不同状态的实例。例如编写一个Weekday
类,状态只有7个,就不要让用户写new Weekday(1)
,直接提供Weekday.MONDAY
即可。
要防止用户使用new
生成实例,就取消public
构造方法,用户要获得静态实例的引用有两个方法:如果申明了public static var
,就可以直接访问,比如Boolean.TRUE
,第二个方法是通过静态工厂方法:Boolean.valueOf(?)
。
如果不提供public
构造方法,让用户只能通过上面的方法获得静态变量的引用,还可以大大简化equals()
方法:
public boolean equals(Object obj) {
return this==obj;
}