如何编写 Null-Safety 代码?一文帮你讲透!

NullPointerExceptions(NPE)空指针异常应该是每个程序员的噩梦,作为 Java 程序员都知道: NPE 是运行时异常,在代码编译过程中很难发现这种异常。因此,这篇文章,我们将分析如何编写 null 安全代码。

在实际工作中,对于 null的处理通常有以下处理方式:

  • Nullable注解
  • NonNull注解
  • 显示处理 null
  • 使用 Optional类
  • 使用断言
  • 编写清晰的文档

Nullable注解

@Nullable是 Spring 的内置注解,可用于方法、参数或字段级别,用于标记方法的返回值或参数或字段可以为 null,通过使用 @Nullable 注解,开发者可以明确地表明某个方法可能返回 null 值,从而在调用该方法时更加谨慎。

如下示例:

 

java

代码解读

复制代码

// 示例1:用于方法 @Nullable public String getUserById(Long userId) { if (userId == null) { return null; } // 根据userId查询用户邮箱 return userRepository.findUserById(userId); } // 示例2:用于参数 User addUser(@NonNull Long userId, @Nullable String email); // 示例3:用于字段 public class User { @NonNull Long userId; @Nullable String email; }

在上述的示例中,@Nullable 用于方法,字段,参数,标识可以为 null,因此,方法的调用用就需要根据业务判断 null。

NonNull注解

@NonNull 是 Spring 的内置注解,可用于方法、参数或字段级别,用于标记方法的返回值或参数或字段不能为 null。通过使用 @NonNull 注解,开发者可以明确地表明某个方法不会返回 null 值,从而在调用该方法时可以放心地使用返回值。

如下示例:

 

java

代码解读

复制代码

import org.springframework.lang.NonNull; // 示例1:用于方法 @NonNull public User getUserById(Long userId) { // 根据userId查询用户信息 User user = userRepository.findUserById(userId); if (user == null) { throw new IllegalArgumentException("User not found"); } return user; } // 示例2:用于参数 User findUserById(@NonNull Long userId); // 示例3:用于字段 public class User { @NonNull Long id; }

在上述的示例中:

示例1,@NonNull 注解标记方法其返回值不能为 null,因此,方法的调用者知道该方法不会返回 null 值,可以放心地使用返回值而不用担心空指针异常。

示例2,@NonNull 注解标记方法的入参不能为 null

示例3,@NonNull 注解标记字段不能为 null,一旦字段值为 null,IDE会出现以下警告:

显式处理 null

显式处理 null应该是我们使用最多也是最灵活处理 null的方法,在编写代码时,要注意对可能为 null 的值进行显式的空值检查和处理,可以使用 if 语句、try-catch块等方式来处理空值情况,避免程序崩溃或数据丢失。

如下代码,针对 user 对象是否为 null 做不同的处理:

 

java

代码解读

复制代码

// 方式1 if (user != null) { // 执行操作 } else { // 处理空值情况 } // 方式2 try { // 业务处理 } catch (Exception e) { // 异常处理 }

使用 Optional类

Optional类是 Java 8 引入的,它的作用是封装一个可能为空的值,通过 Optional 类的方法,可以更加安全和优雅地处理空值情况,从而避免 NPE 空指针异常。

如下示例,针对上面手动处理 null, 我们可以使用 optional更优雅的实现 null 判断:

 

java

代码解读

复制代码

Optional<User> userOptional = Optional.ofNullable(user); String userName = userOptional.map(User::getName).orElse("Unknown");

使用 Optional 类的几个好处:

  • 可以避免 NPE 空指针异常,提高代码的健壮性;
  • 可以减少手动 null 判断,使代码更简洁和优雅;
  • 符合函数式编程的特性;

使用断言

断言 assert 可以帮助发现潜在的空指针异常问题,确保代码的假设得到正确验证。

如下示例:

 

java

代码解读

复制代码

assert user != null : "User object should not be null";

在上述示例中,我们使用断言来验证 user 对象不为 null,如果断言失败,则抛出相应的错误信息。

编写清晰的文档

在方法的注释中明确说明方法的返回值和参数是否可以为 null,以及如何处理 null情况。清晰的文档可以帮助其他开发者更好地理解代码逻辑和规范。

 

java

代码解读

复制代码

/** * 根据用户ID获取用户邮箱地址 * @param userId 用户ID * @return 用户邮箱地址,可能为null */ @Nullable public String getUserEmailById(Long userId) { // 方法实现 }

上述示例中,我们编写了清晰的方法注释,明确说明了方法的返回值可能为 null,帮助其他开发者更好地理解方法的使用规范和处理空值情况。

注意事项

对于上述 Spring 的 @Nullable 和 @NonNull 注解,并非所有开发工具都能显示这些编译警告,如果未看到相关警告,请检查 IDE 中的编译器设置。这里以 IntelliJ 设置为例:

为了方便 IDE 进行一些自动化代码检查,我们可以使用 SpotBugs提供了一个 插件,可以检测由于可为 null 性而导致的代码异味。

 

java

代码解读

复制代码

[ERROR] High: xxx may return null, but is declared @Nonnull At xxx.java:[line 36] NP_NONNULL_RETURN_VIOLATION

总结

本文,我们总结了对 null处理的几种通常方式:

  • Nullable注解
  • NonNull注解
  • 显示处理 null
  • 使用 Optional类
  • 使用断言
  • 编写清晰的文档

功夫在平常,功夫在细节!只要我们在日常开发中多注意一点细节,让好习惯成为自然,终有一天,不但可以写出让人羡慕的优雅代码,还可以写出高质量的代码。

  • 15
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值