在《阿里巴巴Java开发手册》中有一段关于包装类之间值比较问题的规范:

【强制】所有整型包装类对象之间值的比较,全部使用 equals 方法比较。
说明:对于 Integer var = ? 在 - 128 至 127 范围内的赋值,Integer 对象是在 IntegerCache.cache 产 生,会复用已有对象,这个区间内的 Integer 值可以直接使用 == 进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用 equals 方法进行判断。

 我们来看一段代码

1
2
3
4
5
public static void main(String[] args) {
Integer a = 127, b = 127, c = 128, d = 128,
System.out.println(a == b);
System.out.println(c == d);
}

true
false

 为什么会这样呢?在 Integer var = ? 这个过程发生了什么才会造成这样的结果呢?

 通过调试,发现通过 Integer var = ? 形式声明变量会通过 java.lang.Integer#valueOf(int) 来构造 Integer 对象。

1
2
3
4
5
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}

 通过以上代码我们可以看出,使用 Integer.valueOf(int) 来创建整型对象时,如果参数小于整型缓存的最大值(
IntegerCache.high且大于整型缓存的最小值(
IntegerCache.low),会直接从缓存数组中获取对象。否则会直接 new 一 整型对象。

 那么为什么要这样做呢?我们来看看该源码中对该方法的介绍

Returns an Integer instance representing the specified int value. If a new Integer instance is not required, this method should generally be used in preference to the constructor Integer(int), as this method is likely to yield significantly better space and time performance by caching frequently requested values. This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range.

 大致意思是:当不需要一个 Integer 对象时,这个方法会先于构造方法执行 。这种方法缓存最常用的值(-128~127),其空间和时间性能会更好。

 下面我们再来思考一个问题:Integer 的缓存区间可以修改吗?想要解决这个问题,我们需要看一下源码中 IntegerCache 是如何对 low 和 high 进行处理的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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;

// ......
}

private IntegerCache() {}
}

 从源码中我们发现,low 的值已经被写死了,其值为 -128,但是 high 的值是可以修改的,如果没有指定 high 的值,那么默认为 127。
 我们可以通过虚拟机参数 -XX:AutoBoxCacheMax=<size>-Djava.lang.Integer.IntegerCache.high=<value> 来设置。
 之所以缓存这个范围的数据,是为了自动装箱时可以符用这些对象,这也是 JLS2 的要求。

 我们也可以参考 JLS 的 Boxing Conversion 部分相关内容

If the valuepbeing boxed is an integer literal of type intbetween -128and 127inclusive (§3.10.1), or the boolean literal trueorfalse(§3.10.3), or a character literal between '\u0000’and '\u007f’inclusive (§3.10.4), then let aand bbe the results of any two boxing conversions of p. It is always the case that a==b.

在 -128 到 127 (含)之间的 int 类型的值,或者 boolean 类型的 true 或 false, 以及范围在’\u0000’和’\u007f’ (含)之间的 char 类型的数值 p, 自动包装成 a 和 b 两个对象时, 可以使用 a == b 判断 a 和 b 的值是否相等。