Integer.valueOf() 在 -128 至 127 范围内返回缓存对象,超出则新建对象,导致 == 比较结果不稳定;应始终用 equals() 比较值,避免装箱陷阱。

Integer.valueOf() 为什么有时返回缓存对象,有时新建对象
因为 Integer.valueOf(int) 内部会查一个静态缓存数组 IntegerCache.cache,范围是 -128 到 127(含)。在这个区间内,每次调用都返回同一个 Integer 实例;超出就 new 一个新对象。
这直接导致 == 比较结果不稳定:两个值都是 100 时可能为 true,都是 200 时却为 false。
- 缓存区间不可配置(JDK 5–21 均为 -128~127,仅可通过 JVM 参数
-XX:AutoBoxCacheMax=xxx扩大上限,但不推荐) -
new Integer(100)总是新建对象,绕过缓存,和valueOf()行为不同 - 其他包装类也有类似机制:
Byte、Short、Character缓存固定范围;Long和Integer一样可调上限;Float和Double不缓存
什么时候会意外触发 == 比较失效
常见于用 == 判断两个 Integer 是否相等,尤其在集合操作或流式处理中容易忽略装箱细节。
比如从 Map<integer string></integer> 取 key 时用 == 判断是否命中,或者在 Stream.filter(x -> x == 128) 中漏掉匹配——因为 128 被装箱成新对象,跟字面量 128 的包装结果不是同一引用。
立即学习“Java免费学习笔记(深入)”;
- 永远优先用
.equals()比较包装类值,除非你明确知道两者都在缓存区间且共享同一实例 - 编译期常量(如
Integer a = 100;)会被自动优化为valueOf(100),但运行时计算出的值(如int x = 100; Integer b = x;)也走valueOf(),行为一致 - 反模式示例:
Integer a = 128; Integer b = 128; System.out.println(a == b); // false
IntegerCache 的初始化时机与内存开销
IntegerCache 是个静态内部类,在 Integer.valueOf() 第一次被调用时才初始化,属于懒加载。缓存数组长度默认 256(-128 到 127),每个元素是一个 Integer 对象,总内存占用极小(约几 KB)。
但要注意:它在类加载后长期驻留堆中,不会被 GC 回收,哪怕你只用了其中几个值。
- 缓存对象在 JVM 启动后即创建并保持强引用,生命周期等同于
Integer类本身 - 扩大缓存上限(如
-XX:AutoBoxCacheMax=500)会提前创建更多对象,增加初始堆压力,且对绝大多数业务无实际收益 - 没有“清空缓存”机制,也不该试图反射破坏它——破坏后
valueOf()行为未定义
替代方案:什么时候该避免自动装箱
高频数值计算、循环内频繁装箱/拆箱(如 for (Integer i : list) sum += i;),会因缓存缺失或对象分配带来额外开销。
这不是缓存机制的问题,而是装箱本身带来的 GC 和间接寻址成本。
- 性能敏感路径优先用基本类型:
int而非Integer,long而非Long - 集合操作中,考虑
IntStream替代Stream<integer></integer>,避免每项都装箱 - 自定义容器(如 FastUtil、Trove)提供原生
int版本 Map/List,彻底绕过装箱 - 注意
OptionalInt等原始类型特化类,比Optional<integer></integer>更轻量
缓存只是装箱机制的一层优化,真正要防的是把包装类当基本类型用,又指望它没开销。










