写在前面:
这是一个老生常谈的话题。本文只适合初学者!!
什么是自动装箱(AutoBoxing)?
官方文档:
http://java.sun.com/j2se/1.5.0/docs/guide/language/autoboxing.html
简单地翻一下,献丑了。\(^o^)/~
所有的Java程序员都知道的,不能把一个int(或者其他的原生数据类型的值)放入一个colletion中。collection中只能放置对象的引用,所以必须把原生数据类型包装(box)成相应的包装类。(比如,Integer是int的包装类)
举个例子:
List list = new ArrayList(); //list.add(1) // 这在JDK5之前,是不可以的。 list.add(new Integer(1)) // 只能这样写
(我开始学Java已经是JDK6了,没体验过这种情况。⊙﹏⊙b)
有了JDK5的autoboxing呢,你就可以这样了:
// 使用了JDK5的泛型。可以简单的理解为,是对List中元素类型的约束! List<Integer> list = new ArrayList<Integer>(); list.add(1); // 写着方便多了吧 O(∩_∩)O~
自动装箱是怎么回事?
肯定是编译器的优化,那编译器是怎么做的呢?
package tiger; public class AutoBox { { Integer i = 15; } }
检查一下AutoBox.class的字节码文件:
Code:
Stack=1, Locals=2, Args_size=1
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: bipush 15
6: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
9: astore_1
10: return
LineNumberTable:
line 2: 0
line 4: 4
line 5: 10
第6行,可以发现,编译器编译时,在class中加入了Integer.valueOf()方法。也就相当于:
package tiger; public class AutoBox { { Integer i = Integer.valueOf(15); } }
Integer.valueOf(int i) 的 JDK源码( JDK6 update16)。
public static Integer valueOf(int i) { if(i >= -128 && i <= IntegerCache.high) return IntegerCache.cache[i + 128]; else return new Integer(i); }
自动装箱的性能问题
从上述叙述中,知道了自动装箱,其实是编译器在背后做了工作,即在class中加入了Integer.valueOf方法。
如果代码中存在大量的 自动装箱的需要,就会比较浪费效率。
然而在上述JDK源码的valueOf中,可以发现,JVM对Integer做了缓存(cache)。缓存的范围是 -128 到 IntegerCache.high.
让我们更进一步看看:
JDK的IntegerCache源码:
private static class IntegerCache { static final int high; static final Integer cache[]; static { final int low = -128; // high value may be configured by property int h = 127; if (integerCacheHighPropValue != null) { // 得到系统参数integerCacheHighPropValue的值 int i = Long.decode(integerCacheHighPropValue).intValue(); // 指定的参数不能小于127,否则会设为127 i = Math.max(i, 127); h = Math.min(i, Integer.MAX_VALUE - -low); } // 127=< high <=Integer.MAX_VALUE-128 high = h; // 填充满Integer缓存 cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); } private IntegerCache() {} }
JVM默认缓存了-128 -- 127 之间的所有的Integer。有了缓存之后,就可以降低自动装箱对性能的影响了。
缓存带来的问题(比较无聊的问题) o(︶︿︶)o
public class T { public static void main(String[] agrs){ int i = 128; int j = 128; System.out.println(i==j); Integer a = 126; Integer b = 126; System.out.println(a==b); Integer one = 128; Integer two = 128; System.out.println(one == two); } }
true
false
分别解释:
i -- j 组:
2个普通的原生数据类型。比较的是128 == 128 (当然相等了),输出true;
a -- b 组:
存在自动装箱,因此JVM在运行时调用Integer.valueOf方法,上文提到JVM默认缓存了-128 -- 127 之间的所有的Integer。所以a 、b都是对缓存中的Integer(126)的引用。所以输出true。
one -- two 组:
存在自动装箱,因此JVM在运行时调用Integer.valueOf方法,但是128不在缓存的范围内,Integer.valueOf接着调用new Interger(128)的方法,所以相当于:
Integer one = new Integer(128); Integer two = new Integer(128);
当然返回false了。
PS:这种缓存问题还存在于 Byte Character Long Short 中。
总结:
那么什么时候使用自动装箱(autoboxing)和自动拆卸(unboxing)呢?仅当在引用类型(也即是包装类)与原生数据类型之间存在 “阻抗失谐”时,比如你想将一个数值 放入到 collection中。在科学计算或者其他性能要求高的数字计算中,是不适合使用自动装箱(autoboxing)和自动拆卸(unboxing)。自动装箱(autoboxing)和自动拆卸(unboxing)模糊了原生数据类型与引用类型之间的区别,但是并没有完全消除这种差别。