在Java中,NullPointerException是一种非常常见的异常。常常出现在以下5种场景中:

  1. 参数值是包装类如Integer,在使用时会出现自动拆箱导致空指针;
  2. 字符串类型常常出现空指针异常;
  3. 例如ConcurrentHashMap这类不支持key或value为null,强行put null的key或value则会出现空指针异常;
  4. A对象包含B对象,在通过A对象的字段获取B之后,没有对字段进行判空就级联调用B的方法而出现空指针异常;
  5. 方法或远程服务返回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);
}

以上程序编译都没有问题,不会出现报错。但是在执行测试用例时,就会抛出空指针异常了。

应对之策

  1. 对于 Integer 的判空,可以使用 Optional.ofNullable 来构造一个 Optional,或者在使用前先进行一次判空也行;
  2. 对于 String 和字面量的比较,可以把字面量放在前面,比如”OK”.equals(s),这样即使 s 是 null 也不会出现空指针异常;而对于两个可能为 null 的字符串变量的 equals 比较,可以使用 Objects.equals,它会做判空处理。
  3. 对于 ConcurrentHashMap,既然其 Key 和 Value 都不支持 null,修复方式就是不要把 null 存进去。HashMap 的 Key 和 Value 可以存入 null,而 ConcurrentHashMap 看似是 HashMap 的线程安全版本,却不支持 null 值的 Key 和 Value,这是容易产生误区的一个地方。
  4. 对于类似 fooService.getBarService().bar().equals(“OK”) 的级联调用,需要判空的地方有很多,包括 fooService、getBarService() 方法的返回值,以及 bar 方法返回的字符串。如果使用 if-else 来判空的话可能需要好几行代码,但使用 Optional 的话一行代码就够了。
  5. 对于 wrongMethod 返回的 List,由于不能确认其是否为 null,所以在调用 size 方法获得列表大小之前,同样可以使用 Optional.ofNullable 包装一下返回值,然后通过.orElse(Collections.emptyList()) 实现在 List 为 null 的时候获得一个空的 List,最后再调用 size 方法。

总结

程序中的变量是 null,就意味着它没有引用指向或者说没有指针。这时,我们对这个变量进行任何操作,都必然会引发空指针异常,在 Java 中就是 NullPointerException。

空指针异常虽然恼人但好在容易定位,更麻烦的是要弄清楚 null 的含义。

标签:

分类:

更新时间: