Java 14 Helpful NullPointerExceptions Explained
Introduction
The
(Note - This feature is part of Java 14Click to read tutorial: Overview of New Features in Java 14.) The new messages The program below triggers a null dereference in several different ways, catches each exception, and prints its message -
OUTPUT of the above code
Explanation of the code
Why local variables appear as <local0>
The JVM computes the message only when the exception is thrown, by analyzing the bytecode of the method which failed. Field and method names are always in the bytecode, but the names of local variables are only kept if the class was compiled with debug information for variables, using
OUTPUT of the above command
Now the unboxing and
Is it safe to show these messages?
The messages contain names of fields, methods and local variables, but never the values of variables. The JEP noted that the names could still reveal details of the code to someone reading error output, which was one reason the feature was off by default in Java 14. Since Java 15 the messages are on by default, and the usual rule applies: do not show raw exception messages and stack traces to end users of an application; log them instead.
Summary
In this tutorial we saw how Java 14's helpful NullPointerExceptions name the exact expression which was null - a field, an array, a method's return value, or a local variable - and the action which failed. We saw that local variable names need classes compiled with
NullPointerException is the most common exception in Java. Before Java 14, its stack trace told you only the line where it happened. On a line such as order.customer().address().city(), any of four references could have been null, and you had to guess which - often by adding logging and running the code again. Java 14 (JEP 358) added helpful NullPointerExceptions: the exception's message now says precisely which expression was null, and what the code was trying to do with it. This tutorial explains helpful NullPointerExceptions with examples. We will see the new messages for different kinds of null dereference, how the messages are computed, why local variable names sometimes appear as <local0>, and when the messages are turned on.(Note - This feature is part of Java 14Click to read tutorial: Overview of New Features in Java 14.) The new messages The program below triggers a null dereference in several different ways, catches each exception, and prints its message -
Different kinds of null dereference
//HelpfulNpeDemo.java
public class HelpfulNpeDemo {
static class Address { String city; }
static class Customer { String name; Address address; Address address() { return address; } }
static void attempt(String label, Runnable action) {
try {
action.run();
} catch (NullPointerException e) {
System.out.println(label + ":\n " + e.getMessage());
}
}
static Customer customer = new Customer(); // name and address are null
static int[] scores; // null array
public static void main(String[] args) {
attempt("Method call on a null field", () -> customer.name.length());
attempt("Chained calls", () -> System.out.println(customer.address().city.toUpperCase()));
attempt("Writing a field of a null object", () -> customer.address.city = "Pune");
attempt("Array access", () -> System.out.println(scores[0]));
attempt("Unboxing null", () -> { Integer count = null; int c = count; });
attempt("Throwing null", () -> { RuntimeException ex = null; throw ex; });
}
}
Method call on a null field: Cannot invoke "String.length()" because "HelpfulNpeDemo.customer.name" is null Chained calls: Cannot read field "city" because the return value of "HelpfulNpeDemo$Customer.address()" is null Writing a field of a null object: Cannot assign field "city" because "HelpfulNpeDemo.customer.address" is null Array access: Cannot load from int array because "HelpfulNpeDemo.scores" is null Unboxing null: Cannot invoke "java.lang.Integer.intValue()" because "<local0>" is null Throwing null: Cannot throw exception because "<local0>" is null
- Each message has two parts: the action which failed ("Cannot invoke String.length()", "Cannot load from int array", "Cannot assign field city") and the reason - the exact expression which was null, such as
"HelpfulNpeDemo.customer.name". - For the chained call, the message pinpoints
the return value of ...address()- the method call in the middle of the chain which returned null. Before Java 14 this line would have produced a bareNullPointerException. - Unboxing a null
Integerinto anintis reported as a failed call toInteger.intValue(), which is what the compiler generates for unboxing. - Two of the messages show
"<local0>"instead of a variable name. This is explained next.
javac -g. Without it, the JVM can only refer to the variable by its slot number in the method, such as <local0> for the first one (here each lambda body is compiled into its own method, so the variable is in slot 0). Compiling the same program with -g -
javac -g -d classes-g HelpfulNpeDemo.java java -cp classes-g HelpfulNpeDemo
Method call on a null field: Cannot invoke "String.length()" because "HelpfulNpeDemo.customer.name" is null Chained calls: Cannot read field "city" because the return value of "HelpfulNpeDemo$Customer.address()" is null Writing a field of a null object: Cannot assign field "city" because "HelpfulNpeDemo.customer.address" is null Array access: Cannot load from int array because "HelpfulNpeDemo.scores" is null Unboxing null: Cannot invoke "java.lang.Integer.intValue()" because "count" is null Throwing null: Cannot throw exception because "ex" is null
throw messages name the variables count and ex. Build tools such as Maven and Gradle compile with full debug information by default, so in most real projects you will see the names.
When are the messages shown?
- Java 14: the messages were off by default, and enabled with the JVM option
-XX:+ShowCodeDetailsInExceptionMessages. - Java 15 and later: they are on by default. The option
-XX:-ShowCodeDetailsInExceptionMessagesturns them off. - They apply only to exceptions thrown by the JVM itself. A
NullPointerExceptioncreated in code, such as byObjects.requireNonNull(x, "x must not be null"), keeps its own message. - The message is computed lazily, only when
getMessage()is called, so there is no cost for exceptions which are caught and never inspected.
javac -g, and otherwise appear as <local0>. Lastly, we saw that the messages are opt-in in Java 14, on by default from Java 15, and only computed when they are needed. Next, read the JFR event streaming tutorialClick to read tutorial: Java 14 JFR Event Streaming Tutorial with Examples.Continue Reading Java 12 to 17 Tutorials
Overview of New Features in Java 14Click to read tutorial: Overview of New Features in Java 14 Java Switch Expressions: Arrow Labels and yieldClick to read tutorial: Java Switch Expressions: Arrow Labels and yield Java 14 JFR Event Streaming Tutorial with ExamplesClick to read tutorial: Java 14 JFR Event Streaming Tutorial with Examples Overview of New Features in Java 12Click to read tutorial: Overview of New Features in Java 12 Overview of New Features in Java 13Click to read tutorial: Overview of New Features in Java 13 Overview of New Features in Java 15Click to read tutorial: Overview of New Features in Java 15 Overview of New Features in Java 16Click to read tutorial: Overview of New Features in Java 16 Overview of New Features in Java 17Click to read tutorial: Overview of New Features in Java 17 Java 12 API Additions: String.indent, teeing, mismatchClick to read tutorial: Java 12 API Additions: String.indent, teeing, mismatch
Overview of New Features in Java 14Click to read tutorial: Overview of New Features in Java 14 Java Switch Expressions: Arrow Labels and yieldClick to read tutorial: Java Switch Expressions: Arrow Labels and yield Java 14 JFR Event Streaming Tutorial with ExamplesClick to read tutorial: Java 14 JFR Event Streaming Tutorial with Examples Overview of New Features in Java 12Click to read tutorial: Overview of New Features in Java 12 Overview of New Features in Java 13Click to read tutorial: Overview of New Features in Java 13 Overview of New Features in Java 15Click to read tutorial: Overview of New Features in Java 15 Overview of New Features in Java 16Click to read tutorial: Overview of New Features in Java 16 Overview of New Features in Java 17Click to read tutorial: Overview of New Features in Java 17 Java 12 API Additions: String.indent, teeing, mismatchClick to read tutorial: Java 12 API Additions: String.indent, teeing, mismatch