-
什么是装箱和拆箱
装箱拆箱是JDK5中引入的新特性。装箱,顾名思义,就是把基本类型转化为包装类型;拆箱,反过来,就是把包装类型转化为基本类型。
例子
Integer i = 2; // 自动装箱
int n = i; // 自动拆箱
用法非常简单,但是不知道你们有没有同笔者一样的疑问?Integer对象是什么被创建的?是通过Integer的构造函数还是其他方式创建Integer对象的?反过来也一样,包装类型怎么就自动转化为基本类型了
自动装箱和拆箱可以说是Java中的一种语法糖,最后还是会编译为.class文件,那我们尝试用反编译工具javap看看是什么东西。
public class IntegerBoxingTest {
public IntegerBoxingTest() {
}
public static void main(String[] args) {
Integer i = 2;
int n = i;
}
}
public class com.java.lang.IntegerBoxingTest {
public com.java.lang.IntegerBoxingTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
public static void main(java.lang.String[]);
Code:
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
}
看不懂上面的字节码文件没有什么关系,看看有没有我们想要的东西。发现了
1: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
Invokestatic 就是调用静态方法的意思,这里会调用Integer.valueOf()方法,也就是说
Integer i = 2; 其实就是等价于 Integer i = Integer.valueOf(2);
这也就是自动装箱背后的原理。
反过来,自动拆箱也是一样
6: invokevirtual #3 // Method java/lang/Integer.intValue:()I
自动拆箱的时候,会调用自身的intValue()方法
也就是说,int n = i; 等价于 int n = i.intValue();
到这里为止,我们已经清楚了自动装箱和拆箱的原理。
接下来,我们看看Integer.valueOf(int i)这个方法是怎么实现的?
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
上面的方法中,首先会判断传进来的值是不是在Integer的缓存池中,如果是的话,直接返回,如果不是的话,会去new Integer()。
ok,那接下来看看IntegerCache这个类是干嘛用的?
IntegerCache
private static class IntegerCache {
// 最小值
static final int low = -128;
// 最大值
static final int high;
// 缓存数组
static final Integer cache[];
static {
// high value may be configured by property
// 最大值默认为127
int h = 127;
// 可以通过设置jdk的AutoBoxCacheMax参数调整,自动缓存区间设置为[-128,N]。注意区间的下界是固定
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;
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() {}
}
从上面的代码中,我们可以知道,在默认情况下,IntegerCache的static静态块创建了-128-127之间的Integer对象,并且将它们存储在cache[]缓存数组中。当然,可以通过设置jdk的AutoBoxCacheMax参数调整,自动缓存区间设置为[-128,N]。注意区间的下界是固定。
知道IntegerCache的原理之后,也就是在自动装箱的时候,如果是-128-127的数字,并不会去创建一个新的Integer对象,而是直接从缓存中直接出来。当然,不在这个范围内的对象就会去创建一个新的对象。
下面,我们来看一看一道非常经典的题目。
@Test
public void testIntegerCache() {
Integer a = 5; //在-128-127范围内,直接从缓存中取出
Integer b = 5; //在-128-127范围内,直接从从缓存中取出
System.out.println(a == b); //true,因为都是缓存中的同一个对象
Integer c = 130; //不在-128-127范围内,新建一个Integer
Integer d = 130; //不在-128-127范围内,新建一个Integer
System.out.println(c == d); //false,因为都是新建的,不是同一个对象
Integer f = new Integer(5); //新建一个Integer
System.out.println(a == f); //false,因为一个是从缓存中拿的,一个是新创建的,并不是同一个对象
}
如果大家都知道了自动装箱时有IntegerCache的存在,加上上面的解释,想必以后在使用自动装箱时,会有更加深刻的理解。
以上,是笔者个人理解,若有错误之处,请指正。