核心是避免null包装类进入自动拆箱:一旦参与==比较、算术运算、赋值给基本类型或Stream mapToInt等操作,即调用xxxValue()方法而抛NPE;应优先从源头减少null(如返回基本类型)、用Objects.requireNonNullElse或Optional封装、过滤null后再处理。

核心就一条:别让 null 的包装类走到自动拆箱那一步。Java 不会在编译期拦住你,但只要一拆箱(比如赋值给 int、参与 == 比较、做加法),null 就立刻抛 NullPointerException。
别在条件或运算里直接用包装类
这些写法看着自然,实则高危:
-
if (status == true)→ 改用Objects.equals(status, true) -
total = amount + tax;(amount 或 tax 是Integer)→ 改成(amount != null ? amount : 0) + (tax != null ? tax : 0) -
list.stream().mapToInt(Integer::intValue).sum()→ 先过滤:list.stream().filter(Objects::nonNull).mapToInt(Integer::intValue).sum()
三元运算符要特别小心
写 flag ? 1 : count,如果 count 是 Integer 且为 null,整个表达式会强制统一为 int 类型,触发拆箱,直接 NPE——哪怕 flag 是 true,count 根本不会被取到。
- 安全做法:两个分支都用包装类,如
flag ? Integer.valueOf(1) : count - 或者显式判空:
flag ? 1 : (count != null ? count : 0) - 更推荐:
Objects.requireNonNullElse(count, 0)或Optional.ofNullable(count).orElse(0)
从源头减少 null 包装类
防御是下策,不产生才是上策:
立即学习“Java免费学习笔记(深入)”;
- 方法返回值优先用基本类型(
int而非Integer),除非语义上必须区分“0”和“未设置” - DTO/VO 字段慎用包装类;必须用时,配 Jackson 注解:
@JsonInclude(NON_NULL)或@JsonSetter(nulls = Nulls.AS_EMPTY) - MyBatis 映射可能为 NULL 的列,用
nullValue="0"或自定义TypeHandler - 高频数值集合考虑 FastUtil 等原始类型集合库,彻底绕过包装类
该用 Optional 就别硬扛
当值天然可空(比如数据库查不到、API 返回缺失字段),就别用 Integer 暴露在接口上,改用 Optional<integer></integer>:
- 取默认值:
Optional.ofNullable(getCount()).orElse(0) - 链式处理:
opt.filter(x -> x > 100).map(x -> x * 2).ifPresent(System.out::println) - 绝不调用
.get(),那是把问题推给调用方
不复杂,但容易忽略。


















