Integer的valueOf方法与缓存策略
问题的导火索
今天做了牛客网的笔试题,有一道选择题是这样的,给差不多是下面这样的一段代码,问输出是啥~~
public class Test {
public static void main(String[] args) {
Integer a = 3;
Integer b = 3;
System.out.println(a == b);
Integer c = 321;
Integer d = 321;
System.out.println(c == d);
}
}
就这个代码,我当时选的是两个true,我觉得3和321并没有区别,也就是说a和b引用的是同一个对象,c和d引用的也应该是同一个对象,这一题怕不是考心理的哦~~~
笔试结束之后我立刻试了一下,发现自己还是太年轻。第一个输出是true,第二个输出是false。当场懵逼,遂单步调试之~~~这里为了方便说明,我就以下面代码展示调试过程:
public class Test {
public static void main(String[] args) {
Integer a = 3;
}
}
调试过程:
在Integer a = 3;前添加断点,程序运行到此处单步调试,由于java的自动装箱机制,编译器会把它编译成Integer a = Integer.valueOf(3);于是会进入valueOf()方法如下:
@HotSpotIntrinsicCandidate
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[]中的一个值。而不在这个范围就会new一个新的Integer对象。
问题到此已经基本明朗了,我们可以猜到3在闭区间[IntegerCache.low, IntegerCache.high]范围内,所以之前代码里的a和b引用的是同一个对象,a==b返回的是true,而321不在那个范围内,即c和d调用valueOf()方法时,会重新new一个对象,在堆里开辟新的空间,c和d引用的自然不是同一个对象了,所以c==d返回的就是false了
IntegerCache.low 与 IntegerCache.high
我们已经能够解释那个问题了,那么到底在哪个范围内是直接引用已有对象,哪个范围内是new新的对象呢以及cache数组存的对象到底是啥?这就关系到IntegerCache.low和IntegerCache.high的值了~~
在return IntegerCache.cache[i + (-IntegerCache.low)];处转入cache的定义,可以进入IntegerCache类,从类的名字可以看出这个类实现了java的Integer类的缓存策略
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 =
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() {}
}
我们逐行解释一下这个类吧~~
首先前三行给出了low的定义以及high,cache的声明,终于可以明确cache[]是内置的常量数组。我们可以清楚地看到low是-128
static final int low = -128;
static final int high;
static final Integer cache[];
然后是static域给出high和cache[]的定义。
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
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;
}
String integerCacheHighPropValue = VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
这句话我不认得,看源码没大看懂,猜测是获取high的值后转为了字符串。这里high并没有初始化,所以integerCacheHighPropValue应该就是null的,所以if里面的东西就基本不用看了。看到后面high = h;
就知道了high是127。
if里面的东西并没有用到,但为什么要写这段代码呢?
看到注释// high value may be configured by property
可以知道high的值实际上是可以进行配置的,这个if段里面的语句也就是为这个服务的了~~~至于怎么配置,这里就不做具体解释了~~~ 主要是我不会呀
然后再来看cache:
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
cache是一个大小为high-low+1,也就是256的Integer对象数组,cache[0]=-128,cache[1]=-127,cache[2]=-126,以此类推,一直到cache[255]=127…
至此,最初的问题已经完全能够解释了,在java里,对于一个int常量i,当执行Integer a = i时,如果-128<=i<=127,那么a会直接引用IntegerCache类内部的静态对象,而i不在这个范围内时,会自动new一个新的对象出来,a会引用这个新的对象。