自动装箱拆箱要点
自动装箱时编译器调用valueOf将原始类型值转换成对象,看Integer类的valueOf源码
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
先读缓存,缓存没有再创建Integer对象.
同时自动拆箱时,编译器通过调用类似intValue(),doubleValue()这类的方法将对象转换成原始类型值。
public int intValue() { return value; }
何时发生自动装箱和拆箱
自动装箱和拆箱在Java中很常见,比如我们有一个方法,接受一个对象类型的参数,如果我们传递一个原始类型值,那么Java会自动讲这个原始类型值转换成与之对应的对象。最经典的一个场景就是当我们向ArrayList这样的容器中增加原始类型数据时或者是创建一个参数化的类,比如下面的ThreadLocal。
ArrayList<Integer> intList = new ArrayList<Integer>(); intList.add(1); //autoboxing - primitive to object intList.add(2); //autoboxing ThreadLocal<Integer> intLocal = new ThreadLocal<Integer>(); intLocal.set(4); //autoboxing int number = intList.get(0); // unboxing int local = intLocal.get(); // unboxing in Java
要注意的事项
这是一个比较容易出错的地方,”==“可以用于原始值进行比较,也可以用于对象进行比较,当用于对象与对象之间比较时,比较的不是对象代表的值,而是检查两个对象是否是同一对象,这个比较过程中没有自动装箱发生。进行对象值比较不应该使用”==“,而应该使用对象对应的equals方法。看一个能说明问题的例子。
public class AutoboxingTest { public static void main(String args[]) { // Example 1: == comparison pure primitive – no autoboxing int i1 = 1; int i2 = 1; System.out.println("i1==i2 : " + (i1 == i2)); // true // Example 2: equality operator mixing object and primitive Integer num1 = 1; // autoboxing int num2 = 1; System.out.println("num1 == num2 : " + (num1 == num2)); // true // Example 3: special case - arises due to autoboxing in Java Integer obj1 = 1; // autoboxing will call Integer.valueOf() Integer obj2 = 1; // same call to Integer.valueOf() will return same // cached Object System.out.println("obj1 == obj2 : " + (obj1 == obj2)); // true // Example 4: equality operator - pure object comparison Integer one = new Integer(1); // no autoboxing Integer anotherOne = new Integer(1); System.out.println("one == anotherOne : " + (one == anotherOne)); // false } } Output: i1==i2 : true num1 == num2 : true obj1 == obj2 : true one == anotherOne : false
值得注意的是第三个小例子,这是一种极端情况。obj1和obj2的初始化都发生了自动装箱操作。但是处于节省内存的考虑,加载Integer.class文件的时候会通过静态代码块中缓存-128到127的Integer对象。因为obj1和obj2实际上是同一个对象。所以使用”==“比较返回true。
看源码
static { // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h; cache = new Integer[(high - low) + 1]; int j = low;
//通过循环创建-128到127的对象,放到cache[] 中,当调用Integer.valueOf(-127到128之间的数),会返回相同的对象,其他的数直接调用 new Integer(i) 创建新的对象 for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; }
最后看下面的测试类
public class Test { public static void main(String[] args) { Integer i = 500; System.out.println(i == 500); } }
返回为 true .说明 i 发生了拆箱操作,500 并没有发生装箱操作.如果发生装箱操作 Integer.valueOf(500) ,会产生一个新的对象,通过 == 比较会返回false.所以得出结论:
基本类型和包装类进行 == 比较时,包装类进行拆箱操作,比较的是基本类型的值