Integer.valueOf 能复用对象是因为其内部使用了 -128 至 127 的缓存池,该池由静态的 IntegerCache 类维护;在此范围内的值调用 valueOf 时直接返回缓存对象,超出则新建。

Integer.valueOf 为什么能复用对象而不是每次都 new
因为 Integer.valueOf 内部用了缓存池,默认缓存了 -128 到 127 范围内的 Integer 实例。只要传入的 int 值落在这区间,就直接返回已有对象,不 new 新对象。
这个缓存由静态代码块初始化,底层是 IntegerCache 类维护的一个 Integer[] 数组,索引偏移后对应值。超出范围(比如 Integer.valueOf(200))则每次都会 new 出新对象。
- 缓存范围可通过 JVM 参数
-Djava.lang.Integer.IntegerCache.high=xxx扩大,但仅对启动时生效,且只影响上限(下限固定为 -128) - 注意:缓存行为只在调用
valueOf(int)时触发;new Integer(int)永远新建对象,已废弃 - 自动装箱(如
Integer i = 100;)底层也调用valueOf,所以同样享受缓存
怎么验证两个 Integer 是否指向同一对象
用 == 比较引用是否相等,而不是 .equals()(后者比较值)。只有缓存命中时,== 才返回 true。
Integer a = Integer.valueOf(100); Integer b = Integer.valueOf(100); System.out.println(a == b); // true —— 同一个缓存对象 Integer c = Integer.valueOf(200); Integer d = Integer.valueOf(200); System.out.println(c == d); // false —— 不同对象,各自 new
- 别用
==比较任意两个Integer,除非你明确知道它们都在缓存范围内 - 测试时避免用字面量赋值(如
Integer x = 100;),它和valueOf行为一致,但容易混淆来源 - 在单元测试里可配合
System.identityHashCode()辅助确认是否为同一实例
缓存池在哪些场景下真正省对象、哪些时候没用
缓存只对重复出现的小整数有效,且前提是统一走 valueOf 或自动装箱。一旦混用 new、解析字符串或跨 JVM,缓存就失效。
- ✅ 适合:循环中反复构造相同小整数(如状态码、枚举序号)、JSON 反序列化后归一化 ID 字段
- ❌ 无效:从
String解析(Integer.parseInt("123")返回int,不是Integer;要再套一层valueOf才进缓存) - ⚠️ 注意:多线程安全——缓存数组是静态 final 的,初始化后不可变,线程间共享无问题
- ⚠️ 注意:缓存对象是不可变的,所以复用不会引发状态污染
为什么不用 Integer.valueOf(-200) 就一定 new,但又不能随便扩 high 值
缓存本质是空间换时间:每多缓存一个值,就多占一个 Integer 对象内存(约 16 字节对象头 + 4 字节字段)。默认 -128~127 是经验值,覆盖了绝大多数计数、索引、HTTP 状态码等高频小整数。
- 扩到 1000 会让缓存数组长度变成 1129,多占约 18KB 内存,但实际业务中 >127 的整数重复率通常很低
- 若真有大量重复的大整数(如用户 ID),应考虑自建
ConcurrentHashMap缓存,而非依赖 JVM 默认池 -
IntegerCache是 package-private 类,无法外部修改或清空,也不支持运行时扩容
缓存池不是万能优化开关,它只在「小、重复、统一构造方式」这三个条件同时满足时才起效。漏掉任一环,对象照样 new。










