Integer 缓存机制
背景
Integer 最常见的面试题,就是问Integer的值如何比较相等。比如:
Integer i1 = 33;
Integer i2 = 33;
System.out.println(i1 == i2);// 输出 true
Integer i1 = 33;
Integer i2 = new Integer(33);
System.out.println(i11 == i22);// 输出 false
Integer i1 = new Integer(33);
Integer i2 = new Integer(33);
System.out.println(i3 == i4);// 输出 false
Integer i1 = 129;
Integer i2 = 129;
System.out.println(i1 == i2);// 输出 false
源码解析
前面的几个可能好理解,主要是最后一个都是129,为什么还是输出false,这个就是Integer 的缓存机制,我们看下Integer 的源码。
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
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的缓存值是从-128 - 127之间的值,我们再看一下,刚刚那个例子的编译以后的class文件的字节码文件内容:
// class version 52.0 (52)
// access flags 0x21
public class com/example/demoweb/controller/tt {
// compiled from: tt.java
// access flags 0x1
public <init>()V
L0
LINENUMBER 7 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
L1
LOCALVARIABLE this Lcom/example/demoweb/controller/tt; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x9
public static main([Ljava/lang/String;)V
// parameter args
L0
LINENUMBER 9 L0
SIPUSH 129
INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
ASTORE 1
L1
LINENUMBER 10 L1
NEW java/lang/Integer
DUP
SIPUSH 129
INVOKESPECIAL java/lang/Integer.<init> (I)V
ASTORE 2
L2
LINENUMBER 11 L2
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ALOAD 2
ALOAD 1
IF_ACMPNE L3
ICONST_1
GOTO L4
L3
FRAME FULL [[Ljava/lang/String; java/lang/Integer java/lang/Integer] [java/io/PrintStream]
ICONST_0
L4
FRAME FULL [[Ljava/lang/String; java/lang/Integer java/lang/Integer] [java/io/PrintStream I]
INVOKEVIRTUAL java/io/PrintStream.println (Z)V
L5
LINENUMBER 14 L5
RETURN
L6
LOCALVARIABLE args [Ljava/lang/String; L0 L6 0
LOCALVARIABLE i1 Ljava/lang/Integer; L1 L6 1
LOCALVARIABLE i2 Ljava/lang/Integer; L2 L6 2
MAXSTACK = 3
MAXLOCALS = 3
}
我们可以看到Integer i1 = 129;其实字节码实际执行的是valueOf方法
L0
LINENUMBER 9 L0
SIPUSH 129
INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
ASTORE 1
L1
再看看valueof方法的源码
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
先从缓存拿,缓存拿不到则会重新new一个对象,这里的129不在缓存里面,所以i1和i2是两个不同的对象,所以相比较为false 。
反思:那Integer 类型的值如何比较呢?
我们再看看equals的方法:
public boolean equals(Object obj) {
//先判断对象是不是int类型,是则比较的是int值,而不是比较两个对象
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
故Integer类型的比较需要使用equals
Long缓存机制
有了Integer缓存的基础,其实Long包装类型也是一样的操作,我们直接看源码:
public static Long valueOf(long l) {
final int offset = 128;
if (l >= -128 && l <= 127) { // will cache
return LongCache.cache[(int)l + offset];
}
return new Long(l);
}
private static class LongCache {
private LongCache(){}
static final Long cache[] = new Long[-(-128) + 127 + 1];
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Long(i - 128);
}
}
从源码开Long类型的缓存范围也是-128-127之间取值
其他基础类型
Byte,Short,Integer,Long 这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character 创建了数值在 [0,127] 范围的缓存数据,Boolean 直接返回 True or False。