如何避免Java代码中的空指针错误NullPointerException? - foojay


根据2016年的一项研究,可怕的代码 (简称NPE)是生产中最常见的Java异常 。在本文中,我们将探讨与之抗衡的主要技术:自我验证模型和 Optional 包装器。
 
自我验证模型
想象一下业务规则:每个客户都必须设置生日。有多种方法可以实现此约束:在创建和更新用例上验证用户数据,通过NOT NULL 数据库约束强制执行 和/或在Customer实体的构造函数中实现空检查权限
以下是当今最常用的用于对构造函数进行空值检查的形式:

public Customer(@NonNull Date birthDate) { // 3
  assert (birthDate != null); // 4
  if (birthDate == null) { // 1
     throw new IllegalArgumentException();
  }
  this.birthDate = Objects.requireNonNull(birthDate); // 2
}

上面的代码包含4种 替代 方法来完成相同的事情,任何一个当然就足够了:
  1. 经典 if 判断
  2. 使用Java 8的检查 java.util.Objects -在当今开发中的项目中使用最广泛
  3. Lombok @NonNull 导致将 if 检查添加到生成的字节码中。
  4. assert关键字:我个人不喜欢它,因为可以通过JVM参数全局禁用断言

如果存在出生日期的设置器,则检查将移至该位置,而构造函数将调用该设置器。 
在数据对象的构造函数中强制执行空检查具有明显的优势:没有人会忘记这样做。但是,通过反射直接写入实例字段的框架可能会绕过此检查。Hibernate默认情况下会执行此操作,因此我的建议是也将数据库中相应的必需列标记为NOT NULL 确保数据返回一致。不幸的是,在必须容忍“不正确的历史数据”的传统系统中,问题可能变得更加复杂。
技巧:Hibernate在每个持久性实体上都需要一个无参数的构造函数,但是可以将该构造函数标记为 protected 对开发人员隐藏。 
如果 null 确实是一个有效值呢?例如,假设我们的客户可能没有会员卡,因为她尚未创建会员卡,或者可能不想注册会员卡。我们将在下一节讨论这种情况。
 
Getter返回Optional
最佳实践:由于Java 8中,每当一个函数需要返回 null,应该返回 Optional。
假设我们正在谈论映射到关系数据库的Entity,那么如果您没有NOT NULL 在相应的列上强制执行 ,则该字段的getter应该返回 Optional。对于不提供null 保护的非持久数据对象或NoSQL数据存储 ,上一节提供了有关如何在实体代码中以编程方式强制执行空检查的想法。 
为了简化过渡到这种新方式的过程,可以使用以下安全步骤序列:
首先,创建第二个getter返回 Optional:
public Optional getMemberCardOpt() { 
   return Optional.ofNullable(memberCard); 
}

其次,更改原始的getter以委托给新的getter:
public String getMemberCard() { 
    return getMemberCardOpt().orElse(null); 
}