
Integer.valueOf() 为什么有时 == 有时 !=
因为 Integer 对 -128 到 127 的整数做了缓存,调用 valueOf() 时直接返回池中对象;超出范围就 new 新对象。所以两个 Integer 用 == 比较,只在缓存范围内才可能为 true。
- 缓存启用条件:必须通过
valueOf()或自动装箱(如Integer i = 100;),new Integer(100)永远不进池 - 缓存范围可调:JVM 启动参数
-XX:AutoBoxCacheMax=200能扩大上限(仅对Integer有效,Long等不支持) - 比较务必用
.equals():哪怕你确定值在 -128~127,一旦未来参数调整或换 JDK 版本,==就会悄无声息出错
Long、Short、Byte、Character 的缓存行为差异
Byte、Short、Character 的缓存范围是固定的,Long 和 Integer 表面类似但实现不同——只有 Integer 支持 JVM 参数调节缓存上限。
-
Byte、Short、Character:固定缓存全部合法值(Byte是 -128~127,Character是 \u0000~\u007f) -
Long:也缓存 -128~127,但-XX:AutoBoxCacheMax对它无效,硬编码不可改 -
Boolean:只缓存TRUE和FALSE两个实例,没范围概念 -
Float、Double:不缓存,每次valueOf()都 new 新对象
缓存池在哪?怎么验证它存在
缓存池是每个包装类内部的 static final 数组或对象池,比如 Integer 的缓存存在 Integer.IntegerCache.cache 里,是包私有的,不能直接访问,但能间接验证。
- 验证方法:写两行
Integer a = 127; Integer b = 127;,再a == b返回true;换成128就变false - 反编译字节码可见:自动装箱语句
Integer i = 100;实际被编译成Integer.valueOf(100) - 注意 JDK 版本:JDK 5 引入缓存,JDK 9+ 未改动逻辑,但某些 GraalVM 或定制版 JDK 可能有差异
为什么 String 也有类似现象但原理完全不同
String 字面量复用(如 "abc" == "abc")靠的是字符串常量池(runtime constant pool),而包装类缓存是堆内存里的静态对象数组,两者内存区域、管理机制、触发条件都不同,别混为一谈。
立即学习“Java免费学习笔记(深入)”;
- 字符串常量池可被
String.intern()手动介入;包装类缓存完全透明,无法手动增删 - 字符串池大小可调(
-XX:StringTableSize),包装类缓存大小只跟范围有关,不占额外调优入口 - 最易错点:看到
Integer i = 100; String s = "100";都“复用”,就以为它们是一回事——其实一个是堆上共享对象,一个是方法区里符号引用匹配
缓存池不是语法糖,是明确写死在 JDK 源码里的优化逻辑;依赖它做对象相等判断,等于把业务逻辑绑在 JVM 实现细节上。真要安全,老老实实用 .equals(),哪怕多一次空指针检查。










