../stay-away-from-the-npe

远离 NPE 的经验之谈

Published:

NPE

Null References: The Billion Dollar Mistake

NullPointException (下文以 NPE 代替) 可谓是 Java 开发者最常碰到的异常了, 没有之一. 下文简单分享几点经验来避免业务代码中的 NPE.

知其源

什么情况下会抛出 NPE? Java Doc

调用一个 null 实例的方法


Bean bean = null;
bean.getName(); // NPE 

获取或者修改 null 实例的字段


Bean bean = null;
bean.name; // NPE
bean.name="name" // NPE

获取 null 数组的长度


String[] sa = null;
int len = sa.length(); // NPE

获取或者修改 null 数组的元素


String[] sa = null;
String s1 = sa[0]; // NPE
sa[0] = "s1"; // NPE

抛出 null 异常实例


throws null; // NPE

几点经验

针对业务代码的几点经验和建议

集合相关

业务代码不可避免的要与集合打交道, 比如从数据库查一批数据.

建议

  1. 方法返回空集合(Collections.emptyList 等),而不是null.
  2. 结果判空使用 CollectionUtils.isEmpty(apache common-lang or spring utils)

字符串

String 和 基础数据类型一样, 代码出现的频率极高.

建议

  1. 方法返回 null.
  2. 结果判空使用 Strings.isNullOrEmpty(Guava)

关于各种 DTO 内字段的声明

一律使用对象. 即不使用 (int,char... 等基础类型), 避免自动开装箱导致的 NPE.

普通对象

  1. 方法返回 null.
  2. 判空使用 if(xx == null){...}

番外, 关于 Optional

经验证, 个人不推荐使用.

验证场景1, 字段使用 Optional 标明该字段是可能为 null.


Class Bean {
  private Integer id;
  private Optional<String> name;
}

问题:

  1. name 可能为 null, 这个 null 需不需要 check ?
  2. 对调用方不友好. name.ifPresent AND name.get 才能取到真正的 name 值

验证场景2, 取值后使用 Optional 包装.


Optinal.ofNullable(name).map(dosomehting).else(dosomething2)

// vs

if(name == null) {
 dosomehting
}else {
 dosomething
}

从可读性上来看, 更倾向于历史做法.

Optional 没有实现序列化接口

业务开发大多有 RPC, 以 Dubbo 为例, 接口的返回值内如果有 Optional, 序列化会失败.

小结

针对经常碰到的 NPE 问题, 给出来可行的建议, 并就不建议使用 Optional 做了简单的解释.