空指针异常(npe)应通过源头防控而非运行时捕获来根治:入口用objects.requirenonnull校验、返回值优先用optional或空集合、字段声明即初始化、配合@nonnull注解与静态检查工具在编译期拦截。

空指针异常(NPE)不是靠 try-catch 捕获来“处理”的,而是要在代码写出来之前就让它没机会发生。核心思路是:把 null 的存在性显式表达出来、限制它出现的位置、用语义清晰的工具替代手写判空。
入参和字段强制非空
方法入口和关键字段是第一道防线。一旦允许 null 进入,错误就会向下游扩散,排查成本指数级上升。
- 用 Objects.requireNonNull(obj, "提示信息") 替代 if (obj == null) throw new ... —— 异常堆栈更准,信息更明确,且支持提前失败
- 对象字段声明时直接初始化,比如 private List
- items = new ArrayList();
,避免后续 add() 或 size() 调用直接崩 - 构造函数中对必填依赖使用 requireNonNull,确保实例一创建就是完整、可用的状态
返回值不裸露 null
public 方法对外暴露的返回值,应尽量避免返回 null。调用方无法从签名得知是否要判空,容易遗漏。
- 集合类统一返回 Collections.emptyList()、Collections.emptySet() 等不可变空容器,而不是 null
- 数组返回 new Type[0],语义清晰,调用方可直接遍历或取 length
- 对于可能无结果的查询场景(如根据 ID 查单个对象),才考虑返回 Optional
,但仅限于返回值,不作为参数或集合元素
嵌套访问用 Optional 链式表达
当需要安全访问 user.getProfile().getContact().getEmail() 这类多层结构时,if 嵌套既难读又易漏,Optional 提供了声明式写法。
- 源头用 Optional.ofNullable(user) 包装,后续 map、filter、orElse 等操作自动跳过 null 分支
- 示例:return Optional.ofNullable(user).map(User::getProfile).map(Profile::getContact).map(Contact::getEmail).orElse("unknown@domain.com");
- 注意:简单判断(如 if (str != null && !str.isBlank()))比包装成 Optional.ofNullable(str).filter(...).isPresent() 更直接,别为用而用
字符串与集合统一空安全习惯
这两类对象最容易被误认为“不会为 null”,结果在 .length()、.isEmpty()、.stream() 时突然抛 NPE。
- 字符串判空优先用 str == null || str.isBlank()(Java 11+),低版本项目可引入 StringUtils.isEmpty() 或 isBlank()
- 对外提供集合时,用 Collections.unmodifiableList() 或 ImmutableList.copyOf() 封装,防止外部传入 null 或修改内部状态
- 流式处理含 null 元素的集合时,先用 .filter(Objects::nonNull) 清洗,再进行 map 或其他操作
借助注解和工具提前拦截
运行时防御是底线,编译期或 IDE 层面拦截才是高阶做法。
- 在参数、字段、返回值上添加 @NonNull(Lombok)或 @NotNull(JetBrains/JSR-305),IDE 实时标红潜在风险
- 配合 SpotBugs、ErrorProne 等静态分析工具,在 CI 阶段卡住带隐患的提交
- 团队内统一启用 Lombok 的 @RequiredArgsConstructor(onConstructor_ = @NonNull),让构造注入自动校验
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











