java中int转integer是编译器插入integer.valueof()调用,其通过integercache缓存-128~127的实例,超出范围则新建对象;==比较引用地址,故127相等而128不等,应统一用.equals()或objects.equals()。

Java里int转Integer到底发生了什么
这不是简单的“自动装箱”,而是编译器在字节码层面插入了Integer.valueOf()调用。JVM不会直接把int塞进对象堆里,它先查缓存——只有在这个缓存命中时,才复用已有对象。
常见错误现象:Integer a = 127; Integer b = 127; System.out.println(a == b); // true,但换成128就输出false。这不是bug,是缓存策略生效了。
-
Integer.valueOf(int)内部用IntegerCache缓存-128到127(含)的实例 - 这个范围是JVM实现规定的最小保证,部分JDK可通过
-XX:AutoBoxCacheMax=xxx扩大上限(如OpenJDK),但不改变默认行为 - 超出缓存范围(比如
new Integer(128)或Integer.valueOf(128))每次都会新建对象,==比较必然为false
为什么==在包装类间不可靠
因为==比的是引用地址,不是数值。缓存让小整数“看起来相等”,但逻辑上它们仍是两个独立对象——只是恰好被设计成共享同一份内存。
使用场景:你在写工具方法、缓存键、或做单元测试断言时,如果依赖==判断数值相等,就会在边界值(比如127和128)上翻车。
立即学习“Java免费学习笔记(深入)”;
- 永远用
.equals()比较包装类数值,它会先判空再比intValue() - 注意
null风险:Integer x = null; x.equals(127)会抛NullPointerException,而Objects.equals(x, 127)更安全 -
switch语句中允许用Integer,但底层仍会自动拆箱为int,所以不触发缓存问题,也不建议在switch里混用null值
IntegerCache的初始化时机与线程安全
缓存数组在Integer类首次被主动使用(比如调用valueOf或访问TYPE)时静态初始化,且只初始化一次。整个过程由JVM保证线程安全,你不需要加锁,也不该去干预。
性能影响:缓存减少了对象分配和GC压力,对高频小整数(如循环索引、状态码)很友好;但如果你的应用大量使用Integer.valueOf(1000)这类非缓存值,其实和new Integer(1000)开销差不多——都是新对象。
- 缓存数组大小固定,默认
256个元素(-128 ~ 127),内存占用极小(约1KB) - 不能通过反射修改
IntegerCache.cache,JDK 9+已设为私有且final,反射尝试会失败 - 自定义类想模仿这种缓存?别照抄静态数组——考虑用
ConcurrentHashMap或WeakReference避免内存泄漏
其他基本类型包装类的缓存规则差异
不是所有包装类都像Integer一样缓存一段连续区间。Boolean只缓存true/false两个实例;Byte、Short、Character缓存0~127;Long和Float在JDK规范里**不保证任何缓存**(虽然某些JDK实现可能缓存小值,但代码不能依赖)。
容易踩的坑:有人看到Integer有缓存,就以为Long.valueOf(100)也一定复用对象——实际在OpenJDK中它不会,==比较大概率失败。
-
Boolean.valueOf("true")总是返回同一个true对象,这是唯一完全可预测的缓存 -
Character.valueOf(char)只缓存\u0000到\u007f(即ASCII 0~127),超出范围就新建 -
Double.valueOf("0.0") != Double.valueOf("0.0")——字符串解析路径不走缓存,且浮点数本身不适合精确相等判断
==当数值比较用,等于把脚伸进隐式转换的暗坑里。










