【JAVA】JDK14的空指针提示

1,741 阅读2分钟

本文旨在带你体验一下JDK14引入的新特性:更有用的空指针异常信息提示,即 JEP 358: Helpful NullPointerExceptions

  1. 下载JDK14

你可以从 OpenJDK 的官方页面下载:JDK 14 General-Availability Release

如果下载速度太慢,还可以去这个 国内镜像站 下载。

  1. 解压,配置环境变量

略。

配置好之后在命令行用 java -version 验证,确保当前用的是 JDK14 版本,示例输出如下:

~$ java -version
openjdk version "14" 2020-03-17
OpenJDK Runtime Environment (build 14+36-1461)
OpenJDK 64-Bit Server VM (build 14+36-1461, mixed mode, sharing)
  1. 编写会抛出空指针异常的代码

Hello.java 内容如下:

public class Hello {

    public static void main(String[] args) {
	// write your code here
        Person p = new Person();
        p.firstName = "Wenjian";
        p.lastName = null;
        String upperLastName = p.lastName.toUpperCase();
        String fullName = p.firstName + upperLastName;
        System.out.println("full name = " + fullName);
    }

    static class Person {
        public String firstName;
        public String lastName;
    }
}


  1. 编译并执行

在命令行窗口执行:

javac Hello.java 
java -XX:+ShowCodeDetailsInExceptionMessages Hello

显示的错误信息如下:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.toUpperCase()" because "<local1>.lastName" is null
        at Hello.main(Hello.java:8)

从这个信息中不仅能看出异常是在 Hello.java 的第 8 行出现的,还能知道是由于 <local1>.lastName 这个变量为空,所以在执行 String.toUpperCase() 方法时导致的这个异常。

其中:

<local1> 就是指的这个方法中声明的 第1个 local 变量,即 Person p = new Person()

接下来我们把变量 p 赋值为 null,即程序改成如下:

public class Hello {

    public static void main(String[] args) {
	// write your code here
        Person p = null;
        p.firstName = "Wenjian";
        p.lastName = null;
        String upperLastName = p.lastName.toUpperCase();
        String fullName = p.firstName + upperLastName;
        System.out.println("full name = " + fullName);
    }

    static class Person {
        public String firstName;
        public String lastName;
    }
}

重新编译并执行之后,错误信息如下:

Exception in thread "main" java.lang.NullPointerException: Cannot assign field "firstName" because "<local1>" is null
        at Hello.main(Hello.java:6)

同样的指出了在第 6 行,由于 <local1> 这个变量是 null, 所以在给它的 firstName 字段赋值时抛出了空指针异常,非常清晰。

另外,你可能注意到了,在我们执行程序时,加上了一个参数: -XX:+ShowCodeDetailsInExceptionMessages, 这是因为 JEP358 这个特性虽然在 JDK14 中已经引入,但是没有默认启用,需要在执行时加入这个参数来输出详细的空指针异常信息。

在 JDK15 版本中,这个特性将被默认设置成启用。

The feature can be toggled with the new boolean command-line option -XX:{+|-}ShowCodeDetailsInExceptionMessages. The option will first have default 'false' so that the message is not printed. It is intended to enable code details in exception messages by default in a later release.


20200424,补充:

在执行 javac Hello.java 的时候添加 -g 参数,可以将提示信息的 <local1> 显示为代码中实际的变量名称,对调试更加友好。

如:

javac Hello.java
java -XX:+ShowCodeDetailsInExceptionMessages Hello

Exception in thread "main" java.lang.NullPointerException: Cannot assign field "firstName" because "p" is null
        at Hello.main(Hello.java:6)

原理是:如果编译的时候加入了 -g 参数,javac 会把变量名字写入到 .class 文件中去。