自动装箱是编译器在编译期将integer i = 100;替换为integer i = integer.valueof(100);,其valueof()缓存-128~127;拆箱即调用intvalue(),null时立即抛npe;==比较引用不可靠,应使用objects.equals()。

自动装箱就是编译器偷偷调用 Integer.valueOf()
Java 里写 Integer i = 100; 看似简单,实际编译后变成 Integer i = Integer.valueOf(100);——这不是 JVM 的运行时魔法,而是 javac 在编译期硬塞进去的调用。你用 javap -c 反编译就能看到字节码里明明白白写着 invokestatic java/lang/Integer.valueOf。
-
Integer.valueOf()有缓存:-128 到 127 的整数复用对象;超出范围就新建实例 - 别用
new Integer(100):绕过缓存、浪费内存,且从 Java 9 起已被标记为@Deprecated(forRemoval = true) - 缓存范围可通过 JVM 参数
-Djava.lang.Integer.IntegerCache.high=500扩大,但只影响valueOf(),不影响new
自动拆箱本质是调用 intValue(),null 就炸
写 int x = integerObj;,编译器会转成 int x = integerObj.intValue();。这意味着只要 integerObj == null,运行时就在这一行直接抛 NullPointerException,不是“后面用的时候”才出问题。
- 隐式拆箱藏得深:
if (a == 5)、a + 1、flag ? a : b全都会触发拆箱 -
List<integer> list = new ArrayList(); list.add(null); int x = list.get(0);</integer>→ 直接 NPE -
stream().mapToInt(x -> x)遇到null也崩,因为mapToInt内部要对每个元素调用intValue()
用 == 比较两个 Integer,结果不可靠
== 比的是引用,不是值。而 Integer.valueOf() 的缓存机制会让小数字“碰巧”指向同一对象,大数字则不会。
-
Integer a = 100, b = 100;→a == b是true(缓存内,复用对象) -
Integer c = 200, d = 200;→c == d是false(缓存外,各自新建) -
Integer e = null;→e == 0会先尝试拆箱,立刻 NPE - 安全做法永远是
Objects.equals(a, b)或显式转基本类型再比
哪些地方会静默触发装箱/拆箱?
不是你写了转换代码才发生,而是编译器为了满足类型契约,在三类上下文里自动补上:赋值、方法调用、运算与比较。
立即学习“Java免费学习笔记(深入)”;
- 赋值:
int i = 5; Integer obj = i;(装箱),Integer obj = new Integer(5); int j = obj;(拆箱) - 方法调用:传
int给void foo(Integer x)→ 装箱;传Integer给void bar(int y)→ 拆箱 - 泛型擦除后,
ArrayList<integer></integer>实际存的是Object,所以list.add(1)是装箱,int x = list.get(0)是拆箱 - 循环中频繁装箱(如
for (int i = 0; i )会显著增加 GC 压力,尤其值超出缓存范围时
最易被忽略的是:拆箱失败不报“空指针在比较时发生”,而是“在拆箱那一刻就挂了”——它发生在你根本没写 .intValue() 的地方。调试时如果看到 NPE 出现在看似安全的算术或条件判断行,先盯住那个包装类型变量是不是可能为 null。










