Integer.valueOf()是唯一触发缓存的入口,自动装箱均转为此调用;new Integer()绕过缓存;缓存范围默认[-128,127]可调;==比较的是引用而非值,安全比较应使用equals()或基本类型转换。

Integer.valueOf() 是缓存行为的唯一入口
所有自动装箱(如 Integer i = 127)最终都编译为 Integer.valueOf(i) 调用,而只有这个方法会查 IntegerCache;new Integer(i) 绕过缓存,每次新建对象,和缓存机制无关。
这意味着:想确认某个值是否命中缓存,只看它是否经由 valueOf() 创建——包括直接赋值、集合泛型推导、Stream boxed 操作等场景。
-
Integer a = 127→ 走缓存 →a == Integer.valueOf(127)为 true -
Integer b = new Integer(127)→ 不走缓存 →b == Integer.valueOf(127)为 false -
List<integer> list = Arrays.asList(128, 128)</integer>→ 两个valueOf(128)→ 返回不同对象 →list.get(0) == list.get(1)为 false
缓存范围不是固定 128,而是 [-128, high] 可调
IntegerCache.low 固定为 -128,不可修改;但 IntegerCache.high 默认是 127,可通过 JVM 参数扩大:
-Djava.lang.Integer.IntegerCache.high=200
立即学习“Java免费学习笔记(深入)”;
启动时加上该参数后,Integer.valueOf(180) 也会复用缓存对象,== 比较可能返回 true。但注意:
- 该参数只影响
valueOf(),不影响new Integer() - high 最小不能低于 127(JVM 强制校验)
- 增大缓存会占用更多类加载时的堆内存,对低内存环境需谨慎
- 其他包装类类似:
Byte/Short/Long缓存范围也是 [-128, 127],Character是 [0, 127],Boolean只有TRUE/FALSE两个实例
== 比较 Integer 就是地址比较,不是数值比较
这是陷阱最常被误读的一点:开发者写 if (a == b) 本意是“值是否相等”,但 JVM 执行的是“是否指向同一块内存”。只要缓存没生效(比如 128)、或用了 new、或来自不同 classloader 加载的 Integer 类,== 就不可靠。
更危险的是混合比较:
-
Integer a = 128; int b = 128; System.out.println(a == b);→ true(a自动拆箱为int,再数值比较) -
Integer a = 128; Long b = 128L; System.out.println(a == b);→ 编译失败(类型不兼容,无法隐式拆箱成同类型) -
Integer a = 128; Long b = 128L; System.out.println(a.equals(b));→ false(Integer.equals()只接受Integer参数,Long直接返回 false)
所以,跨包装类比较必须显式转基本类型,例如 a.longValue() == b.longValue()。
真实业务中唯一安全的比较方式是 equals() + 类型一致前提
不要依赖缓存范围写条件逻辑,比如“因为值小就用 ==”——这会让代码在参数调整或升级 JDK 后突然出错。正确做法是:
- 数值比较一律用
.equals(),且确保两边是同一包装类(Integer与Integer) - 涉及基本类型混合时,优先转成基本类型再比:
if (objA != null && objB != null && objA.intValue() == objB.intValue()) - 集合去重、Map key 等场景,
Integer本身重写了equals()和hashCode(),只要值相同就表现一致,不受缓存影响 - 日志或调试时别只看
==结果来判断逻辑分支,要打数值(i.toString())或用断点观察实际值
缓存机制是 JVM 的内部优化细节,不是语言契约。你写的代码不该假设 127 和 128 在对象层面有任何行为差异——它们只是数字,仅此而已。


















