NullPointerException本质是逻辑漏洞,需从源头预防:对外部输入做非空校验、用Objects.requireNonNull明确契约、慎用Optional、启用IDE空值检查、区分Map中null键值语义。

NullPointerException 出现时,别急着加 try-catch
绝大多数 NullPointerException 不该靠捕获来解决,而是从源头掐断——它本质是逻辑漏洞,不是运行时意外。Java 的空指针异常往往暴露的是「本该非空却没校验」的契约断裂,比如传入 null 的参数、未初始化的字段、或链式调用中某环节返回了 null。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 对所有外部输入(方法参数、JSON 反序列化结果、数据库查询返回值)做显式非空判断,优先用
Objects.requireNonNull()抛出带提示的NullPointerException,而不是等后续某个.toString()才崩 - 避免在业务逻辑里写
if (obj != null) { obj.doSomething(); }这类防御性代码;该抛异常就抛,该设计为不可为空就用注解(如@NonNull)配合 IDE 或 Lombok 的@NonNull生成构造器校验 - 慎用
String.valueOf(obj)替代obj.toString()—— 它只是把null转成字符串"null",掩盖了本该失败的场景
Optional 不是用来包装所有可能为 null 的返回值
Optional 是为「函数式 API 设计」服务的,不是空值保险丝。滥用它会让调用方被迫写一堆 .orElse()、.map().orElse(null),反而增加理解成本和空指针风险(比如 Optional.of(null) 直接抛异常,Optional.empty().get() 也抛异常)。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 只在「明确表示‘有或没有’语义」的方法返回值中使用
Optional,例如findUserById(Long id),而不是getUserRole(User user)(后者应保证User非空,角色字段本身可为空则单独建模) - 不要用
Optional包装字段或参数:成员变量类型不能是Optional<String>,方法参数也不该是Optional<Integer>—— 这违反了其设计初衷,且序列化、ORM 映射都会出问题 - 避免链式调用中混用
Optional和普通对象:比如user.getProfile().map(Profile::getAvatar).orElse("default.png")看似安全,但如果user.getProfile()返回null,第一步就 NPE 了 ——Optional没法保护你调用链里的任意一环
IDEA / 编译器能帮你提前发现大部分空指针隐患
现代 Java 开发环境已经能把很多空指针问题拦在编译期或编辑期,前提是开启并信任这些检查。很多人关掉它们,或者把警告当耳旁风,结果上线后才踩坑。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 启用 IDEA 的
Nullable/NotNull inspection,并给关键参数、返回值加上@Nullable/@NotNull(推荐 JetBrains 注解,轻量无依赖) - 在
javac编译时加参数-Xlint:unchecked -Xlint:nullable(需 JDK 15+ 或插件支持),或用 Error Prone 的NullnessChecker - Lombok 的
@RequiredArgsConstructor+@NonNull字段能自动生成非空校验,但注意:它只校验构造时,不防住后续 setter 或反射修改;且若字段是基本类型包装类(如Integer),仍需业务逻辑判断是否为null
Map.get() 返回 null 并不总是意味着键不存在
这是最容易被忽略的空指针来源之一:Map 允许 null 作为 value 存储,所以 map.get(key) == null 无法区分「键不存在」和「键存在但值为 null」。直接判空再调用方法,极易触发 NPE。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 用
map.containsKey(key)明确判断键是否存在,再决定是否取值;或改用Map.getOrDefault(key, defaultValue),避免后续判空分支 - 如果业务上不允许
null值,初始化Map时选ConcurrentHashMap(它禁止null键值),或封装一层SafeMap在put时校验 - Stream 处理 Map 时尤其危险:比如
map.values().stream().map(String::length).collect(...)—— 若任意 value 为null,String::length就炸了;应先过滤:map.values().stream().filter(Objects::nonNull).map(String::length)...
真正难的不是写 Optional.ofNullable(x).map(...).orElse(...),而是想清楚 x 为什么可能为 null、这个 null 在领域模型里代表什么含义、以及谁该为这个状态负责。空指针从来不是语法问题,是设计信号。










