“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你马上就知道答案。
Java 中的 Integer 是包装类,底层实则就是一个对象:
Integer x = 127;编译器看到这行代码,实则是:
Integer x = Integer.valueOf(127);public static Integer valueOf(int i) {
if (i >= -128 && i <= 127) {
return IntegerCache.cache[i + 128];
} else {
return new Integer(i);
}
}Integer x = 1000;
Integer y = 1000;
System.out.println(x == y); // false你以为是一样,实则是两个不同对象。
这不是拍脑袋定的,而是 **“JVM 规范 + 二进制效率 + 内存权衡”**共同决定的:
大量统计发现,开发中 大部分整型常量都聚焦在 -128 到 127:
每次 new 一个 Integer 对象都是对堆的消耗,GC 也更频繁。
通过缓存对象(享元模式),可以极大优化性能。
JVM 启动时允许你通过参数设置缓存上限:
-XX:AutoBoxCacheMax=<size>例如:
java -Djava.lang.Integer.IntegerCache.high=512 MyApp可以让 Integer.valueOf(300) 返回缓存对象。
❗ 提议只在性能敏感 + 你能全程掌控代码的场景下调整。
Integer x = 1000;
Integer y = 1000;
System.out.println(x == y); // false推荐写法:
x.equals(y); // trueMap<Integer, Integer> countMap = new HashMap<>();
for (int i = 0; i < 1000000; i++) {
countMap.put(i, i); // 频繁装箱 new Integer
}应使用基本类型或其他计数结构,如 Int2IntMap(FastUtil)或 AtomicInteger。
Integer x = null;
int y = x; // NullPointerException!项目 | 内容 |
缓存范围 | -128 到 127(默认) |
缓存位置 | IntegerCache 类静态数组 |
触发条件 | 调用 Integer.valueOf(int) 时生效 |
可否修改 | 启动参数 |
面试陷阱 | == 比较地址,equals() 比较值 |
常见踩坑 | null 拆箱、超出范围 == 比较失败 |
Java 为了减少 Integer 对象的频繁创建,引入了缓存机制。默认缓存范围是 -128 到 127,对应 byte 类型范围,同时这些值在开发中出现频率极高。调用 Integer.valueOf() 方法时,如果值在缓存范围内,会返回缓存对象,否则创建新对象。因此 Integer a = 1000; b = 1000; a == b 会是 false,由于引用不同。推荐使用 equals() 进行数值比较,避免缓存范围误判导致程序 Bug。