Integer的自动装箱与拆箱
程序员文章站
2024-03-07 13:04:45
...
装箱和拆箱
- 装箱:将基本数据类型转换成封装类型。
- 拆箱:将封装类型转换成基本数据类型。
自动装箱和自动拆箱
在jdk1.5开始增加了自动装箱和自动拆箱机制,就是为了方便基本类型和封装类型之间的互相转换。
下面来看看自动装箱(拆箱)和显示装箱(拆箱)的例子:
Integer a1 = 3; // 自动装箱
Integer a2 = Integer.valueOf(3); // 显示装箱
int a3 = new Integer(3); // 自动拆箱
int a4 = new Integer(3).intValue(); // 显示拆箱
自动装箱 / 拆箱的实现
其实自动装箱或拆箱是通过编译器自动执行的,当然调用的方法还是一样的。下面来看看源码实现。以Integer为例:
Integer.valueOf(int i)
首先进入valueOf方法。这里先判断传入的值是否在IntegerCache.low和IntegerCache.high范围之内,如果在则从IntegerCache.cache数组中直接返回一个对象,否则就new一个新的对象。
public static Integer valueOf(int i) {
// -128 < i < 127,返回一个缓存对象
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
// 否则新建一个对象
return new Integer(i);
}
这里的IntegerCache是个私有内部静态类,具体看代码中的注释。这里可以清楚的知道上面说的缓存的范围是在-128~127之间,但是最大值可以通过虚拟机参数修改。
private static class IntegerCache {
static final int low = -128; // low默认为-128
static final int high; // high的初始化在静态代码块中
static final Integer cache[]; // 存放缓存对象的数组
static {
// 最高值可能由虚拟机参数设置
int h = 127;
// 这里可以通过VM参数-XX:AutoBoxCacheMax来设置high的值
String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
// 判断是否设置了参数
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
// high不能小于127
i = Math.max(i, 127);
// high的最大值不能超过Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// 如果参数不能解析为int值,则直接忽视它
}
}
// 在这里设置high的值,如果没有设置参数,那么它将是默认的127
high = h;
// 初始化cache数组,将容量设置为high - low,也就是刚好装下-128~127的Integer对象
cache = new Integer[(high - low) + 1];
int j = low;
// 将-128~127的Integer对象都初始化,然后放在cache中
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;
}
private IntegerCache() {}
}
Integer.intValue()
这个就没啥好说的啦,就是直接返回这个数值。
public int intValue() {
return value;
}
下面来看一下可能出现的面试 / 笔试题:
// Integer类型的实例
Integer a1 = new Integer(127);
Integer a2 = new Integer(127);
Integer a3 = new Integer(128);
// 基本数据类型
int a4 = 128;
// 自动装箱得到Integer类型的实例
Integer a5 = a3.intValue();
Integer a6 = 127;
Integer a7 = 127;
// 显示装箱得到Integer类型的实例
Integer a8 = Integer.valueOf(128);
// 这个肯定为false啦,因为a1和a2是两个不同的实例。两个实例用等号进行比较的话,会比较对象所引用的地址是否相同,这里肯定是不同的。
System.out.println(a1 == a2);
// 这里与上面类似,虽然a6会自动装箱为Integer类型,但这里还是两个不同的实例,所以不相等
System.out.println(a1 == a6);
// 因为a4是个基本类型,封装类型遇到基本类型会自动拆箱的,a3变为基本类型。两个基本类型进行比较直接比较内容是否相等就行啦,这里两个数值相等,所以为true。
System.out.println(a3 == a4);
// 这个与上面的类似,自动装箱类型遇到基本类型又会自动拆箱的。也为true。
System.out.println(a4 == a5);
// 这个就比较有意思了。两个都是自动装箱得到的Integer实例,因为Integer有个缓存机制,如果没有认真看,应该会认为是true,其实不然。
// 因为这里的128超出了Integer的默认缓存范围(-128~127),所以在缓存数组中找不到这个Integer实例,只能重新创新一个新的Integer实例,这就变成了类似第一个的比较,所以为false
System.out.println(a5 == a8);
// 这里两个数都是127,在缓存范围内。所以会直接从缓存数组中取已经提前实例化好的对象,所以取的是同一个对象(引用地址也相同哦),自然结果是true啦。
System.out.println(a6 == a7);
总结
- 两个实例进行比较,不仅要内容相同,还要所引用的对象地址也相同,否则是不相同的(所以比较两个封装类型的对象一般使用equals)。
- 封装类型遇到基本类型会自动拆箱,所以Integer实例与基本类型比较,只要内容相等,就是相等的。
- Integer内有个缓存机制,为了对象的重用。范围在-128~127的整型通过自动装箱得到的对象是相同的(返回的是同一个对象),超出范围则不会相同(会new一个新的对象)。范围最大值可通过虚拟机参数-XX:AutoBoxCacheMax来设置。