Java 避免空指针的方法及Optional最佳实践

【一】避免空指针的通用方法

✅ 1️⃣ 设计阶段:避免产生 null

  • 领域模型里避免「nullable」属性

  • 构造函数里要求非空

✅ 示例

public User(String name) {

   this.name = Objects.requireNonNull(name, "name must not be null");

}


✅ 2️⃣ 静态检查

  • 注解

    • @Nonnull, @Nullable

    • Checker Framework / ErrorProne / SpotBugs

✅ 示例

public String process(@Nonnull String input) { return input.trim(); }


✅ 3️⃣ 运行时保护

  • Objects.requireNonNull()


✅ 4️⃣ 语言级增强

  • Java 14+: 增强的 NPE 异常信息

    • 默认开启(Java 16+)

    • -XX:+ShowCodeDetailsInExceptionMessages

✅ 输出示例

Cannot invoke "Address.getCity()" because "person.getAddress()" is null


✅ 5️⃣ Null Object 模式

避免返回 null,用无害对象表示「空行为」

✅ 示例

class NullLogger implements Logger {

   public void log(String msg) { /* do nothing */ }

}


✅ 6️⃣ Optional

Java 8+ 的核心改进:明确表达「可能缺失」

✅ 语义

  • Optional 返回值 → 明确可为空

  • 避免调用者遗漏 null 检查

✅ 基本用法

Optional<User> findById(String id) {

   return Optional.ofNullable(repo.get(id));

}


【二】Optional 最佳实践

✅ ① 适用场景

✅ 用于方法返回值

  • 表示查询、解析、计算 可能没有结果

✅ 示例

Optional<User> findUserById(String id);


❌ ② 不推荐场景

❌ 参数

void setUser(Optional<User> user); // 不推荐

原因说明:

  • 违背设计初衷Optional 被设计为返回值容器,用于避免 null 返回值,不是为参数设计。

  • 调用不便:需要额外封装,降低可读性

  • 不能传 null:如果传 null,会抛 NullPointerException,如 Optional.of(null)

  • 不利于重载方法设计:Java 编译器类型擦除后会导致签名冲突

✅ 用

void setUser(@Nullable User user);


❌ 类字段

class User {

   Optional<Address> address; // 不推荐

}

原因说明:

  • 违反 JavaBean 规范:Optional没有实现序列化接口导致序列化报错,标准 JavaBean 属性期望 getX() 返回具体值而不是容器。EL 表达式(如 JSP/Thymeleaf)访问 user.name 只会得到 Optional 实例。

  • ORM 不支持:实体字段使用 Optional 会抛出 MappingException 或无法映射。

  • Jackson/Gson 无法正确序列化/反序列化

  • 运行时额外开销:每次访问字段都需要 .get().isPresent(),代码冗长。

✅ 用

class User {

   @Nullable Address address;

}

然后在 getter 中返回 Optional

public Optional<Address> getAddress() {

    return Optional.ofNullable(address);

}


❌ 集合

Optional<List<User>> getUsers(); // 不推荐

原因说明:

  • 设计冗余:集合天然支持元素缺失或为空的语义。

  • 处理繁琐

  • 泛型复杂度提升:组合类型如 Map<String, Optional<List<String>>> 可读性极差

✅ 直接返回空集合

List<User> getUsers();


✅ ③ 创建 Optional

Optional.of(value) // value != null

Optional.ofNullable(value) // 允许 value 为 null

Optional.empty()


✅ ④ 消费值

optional.ifPresent(u -> sendEmail(u.getEmail()));


✅ ⑤ 默认值

String result = optional.orElse("default");

String lazy = optional.orElseGet(() -> computeDefault());


✅ ⑥ 抛异常

User user = optional.orElseThrow(() -> new NotFoundException());


✅ ⑦ 链式变换

Optional<Address> address = userOpt.map(User::getAddress); Optional<String> city = userOpt.map(User::getAddress).map(Address::getCity);


✅ ⑧ 过滤

optional.filter(u -> u.isActive())


✅ ⑨ Java 9+ 扩展

optional.or(() -> otherOptional);

optional.ifPresentOrElse( val -> process(val), () -> handleAbsent() );


✅ ⑩ 避免 get()

⚠️ 反模式

String s = optional.get(); // 潜在 NPE

✅ 推荐

optional.orElse(...) optional.orElseThrow(...)


总结

✅ 方法返回值 → Optional 表示「可无」
✅ 参数 → 不用 Optional,直接 @Nullable
✅ 字段 → 不用 Optional,用普通类型 + @Nullable
✅ 集合返回值 → 返回空集合,不要包 Optional
✅ 使用链式 map / flatMap / filter
✅ 消费时用 orElse / orElseThrow / ifPresent

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fire-flyer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值