java的autoboxing auto-unboxing
java 5开始引入了autoboxing 和auto-unboxing机制,方便了基本类型和其对应的wrapper类型的转换。比如我们可以直接把一个基本类型的值赋给其wrapper类型,反之亦然;可以把一个基本类型直接放入容器里,封装的过程由编译器来完成;调用方法的时候可以直接匹配参数autoboxing和auto-unboxing之后的版本。
这个过程编译器也只是做了个简单的处理,通过wrapper class的valueOf()方法对基本类型进行包装,通过wrapper class 的"基本类型名称"+Value() 方法得到其基本类型。
比如
Integer i=5;
int ii=i;
编译器将其变换为:
Integer i=Integer.valueOf(5);
int ii=i.intValue();
对于其他基本类型是一致的,不过auto-unboxing使用的方法名要根据类型变换,比如boolean类型的Boolean.booleanValue(),byte的Byte.byteValue()等等。
autoboxing的时候为什么使用valueOf() 方法而不使用new 来创建呢?
看文档的解释:
Integer的valueOf()方法:因为该方法有可能通过缓存经常请求的值而显著提高空间和时间性能。
注意两点:
1.重点:缓存!(设计模式中的享元模式)
Boolean类型中直接缓存了两个Boolean对象,TRUE和FALSE,这样使用valueOf()方法时只需要直接返回这两个对象中的一个,而不是每次调用的时候都用new,这也就是文档里所说的通过缓存经常请求的值二显著提高空间和时间性能。
Byte,Integer 和Long都是缓存了-128~+127之间的对象,autoboxing的时候,如果需要boxing的值在此范围之内,则直接返回缓存的对象,没有的时候再去new.
Character因为类型的特殊性,保存的是0-127之间的对象。
举个实际的例子,这是Integer中的实现:
- private static class IntegerCache {
- private IntegerCache(){}
- static final Integer cache[] = new Integer[-(-128) + 127 + 1];
- static {
- for(int i = 0; i < cache.length; i++)
- cache[i] = new Integer(i - 128);
- }
- }
- public static Integer valueOf(int i) {
- final int offset = 128;
- if (i >= -128 && i <= 127) {
- return IntegerCache.cache[i + offset];
- }
- return new Integer(i);
- }
知道了上面这点,Integer i1=1;Integer i2=1; (i1==i2)这个表达式为true的原因也就清楚了。
2.“可能”
文档里只说是可能,也就是说并不保证这一点,不同的类库实现起来可能是存在差别的。因此,比较引用所指向的对象的值时我们仍然要使用equals() 方法,比较引用是否相同时,使用==。
还有一点比较有意思,<Java Puzzlers>一书里的一个例子:
请提供i1和i2的声明,(i1!=i2)&&(i1<=i2)&&(i1>=i2)为true。
看到i1<=i2 并且i1>=i2都成立,我们自然的想法就是i1==i2,但是java中的autoboxing和auto-unboxing破坏了这点。
看这个声明:
Integer i1=new Integer(0);
Integer i2=new Integer(0);
那么i1>=i2和i1<=i2都是true,因为使用>=和<=的时候会进行auto-unboxing操作,实际比较的是i1和i2auto-unboxing之后的基本类型值,因此这两个都是true,而i1和i2明显指向的不是同一个对象,所以i1!=i2也是true。