在Java 14中,一个新的特性被引入,即帮助性的NullPointerException。这最初是一个预览特性,后来成为了JDK的标准部分。这一增强的目的在于丰富NullPointerException的信息,因为传统的NullPointerException只提供了文件名、方法以及NullPointerException发生时的行号信息。
传统Java中的NullPointerException
在Java 14引入NullPointerException增强之前,以下代码会产生一个如下的NullPointerException消息:
Employee emp = new Employee(1,"Robert",null);
String dept = emp.getDept().getName();
如果使用以上代码片段来运行,将会产生类似的结果:
Exception in thread "main" java.lang.NullPointerException
at com.tutorialspoint.Tester.main(Tester.java:10)
Java 中新的信息丰富的NullPointerException
在调试过程中,这样的错误信息并不实用。随着层级的嵌套增加,检查哪个字段为null而导致的问题变得更加困难。Java 14解决了这一需求。它提供了一个选项来返回一个更加有用的消息,如下所示:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.tutorialspoint.Department.getName()" because the return value of "com.tutorialspoint.Employee.getDept()" is null
at com.tutorialspoint.Tester.main(Tester.java:10)
这里我们可以看到,打印出了更多的信息来协助调试getDept()方法返回null的问题。为了在Java 14中启用此特性,我们需要使用以下标志来运行Java代码:
-XX:+ShowCodeDetailsInExceptionMessages
现在,在Java 20中,这个标志不再是必需的。
对Null对象的帮助性NullPointerException
在这个例子中,我们展示上面学到的概念。这是一个很有用的功能,可以帮助调试由null引用导致的问题。这里我们创建了两个类Employee和Department。在main方法中,我们创建了Employee类的对象,但是部门传入的是null。然后我们尝试获取员工的部门名字,这将导致一个NullPointerException。在输出中,我们可以看到JVM抛出的带有详细信息的空指针异常。
示例
package com.tutorialspoint;
import java.util.List;
public class Tester {
public static void main(String[] args) {
Employee emp = new Employee(1,"Robert",null);
String dept = emp.getDept().getName();
System.out.println(dept);
}
}
class Employee {
int id;
String name;
Department dept;
Employee (int id, String name, Department dept){
this.id = id;
this.name = name;
this.dept = dept;
}
}
class Department {
int id;
String name;
List<Employee> employees;
}
输出 让我们编译并运行上述程序,这将产生如下结果:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.tutorialspoint.Department.getName()" because the return value of "com.tutorialspoint.Employee.getDept()" is null
at com.tutorialspoint.Tester.main(Tester.java:10)
对局部Null变量的信息丰富的NullPointerException
与对象引用类似,局部变量也可以为null。在Java 14中,如果使用了一个null的局部变量,那么JVM会在打印变量名时显示为,如下所示:
String name = null;
System.out.println("Length: " + name.length() );
如果使用以上代码片段来运行,将会产生类似的结果:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "<local1>" is null
at com.tutorialspoint.Tester.main(Tester.java:8)
为了打印出局部变量的名字,我们需要在运行程序时使用以下标志:
-g
使用这个标志会导致以下输出:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "name" is null
at com.tutorialspoint.Tester.main(Tester.java:8)
在Java 20及以后的版本中,这个标志不再是必需的。请参见下面的完整示例:
对Null局部变量的帮助性NullPointerException
在这个例子中,我们展示了如何调试null局部引用。这里我们创建了一个名为name的局部变量,并试图获取一个null字符串变量的长度,这将导致NullPointerException。在输出中,我们可以看到JVM抛出了带有实际null变量名的详细信息的空指针异常。
示例
package com.tutorialspoint;
public class Tester {
public static void main(String[] args) {
String name = null;
System.out.println("Length: " + name.length() );
}
}
输出 让我们编译并运行上述程序,这将产生如下结果:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "name" is null
at com.tutorialspoint.Tester.main(Tester.java:8)