从源码解析Java中的“128陷阱”
现象:为什么Integer(128) == Integer(128)
返回false?
在Java中,以下代码会输出不同的结果:
Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true
Integer c = 128;
Integer d = 128;
System.out.println(c == d); // false
这是因为Java对**-128到127之间的Integer对象**进行了缓存,导致范围内外的对象比较行为不同。这一现象被称为“128陷阱”。
源码解析:IntegerCache的实现
Java通过静态内部类IntegerCache
实现缓存机制,核心源码如下(修正了用户提供的代码片段中的笔误):
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// 默认 high 值为127
int h = 127;
// 尝试通过系统属性配置上限
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = Integer.parseInt(integerCacheHighPropValue);
i = Math.max(i, 127); // 下限不能低于127
// 上限不能超过Integer.MAX_VALUE - (-low) -1(防止溢出)
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch (NumberFormatException nfe) {
// 配置无效时忽略
}
}
high = h;
// 初始化缓存数组
cache = new Integer[(high - low) + 1];
int j = low; // 修正变量名错误(原代码中误写为i++)
for (int k = 0; k < cache.length; k++) {
cache[k] = new Integer(j++);
}
// 断言:缓存上限至少为127
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
关键逻辑分析
-
缓存范围
- 默认范围是
-128
到127
,但可通过JVM参数-Djava.lang.Integer.IntegerCache.high=xxx
扩展上限。 - 上限最大值受
Integer.MAX_VALUE - (-low) -1
限制,防止数组长度溢出。
- 默认范围是
-
缓存初始化
- 在类加载时,静态块会创建并填充缓存数组。
- 若配置了自定义上限,会优先使用,但不会低于127。
-
自动装箱与缓存
- 当使用
Integer.valueOf(int)
或自动装箱(如Integer a = 127;
)时,会直接从缓存中获取对象。 - 源码片段:
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
- 当使用
为什么会产生“128陷阱”?
- 缓存范围内:
valueOf
返回的是同一个缓存对象,==
比较的是对象内存地址,结果为true
。 - 缓存范围外:每次
valueOf
会创建新对象,==
比较的是不同对象的内存地址,结果为false
。
如何避免?
- 使用
equals()
代替==
equals()
比较的是整数值,而非对象地址。 - 使用基本类型
int
直接使用int
可绕过对象比较问题。 - 谨慎配置缓存上限
修改上限需权衡内存开销,不建议盲目扩大。
总结
“128陷阱”本质是Java对常用小整数的优化策略。理解IntegerCache
的实现后,开发者应始终使用equals()
比较包装类型对象,或在需要高性能的场景下优先使用基本类型。源码中通过灵活的配置和断言机制,既保证了默认性能,又提供了扩展性,体现了Java设计的巧妙之处。