避免空指针异常的核心是从设计源头杜绝null:构造阶段强制非空、用Optional封装可选值、返回空集合或Optional替代null、注解声明非空契约、用业务语义类型替代null。

避免空指针异常(NullPointerException)的核心,不是靠事后捕获,而是从对象设计源头就杜绝 null 的不合理存在。关键在于:让对象在构造完成时即处于可用状态,把“可能为空”的责任从调用方转移到设计方。
构造阶段强制非空,拒绝“半成品”对象
不要提供无参构造器或允许关键字段为 null 的构造方式。使用全参数构造器,并在构造方法中对必要参数做非空校验。
- 用
Objects.requireNonNull(param, "name cannot be null")立即抛出清晰异常,而不是静默接受null - 若某字段逻辑上可选,考虑用
Optional封装,明确表达“可能存在也可能不存在” - 避免 setter 方法暴露关键字段的修改入口,防止对象中途被置为无效状态
返回值不裸露 null,用语义化替代方案
方法返回值是空指针高发区。与其返回 null,不如返回更安全、更有表达力的结构。
- 集合类统一返回空集合(如
Collections.emptyList()),而非null - 查找方法可返回
Optional,迫使调用方显式处理“未找到”分支 - 工厂或构建方法可返回专用结果类型(如
Result),内含成功数据或错误原因
API 接口契约清晰,用注解辅助静态检查
通过代码即文档的方式,在接口层面声明空值约束,让问题在编码阶段暴露。
立即学习“Java免费学习笔记(深入)”;
- 在参数、返回值、字段上标注
@NonNull(如 JetBrains 或 Checker Framework 提供的注解) - 配合 IDE(如 IntelliJ)或构建插件(如 ErrorProne),自动提示潜在空值解引用
- 团队内部约定:未标注
@Nullable的引用类型,默认视为不可为空
谨慎使用 null 作为业务语义,优先建模真实含义
很多场景下,null 并非技术妥协,而是掩盖了业务逻辑模糊。应将其转化为明确的状态或类型。
- 用户未填写电话 → 不是
phone = null,而是contactPreference = ContactPreference.NONE - 订单尚未分配快递单号 → 不是
trackingNo = null,而是status = OrderStatus.WAITING_SHIPMENT - 避免用
null表示“未知”“不适用”“已删除”,而用枚举、标记字段或独立子类型表达
防御性不是写一堆 if (obj != null) ,而是让 null 在系统里没有合法落脚点。设计时多想一步“这个值为什么可能为空”,往往就能找到更健壮的建模方式。











