自动装箱和拆箱由编译器在基本类型与包装类混用时隐式插入,常见于集合操作、泛型传参、方法重载、算术运算等场景;装箱是int→integer,拆箱是integer→int,null拆箱抛nullpointerexception;integer缓存-128~127,故==比较仅在此范围内安全;推荐用objects.equals()或判空后equals();parseint()返回int更轻量,valueof()返回integer且小整数复用缓存;拆箱空指针需提前判空或用optional预防。

自动装箱和拆箱到底在什么时候发生
它不是“写代码时主动调用”的操作,而是编译器在你混用 int 和 Integer 时悄悄插入的隐式转换。比如把一个 int 值放进 ArrayList<integer></integer>,或者用 == 比较两个 Integer 变量——这些地方都在触发装箱或拆箱。
关键判断点:只要类型不匹配但能“勉强过编译”,JVM 就可能介入。常见场景包括集合操作、泛型参数传入、方法重载歧义、算术运算中混合使用基本类型和包装类。
- 装箱发生在:基本类型 → 包装类(如
int赋值给Integer变量、传参到Integer形参) - 拆箱发生在:包装类 → 基本类型(如调用
intValue()、参与+运算、作为if条件) - 注意:
null拆箱会直接抛NullPointerException,这是最常踩的坑
为什么 Integer a = 127; Integer b = 127; a == b 是 true,但 128 就是 false
因为 Integer.valueOf(int) 对 -128 到 127 之间的值做了缓存复用,超出范围就每次新建对象。所以 == 比较的是引用是否相同,而不是值是否相等。
这个缓存行为不是语言规范强制的,而是 OpenJDK 的实现细节,但几乎所有主流 JDK 都遵循。其他包装类也有类似逻辑:Boolean 总是缓存,Byte、Short、Character(0~127)也缓存,Long 和 Float 不缓存。
立即学习“Java免费学习笔记(深入)”;
- 永远别用
==比较两个包装类变量,除非你明确知道它们非null且在缓存范围内 - 安全做法是用
Objects.equals(a, b)或显式调用a.equals(b) - 如果真要比较值又怕
null,写成a != null && a.equals(b)更稳妥
Integer.parseInt("123") 和 Integer.valueOf("123") 有什么区别
前者只做字符串解析,返回 int;后者先解析再装箱,返回 Integer。虽然看起来只是多了一层包装,但性能和语义差异不小。
Integer.parseInt() 更轻量,适合后续要做大量计算的场景;Integer.valueOf() 在小整数范围内会复用缓存对象,避免频繁创建,适合放进集合或长期持有。
- 如果目标是存进
List<integer></integer>,用valueOf()更合适(避免额外装箱) - 如果只是临时算一下、马上参与加减乘除,用
parseInt()省一次对象分配 - 两者都可能抛
NumberFormatException,但valueOf(String)底层就是调用parseInt()再装箱,没额外容错
拆箱空指针异常怎么快速定位和预防
典型错误是把数据库查出来的可能为 null 的包装类字段,直接参与运算或比较。比如 user.getAge() + 1,而 getAge() 返回 Integer 且实际是 null —— 这里就会在运行时崩在拆箱那步,堆栈里只显示 NullPointerException,不提哪一行在拆箱。
IDE 通常不会警告这种潜在风险,得靠经验或静态检查工具(如 SpotBugs)发现。最简单的防御方式是提前判空或用 Optional。
- 不要写
if (integerVar > 0),先确认integerVar != null - 涉及数据库映射、JSON 反序列化、RPC 返回值的包装类型,一律默认可能为
null - Java 14+ 可以配合
@Nullable注解 + 编译器检查,但得统一团队规范
复杂点在于,有些框架(比如 MyBatis)对基本类型字段映射 null 时会静默设为默认值(如 0),换成包装类型才暴露真实 null —— 这个边界最容易被忽略。










