自动装箱与拆箱是编译器在编译期插入的语法糖,本质是将Integer i = 100;替换为Integer.valueOf(100),int x = i;替换为x = i.intValue();valueOf对-128~127范围内的整数缓存复用,超出则新建对象;拆箱时若包装类为null则调用xxxValue()抛NullPointerException。

自动装箱与拆箱本质是编译器在编译期插入的语法糖,不是运行时动态行为。理解它,关键看三点:编译后字节码怎么变、valueOf()如何决定是否复用对象、拆箱时null怎么引发异常。
编译器如何把代码“改写”
Java 源码中看似简单的赋值,编译后会被替换成明确的方法调用:
- Integer i = 100; → 编译为 Integer i = Integer.valueOf(100);
- int x = i; → 编译为 int x = i.intValue();
- 同理,Boolean b = true; 变成 Boolean b = Boolean.valueOf(true);
这个替换发生在 javac 阶段,JVM 运行时只执行 valueOf 或 xxxValue 方法,不感知“自动”二字。
valueOf 的缓存逻辑决定 == 是否安全
以 Integer 为例,valueOf(int) 内部有缓存判断:
立即学习“Java免费学习笔记(深入)”;
- 若参数在 -128 到 127(含)之间,直接返回 IntegerCache.cache 中已有对象
- 超出该范围,每次 new Integer(i),生成新对象
- Byte/Short/Character/Long 同样缓存 -128~127;Boolean 全部缓存 true/false;Float/Double 不缓存
所以 Integer a = 127, b = 127; a == b 是 true;但 Integer c = 128, d = 128; c == d 是 false —— 因为后者指向不同堆对象。
拆箱是隐式调用 xxxValue(),null 就崩
拆箱不是“解开包装”,而是调用包装类实例的方法取基本值:
- int v = someInteger; 等价于 int v = someInteger.intValue();
- 如果 someInteger == null,调用 intValue() 就抛 NullPointerException
- 常见高危场景:从 Map
取值后直接赋给 int、三元表达式中混用 null 包装类、泛型集合 get() 后未判空就拆箱
性能和设计层面的关键提醒
装箱拆箱方便,但代价真实存在:
- 高频装箱(如 for 循环里 list.add(i))→ 大量短命 Integer 对象 → 增加 GC 压力
- 集合操作优先用原始类型替代方案(如 Eclipse Collections 的 IntList、或数组 int[])
- 比较值一律用 .equals() 或先确保非 null 再用 ==;避免依赖缓存边界做逻辑分支
- 构造器 new Integer(x) 已废弃(Java 9+),必须用 valueOf(x) 或直接字面量赋值


















