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免费学习笔记(深入)”;
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 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")——字符串解析路径不走缓存,且浮点数本身不适合精确相等判断
==当数值比较用,等于把脚伸进隐式转换的暗坑里。

















