总结Java中常见的空指针NPE异常
在Java中,NullPointerException是一种非常常见的异常。常常出现在以下5种场景中:
- 参数值是包装类如Integer,在使用时会出现自动拆箱导致空指针;
- 字符串类型常常出现空指针异常;
- 例如ConcurrentHashMap这类不支持key或value为null,强行put null的key或value则会出现空指针异常;
- A对象包含B对象,在通过A对象的字段获取B之后,没有对字段进行判空就级联调用B的方法而出现空指针异常;
- 方法或远程服务返回List不是空而是null,没有进行判空就直接调用List的方法而出现空指针异常。
为了对以上5种场景进行理解,下面将进行举例说明。
测试示例
下面这段代码分别涵盖了前面所提到的5种场景。
// 定义一个将抛出空指针的demo类
public class NullPointerExceptionExample {
public List wrongMethod(FooService fooService, Integer i, String s, String t) {
// Scenario 1
System.out.println(i + 1);
// Scenario 2
System.out.println(s.equals("OK"));
System.out.println(s.equals(t));
// Scenario 3
new ConcurrentHashMap().put(null, null);
// Scenario 4
if (fooService.getBarService().bar().equals("OK")) {
System.out.println("OK");
}
// Scenario 5
return null;
}
}
public class FooService {
private BarService barService;
public BarService getBarService() {
return barService;
}
}
public class BarService {
public String bar() {
return "OK";
}
}
下面这段代码为junit测试代码:
@Test
public void wrongMethod() {
NullPointerExceptionExample service = new NullPointerExceptionExample();
FooService fooService = new FooService();
List res = service.wrongMethod(fooService, null, null, null);
}
以上程序编译都没有问题,不会出现报错。但是在执行测试用例时,就会抛出空指针异常了。
应对之策
- 对于 Integer 的判空,可以使用 Optional.ofNullable 来构造一个 Optional,或者在使用前先进行一次判空也行;
- 对于 String 和字面量的比较,可以把字面量放在前面,比如”OK”.equals(s),这样即使 s 是 null 也不会出现空指针异常;而对于两个可能为 null 的字符串变量的 equals 比较,可以使用 Objects.equals,它会做判空处理。
- 对于 ConcurrentHashMap,既然其 Key 和 Value 都不支持 null,修复方式就是不要把 null 存进去。HashMap 的 Key 和 Value 可以存入 null,而 ConcurrentHashMap 看似是 HashMap 的线程安全版本,却不支持 null 值的 Key 和 Value,这是容易产生误区的一个地方。
- 对于类似 fooService.getBarService().bar().equals(“OK”) 的级联调用,需要判空的地方有很多,包括 fooService、getBarService() 方法的返回值,以及 bar 方法返回的字符串。如果使用 if-else 来判空的话可能需要好几行代码,但使用 Optional 的话一行代码就够了。
- 对于 wrongMethod 返回的 List,由于不能确认其是否为 null,所以在调用 size 方法获得列表大小之前,同样可以使用 Optional.ofNullable 包装一下返回值,然后通过.orElse(Collections.emptyList()) 实现在 List 为 null 的时候获得一个空的 List,最后再调用 size 方法。
总结
程序中的变量是 null,就意味着它没有引用指向或者说没有指针。这时,我们对这个变量进行任何操作,都必然会引发空指针异常,在 Java 中就是 NullPointerException。
空指针异常虽然恼人但好在容易定位,更麻烦的是要弄清楚 null 的含义。