概述
了解Java语言的应该都知道Integer这个类,知道它是基本数据类型int的包装器类,也知道它的用法等,但仅仅停留在[会用]这个层面上的程序员不是好司机。通过本文,我们将知道:
- Integer核心源码
- 自动装箱和自动拆箱实现原理
- Integer缓存机制
自动装箱
请看下面代码:
public class IntegerTest {
public static void main(String[] args) {
Integer i1 = 1; //why
System.out.println(i1);
}
}
我们对上面的代码再熟悉不过,熟悉到我们可以不假思索地说出输出结果是1,熟悉到我们忘了问自己:
为什么可以把一个int类型的数赋值给Integer类型? 下面我们就一步步揭开这条语句的神秘面纱。<br />
通过工具javap来反编译IntegerTest.class(javap -c IntegerTest.class),部分结果截图如下:
从以上截图可以看到,编译器在编译Integer i1 = 1时调用了Integer.valueOf(int i)方法,我们来看看valueOf方法源码:
#Integer.java
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
这是一个if-else结构语句,else语句体很好理解,通过调用Integer构造器创建一个新对象。但if语句体里面是什么鬼东西呢?其实是这样的,在Java中new对象会调用构造器创建一个新的对象,给它分配内存、管理它的生命周期等等等。为了解决每次new对象带来的存储分配、生命周期管理等问题,Java引入了 [对象缓存] ,通过将那些比较常用的对象缓存起来,这样就不必每次都重新创建一个新的对象,而只需将已缓存对象返回即可。这种缓存策略在Boolean、Byte、Char、Integer、Long中都有体现,今天我们看看缓存在Integer中是如何实现的:
#Integer.java
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
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;
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静态内部类 [IntegerCache] 默认将-128~127之间的整数对应的对象缓存在cache数组中,其中最大整数high可以通过JVM启动参数+XX:AutoBoxCacheMax=size修改,这样我们就可以根据应用实际情况灵活地调整来提高性能。
再回到valueOf(int i)方法,如果参数i的值在[IntegerCache.low, IntegerCache.high]之间,则返回IntegerCache.cache数组中对应的对象,否则,通过Integer构造器创建新的对象后返回。<br />以上就是Integer自动装箱的实现原理:编译器编译Integer i1 = 1语句时,通过调用Integer.valueOf(int i)方法实现自动装箱。
自动拆箱
再看以下代码:
public class IntegerTest {
public static void main(String[] args) {
Integer i1 = 1;
int i2 = i1; //why
System.out.println(i2);
}
}
这又是什么鬼?为什么Integer对象可以赋值给基本数据类型int? 同样,我们通过javap工具反编译IntegerTest.class,截图如下:
从以上反编译结果可以看到,编译器在编译int i2 = i1语句时,调用了Integer.intValue()方法
#Integer.java
public int intValue() {
return value;
}
intValue()方法返回了对象的value值。
#Integer.java
private final int value;
public Integer(int value) {
this.value = value;
}
以上就是Integer自动拆箱的实现原理:编译器编译int i2 = i1时,通过调用Integer.intValue()方法实现自动拆箱。
缓存机制
再看以下代码:
public class IntegerTest {
public static void main(String[] args) {
Integer i1 = 1;
Integer i2 = 1;
Integer i3 = 128;
Integer i4 = 128;
System.out.println(i1 == i2); //true
System.out.println(i3 == i4); //false
}
}
从自动装箱我们知道Integer将常用整数[-128~127]缓存在IntegerCache.cache对象数组中,所以引用i1和i2指向同一个对象,即i1 == i2;i3和i4的值128没有缓存在cache数组中,所以每次调用Integer.valueOf(int i)方法时,都会重新创建一个对象,因此引用i3和i4指向不同的对象,即i3 != i4。
欢迎留言交流