探秘Java的自动装箱与拆箱
Java提供了8种基本数据类型:byte、short、int、long、char、boolean、float和double,然而它们却不能像引用类型那样向上转型为Object
,也无法使用泛型。因此,Java提供了它们对应的引用类型的包装类:Byte、Short、Integer、Long、Character、Boolean、Float和Double
什么是自动装箱与自动拆箱
在Java 5
开始就提供了自动装箱/拆箱的特性,装箱
就是将基本数据类型转换为它的包装类,而拆箱
就是将包装类转换为基本数据类型。
// 自动装箱 Integer a = 1; // 自动拆箱 int b = a;
自动装箱/拆箱的实现原理
我们使用javac
编译上面的代码,然后使用javap
来查看一下字节码,可以看到这么一部分
0: iconst_1 1: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 4: astore_1 5: aload_1 6: invokevirtual #3 // Method java/lang/Integer.intValue:()I 9: istore_2 10: return
自动装箱调用了包装类的valueOf
方法,而自动拆箱调用了xxxValue
方法,就是这么简单
包装类缓存
观察如下代码,思考一下输出结果
public class Main { public static void main(String[] args) { Integer a = 1; Integer b = 1; Integer c = 233; Integer d = 233; System.out.println(a == b); System.out.println(c == d); } }
返回两个true?还是返回两个false?
正确答案是true false
。是不是有点出乎意料?我们进入Integer.valueOf()
方法的源码来一探究竟。
valueOf的实现
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
我们看到,它首先判断i值的范围,如果在IntegerCache.low
和IntegerCache.high
之间的话,就从名为IntegerCache.cache
的缓存数组中返回包装类的对象。
private static class IntegerCache { static final int low = -128; static final int high; static final Integer cache[];
low的值为-128
,而high的值一般默认为127
。这就很好的解释了为什么1和1自动装箱的引用相等,而233和另一个233装箱后的引用不同。
其他包装类的valueOf
大家可以自行查看源代码,这里只做一个简单总结
- byte:全部缓存,-128-127
- short:缓存-128到127
- int:缓存-128到high(默认127)
- long:缓存-128到127
- char:缓存0-127
- boolean:使用名为TRUE和FALSE的缓存
- float:不缓存
- double:不缓存
装箱拆箱与运算的复杂情况
先下结论:当 ==运算符的两个操作数都是包装器类型的引用,则是比较是否是同一个实例,而如果其中有一个是基本数据类型则比较数值(自动拆箱)。此外,对于包装器类型,equals方法不会进行类型转换。
综合练习
public class Main { public static void main(String[] args) { Integer a = 1; Integer b = 2; Integer c = 3; Integer d = 3; Integer e = 321; Integer f = 321; Long g = 3L; Long h = 2L; System.out.println(c == d); // true System.out.println(e == f); // false System.out.println(c == (a + b)); // true System.out.println(c.equals(a + b)); // true System.out.println(g == (a + b)); // true System.out.println(g.equals(a + b)); // false -> Integer不是Long实例 System.out.println(g.equals(a + h)); // true } }