Java Integer缓存池:为什么-128到127用==比较是true
在Java开发中,使用 == 比较两个 Integer 对象时,经常会遇到一个令人困惑的现象:数值在 -128 到 127 之间时结果为 true,超出这个范围结果却变成了 false。这并非系统Bug,而是Java为了优化性能和内存使用而设计的“缓存池”机制在起作用。
1. 复现问题现象
首先,通过一段简单的代码 验证 这个问题的存在。
编写 并运行以下测试代码:
public class IntegerCacheTest {
public static void main(String[] args) {
Integer a = 127;
Integer b = 127;
System.out.println("127 == 127 : " + (a == b)); // 输出 true
Integer c = 128;
Integer d = 128;
System.out.println("128 == 128 : " + (c == d)); // 输出 false
Integer e = -129;
Integer f = -129;
System.out.println("-129 == -129 : " + (e == f)); // 输出 false
}
}
观察 输出结果,你会发现只有数值 127 的比较返回了 true。这是因为 a 和 b 指向了内存中同一个对象,而 c、d、e、f 都是内存中独立的新对象。
2. 追踪源码逻辑
Java 在自动装箱时会调用 Integer.valueOf(int i) 方法。查看 该方法的源码逻辑是理解问题的关键。
阅读 核心判断逻辑:
public static Integer valueOf(int i) {
// 判断数值 i 是否在缓存范围 [low, high] 内
if (i >= IntegerCache.low && i <= IntegerCache.high)
// 如果在范围内,从缓存数组 cache 中直接返回已有对象
return IntegerCache.cache[i + (-IntegerCache.low)];
// 如果不在范围内,创建一个新的 Integer 对象
return new Integer(i);
}
从上述代码可以清晰看出,当 i 的值满足 i >= IntegerCache.low 且 i <= IntegerCache.high 时,JVM 复用 缓存中的对象,否则 创建 新对象。
3. 理解缓存池机制
IntegerCache 是 Integer 类的一个静态内部类,它在类加载的初始化阶段 创建 并 缓存 了一系列 Integer 对象。
分析 缓存初始化流程:
- 设定 默认范围下界
low为-128。 - 设定 默认范围上界
high为127。 - 构建 一个大小为
(high - low) + 1的数组cache。 - 循环 遍历从
low到high的每一个整数值,将其封装为Integer对象并存入数组。
为了更直观地理解这一判断过程,可以参考以下逻辑流向:
正因为 127 落在这个缓存区间内,Integer a = 127 和 Integer b = 127 实际上获取的是数组中同一个下标的对象引用,所以 a == b 为 true。而 128 超出了范围,每次都会触发 new Integer(128),生成了两个不同的对象,引用地址不同,结果自然为 false。
4. 调整缓存上限(可选操作)
虽然缓存的下界 -128 是固定的,但上界 127 是可以调整的。如果你的应用场景中频繁使用较大的整数(如 0 到 1000),可以通过 JVM 参数 扩大 缓存范围以提升性能。
执行 以下启动命令来设置缓存上限:
java -XX:AutoBoxCacheMax=1000 YourApplication
设置该参数后,IntegerCache.high 将被更新为 1000。这意味着数值在 -128 到 1000 之间时,== 比较都将返回 true。
5. 对比其他包装类
不仅 Integer 拥有缓存机制,Java 的其他包装类也设计了类似的策略,但具体范围和配置灵活性各不相同。
参考 下表了解各包装类的缓存策略:
| 包装类 | 缓存范围 | 是否可配置 |
|---|---|---|
Byte |
-128 ~ 127 | 否(覆盖全部取值) |
Short |
-128 ~ 127 | 否 |
Integer |
-128 ~ 127 | 是(可调大 high) |
Long |
-128 ~ 127 | 否 |
Character |
0 ~ 127 | 否 |
Float |
无 | 否 |
Double |
无 | 否 |
Boolean |
true, false | 否(仅两个实例) |
6. 避免比较陷阱
由于缓存机制的存在,直接使用 == 比较 Integer 对象具有极大的不确定性。
遵循 最佳实践:
-
使用
equals()方法进行数值比较。Integer c = 128; Integer d = 128; System.out.println(c.equals(d)); // 始终返回 true -
避免 在业务逻辑中使用
==判断包装类的值相等性,除非你明确意图是比较对象引用(例如判断是否为同一缓存实例)。 -
拆箱 比较:如果必须使用
==,可以确保其中一方是基本类型int,这样会触发自动拆箱,比较的是数值而非引用。Integer c = 128; int d = 128; System.out.println(c == d); // 返回 true,因为 c 拆箱为 int 后比较数值

暂无评论,快来抢沙发吧!