Integer a = 1000; Integer b = 1000; a == b 是 true 还是 false?”
不少 Java 面试者在这道题前频频翻车——背后的核心就是 Integer 缓存机制。
本篇文章带你从源码、字节码、JVM 优化与面试实战多个维度,彻底搞懂这个“== 陷阱”。
一、先抛出灵魂三问
Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true
Integer c = 128;
Integer d = 128;
System.out.println(c == d); // false
为什么前面是 true,后面是 false?
- 是因为 缓存机制吗?
- 缓存的范围为什么是 -128 到 127?
- 可以改吗?实战中有坑吗?
你马上就知道答案。
二、揭秘:Integer 的缓存机制
Java 中的 Integer 是包装类,底层其实就是一个对象:
Integer x = 127;
编译器看到这行代码,其实是:
Integer x = Integer.valueOf(127);
来看源码(JDK 8):
public static Integer valueOf(int i) {
if (i >= -128 && i <= 127) {
return IntegerCache.cache[i + 128];
} else {
return new Integer(i);
}
}
重点解释:
- IntegerCache.cache[] 是一个缓存数组,提前创建好了 -128 到 127 范围的 Integer 对象。
- 这个范围内的数调用 valueOf() 会返回已有对象引用,而不是新建。
- 超出这个范围就会new 一个新对象,所以:
Integer x = 1000;
Integer y = 1000;
System.out.println(x == y); // false
你以为是一样,其实是两个不同对象。
三、为啥是 -128 到 127?背后有讲究!
这不是拍脑袋定的,而是 **“JVM 规范 + 二进制效率 + 内存权衡”**共同决定的:
✅ 原因 1:与 byte 类型范围一致
- byte 类型的范围就是 -128 到 127。
- Java 是强类型语言,很多常量表达式都用 byte 表示,提升执行效率。
✅ 原因 2:Integer 最常用的数值集中在这个范围
大量统计发现,开发中 大部分整型常量都集中在 -128 到 127:
- 下标、状态码、布尔映射值、分页编号、枚举 ID……
- 缓存这部分能 极大减少对象创建次数。
✅ 原因 3:减少堆内存压力 + 减少 GC
每次 new 一个 Integer 对象都是对堆的消耗,GC 也更频繁。
通过缓存对象(享元模式),可以极大优化性能。
四、这个范围能改吗?能!但你敢改吗?
JVM 启动时允许你通过参数设置缓存上限:
-XX:AutoBoxCacheMax=<size>
例如:
java -Djava.lang.Integer.IntegerCache.high=512 MyApp
可以让 Integer.valueOf(300) 返回缓存对象。
⚠️ 注意事项:
- 这个改动只影响 通过 valueOf() 方法装箱的场景;
- 手动 new Integer() 无效,永远是新对象;
- 对于第三方库、多人协作项目,改缓存值容易埋雷。
❗ 建议只在性能敏感 + 你能全程掌控代码的场景下调整。
五、实战误区:你踩过这些坑吗?
❌ 1. 使用 == 判断 Integer 数值相等
Integer x = 1000;
Integer y = 1000;
System.out.println(x == y); // false
推荐写法:
x.equals(y); // true
❌ 2. 在集合中频繁装箱导致 GC
Map<Integer, Integer> countMap = new HashMap<>();
for (int i = 0; i < 1000000; i++) {
countMap.put(i, i); // 频繁装箱 new Integer
}
应使用基本类型或其他计数结构,如 Int2IntMap(FastUtil)或 AtomicInteger。
❌ 3. 忘记 null 拆箱直接崩
Integer x = null;
int y = x; // NullPointerException!
六、总结:关于 Integer 缓存的全套记忆法
| 项目 | 内容 |
|---|---|
| 缓存范围 | -128 到 127(默认) |
| 缓存位置 | IntegerCache 类静态数组 |
| 触发条件 | 调用 Integer.valueOf(int) 时生效 |
| 可否修改 | 启动参数 -Djava.lang.Integer.IntegerCache.high |
| 面试陷阱 | == 比较地址,equals() 比较值 |
| 常见踩坑 | null 拆箱、超出范围 == 比较失败 |
七、面试回答范式(收藏备用)
Java 为了减少 Integer 对象的频繁创建,引入了缓存机制。默认缓存范围是 -128 到 127,对应 byte 类型范围,同时这些值在开发中出现频率极高。调用 Integer.valueOf() 方法时,如果值在缓存范围内,会返回缓存对象,否则创建新对象。因此 Integer a = 1000; b = 1000; a == b 会是 false,因为引用不同。推荐使用 equals() 进行数值比较,避免缓存范围误判导致程序 Bug。