Java自动装箱与拆箱是JDK 5引入的语法糖,编译期转为valueOf()和xxxValue()调用;运行时依赖缓存(如Integer[-128,127]复用对象),null拆箱抛NPE,==比较易误判,应优先用valueOf、避免new、判等用equals。

Java 自动装箱(Autoboxing)与拆箱(Unboxing)看似简单,但面试官追问“底层怎么实现”“什么情况下会出问题”“和缓存机制什么关系”,就容易露怯。关键不是背概念,而是讲清楚 编译期动作、运行时行为、字节码表现、缓存策略、潜在陷阱 这五层逻辑。
编译期:语法糖,实为方法调用
自动装箱/拆箱是 Java 5 引入的语法糖,编译器在编译阶段就替换成对应包装类的静态工厂方法或实例方法调用,运行期完全感知不到“自动”二字。
- 装箱(如 Integer i = 100;)→ 编译为 Integer.valueOf(100)
- 拆箱(如 int j = i;)→ 编译为 i.intValue()
验证方式:用 javap -c 查看字节码,你会看到明确的 invokestatic java/lang/Integer.valueOf 和 invokevirtual java/lang/Integer.intValue 指令,没有特殊 opcode。
运行时:valueOf() 是核心,缓存策略决定对象复用
Integer.valueOf(int) 不是简单 new 对象,它内置了缓存优化:
立即学习“Java免费学习笔记(深入)”;
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- 对值在 [-128, 127] 范围内的 int,直接返回缓存的 Integer 实例(通过静态内部类 IntegerCache 初始化)
- 超出该范围,则每次 new 新对象
所以:Integer a = 127; Integer b = 127; → a == b 为 true;Integer c = 128; Integer d = 128; → c == d 为 false(引用不同)。这和“== 比较地址”本质一致,不是 bug,是缓存设计的必然结果。
注意:Boolean、Byte、Character(\u0000–\u007f)、Short、Long 也有类似缓存,但范围不同(如 Boolean 只缓存 true/false 两个实例);Float、Double 的 valueOf 不缓存(浮点精度和范围使缓存意义不大)。
陷阱高频区:null 拆箱、集合泛型、== 判等
真正踩坑的地方,几乎都源于“把包装类当基本类型用,却忘了它是对象”:
- Integer x = null; int y = x; → 运行时报 NullPointerException(拆箱调用 x.intValue(),而 x 是 null)
- 使用 ArrayList
存数据,remove 时传 int 值:list.remove(1) → 本意删索引 1,实际调用 remove(Object),装箱成 Integer(1),删的是值为 1 的元素(可能误删) - 判等不用 equals() 用 ==:尤其在涉及计算、方法返回、跨范围赋值后,== 结果不可靠(缓存与否+是否 new 决定)
进阶对比:手动 new vs valueOf vs 直接赋值
三者行为差异直指底层逻辑:
- new Integer(100) → 总是新建对象,绕过缓存,== 必为 false(除非自己引用同一对象)
- Integer.valueOf(100) → 走缓存逻辑,[-128,127] 复用,否则 new
- Integer i = 100; → 编译即转为 valueOf(100),等价于第二条
所以《Effective Java》明确建议:**优先用 valueOf,禁用 new 包装类构造器**——既符合规范,又利用缓存,还避免无谓对象创建。

















