自动拆箱在调用intvalue()、longvalue()等方法的瞬间触发空指针,典型场景包括==比较、算术运算、基本类型传参及强制类型转换。

自动拆箱在哪一刻触发空指针?
不是在赋值时,而是在调用 intValue()、longValue() 等方法的瞬间。JVM 会隐式插入这些调用,你根本看不到——但堆栈里清清楚楚写着 java.lang.Long.longValue 或 Integer.intValue。
典型触发点包括:
-
==比较包装类与基本类型(如Integer a = null; if (a == 5)) - 算术运算(
Integer b = null; int c = b + 10;) - 传参给基本类型形参(
void f(int x) { };f(nullInteger)) - 强制类型转换(
(Long) map.get("id"),当值为null时)
包装类判空不能只靠 != null 吗?
能,但容易漏。比如你写了 if (num != null && num > 0),看似安全——可如果 num 是 Integer,这个 > 依然会触发自动拆箱,前面的判空挡不住后面的 num.intValue() 调用。
真正安全的写法是:
- 用
Objects.equals(a, b)替代==(支持null左右操作数) - 显式拆箱前加判空:
if (num != null) { int v = num.intValue(); ... } - 用
Optional.ofNullable(num).orElse(0)提前兜底,避免后续反复判空
集合或 Map 中取包装类值怎么防崩?
从 Map<string integer></string> 或 List<integer></integer> 里取值,最容易在 get() 或 get(index) 后直接参与运算或比较。
错误示范:int age = userMap.get("age") - 5; —— 只要 "age" 对应值是 null,立刻 NPE。
推荐做法:
- 统一用
Map.getOrDefault(key, defaultValue),例如userMap.getOrDefault("age", 0) - 对
List元素,先用Objects.nonNull(item)判再处理,别信list.size() > 0就代表元素非空 - RPC 或 JSON 反序列化返回的包装类字段,初始化时就设默认值(如
private Integer status = 0;),比处处判空更省心
为什么 IDE 警告和静态检查比“业务上不会为 null”更可靠?
因为 null 来源太多:数据库字段允许 NULL、MyBatis 映射未配置 jdbcType、Feign 接口返回体缺失字段、JSON 序列化库忽略空值策略……这些都不是业务逻辑能完全控制的。
真正管用的是两层防护:
- 编译期:启用 IntelliJ 的
Boxed value is compared using ==警告,或 ErrorProne 规则NP_AUTOBOX_NULL - 运行时:在关键入参处加
Objects.requireNonNull(param, "param must not be null"),尤其对 RPC 返回值、DAO 查询结果
最常被忽略的一点:泛型擦除后,List<integer></integer> 在运行时就是 List,JVM 不阻止你往里面塞 null——所以容器本身不保安全,得靠人盯住每个取值点。










