null and NullPointerException in Java: How to Avoid It
Introduction
(Note - This tutorial builds on Stack vs Heap Memory and Object ReferencesClick to read tutorial: Stack vs Heap Memory in Java and Object References.) What is null?
OUTPUT of the above code
Explanation of the code
Technique 1 - check for null, in the right order
When a value may legitimately be absent, check it before using it. Put the
OUTPUT of the above code
The
OUTPUT of the above code
Never return
Technique 3 - fail fast with requireNonNull
When
OUTPUT of the above code
Technique 4 - Optional for values that may be absent
For a method whose result may legitimately be missing, return
OUTPUT of the above code
Use
NullPointerException is the most common exception in Java programs. Its inventor, Tony Hoare, famously called the null reference his "billion-dollar mistake". This tutorial explains null and NullPointerException in Java, and how to avoid it. We will start with what null means, and where it comes from. We will then see every way a NullPointerException can be thrown - including some surprising ones - and read the helpful messages Java gives. Next we will go through the techniques that prevent it: checks, safe defaults, failing fast, Optional, and design choices that keep null out of your code in the first place.(Note - This tutorial builds on Stack vs Heap Memory and Object ReferencesClick to read tutorial: Stack vs Heap Memory in Java and Object References.) What is null?
null is a special value which means "this reference points to no object". It can be assigned to any reference type - String, arrays, your own classes - but not to a primitive such as int. null appears in a program in several ways -- fields of reference types, and elements of new object arrays, default to null;
- code assigns it explicitly, often to mean "no value" or "not found";
- library methods return it -
Map.get()for a missing key,System.getenv()for an unset variable,BufferedReader.readLine()at the end of input.
null: comparing with ==, assigning, passing it as an argument, and joining it to a string, which adds the text null.
When NullPointerException is thrown
A NullPointerException - an unchecked exception (see checked vs uncheckedClick to read tutorial: Checked vs Unchecked Exceptions in Java) - is thrown whenever the program tries to use the object a null reference does not point to. Since Java 14, its message says exactly what was null - see Helpful NullPointerExceptions in Java 14Click to read tutorial on Java 14 helpful NullPointerExceptions.
Ways a NullPointerException can happen
//NpeSources.java
import java.util.HashMap;
import java.util.Map;
public class NpeSources {
static class Customer {
String name;
Customer referredBy;
}
static String[] labels = {"call a method", "read a field", "array length", "array element",
"unboxing", "switch on null", "chained call", "throw null"};
public static void main(String[] args) {
String text = null;
Customer customer = new Customer(); // customer.name and referredBy are null
int[] numbers = null;
Map<String, Integer> stock = new HashMap<>();
for (int i = 0; i < labels.length; i++) {
try {
switch (i) {
case 0 -> text.length();
case 1 -> System.out.println(customer.referredBy.name);
case 2 -> System.out.println(numbers.length);
case 3 -> numbers[0] = 1;
case 4 -> { int count = stock.get("pens"); } // Integer null unboxed to int
case 5 -> { switch (text) { case "a" -> System.out.println("a"); default -> System.out.println("?"); } }
case 6 -> customer.name.trim().length();
case 7 -> { RuntimeException e = null; throw e; }
}
} catch (NullPointerException e) {
System.out.printf("%-15s -> %s%n", labels[i], e.getMessage());
}
}
}
}
call a method -> Cannot invoke "String.length()" because "text" is null read a field -> Cannot read field "name" because "customer.referredBy" is null array length -> Cannot read the array length because "numbers" is null array element -> Cannot store to int array because "numbers" is null unboxing -> Cannot invoke "java.lang.Integer.intValue()" because the return value of "java.util.Map.get(Object)" is null switch on null -> Cannot invoke "String.hashCode()" because "<local6>" is null chained call -> Cannot invoke "String.trim()" because "customer.name" is null throw null -> Cannot throw exception because "e" is null
- The first four are the obvious cases: calling a method, reading a field, or using the length or an element of an array through a
nullreference. - Unboxing is a hidden one:
stock.get("pens")returnednullfor the missing key, and assigning it to anintcalledintValue()on it. - A classic
switchon anullString or enum throws too, unless it has acase null(Java 21+). Its message is less helpful - it names<local6>, a hidden variable the compiler creates to hold the switch value, andhashCode(), which the compiled switch calls to find the matching case. - In a chain such as
customer.name.trim().length(), the message pinpoints which link wasnull- herecustomer.name.
null check first in a condition, so that short-circuit evaluationClick to read tutorial: Java Arithmetic, Relational and Logical Operators skips the risky call. When comparing with a constant, call equals() on the constant, which can never be null.
Checking for null safely
//NullChecks.java
import java.util.Objects;
public class NullChecks {
static boolean isAdmin(String role) {
return "ADMIN".equals(role); // safe even when role is null
}
static int nameLength(String name) {
if (name != null && !name.isBlank()) { // null check first
return name.strip().length();
}
return 0;
}
public static void main(String[] args) {
System.out.println("isAdmin(null) = " + isAdmin(null));
System.out.println("nameLength(null) = " + nameLength(null));
System.out.println("nameLength(\" Tom \") = " + nameLength(" Tom "));
System.out.println("Objects.equals(null, null) = " + Objects.equals(null, null));
System.out.println("Objects.toString(null, \"-\") = " + Objects.toString(null, "-"));
System.out.println("Objects.hash(null, 1) = " + Objects.hash(null, 1));
}
}
isAdmin(null) = false
nameLength(null) = 0
nameLength(" Tom ") = 3
Objects.equals(null, null) = true
Objects.toString(null, "-") = -
Objects.hash(null, 1) = 962
java.util.Objects class has null-safe helpers: equals(), toString(value, default), hash() and isNull()/nonNull().
Technique 2 - replace null with a safe default
Often the simplest fix is to never let null into the rest of the code: return an empty string, an empty collection, or a sensible default instead.
Safe defaults instead of null
//SafeDefaults.java
import java.util.List;
import java.util.Map;
import java.util.Objects;
public class SafeDefaults {
static List<String> findOrders(String customer) {
Map<String, List<String>> orders = Map.of("Tom", List.of("#101", "#105"));
return orders.getOrDefault(customer, List.of()); // empty list, never null
}
public static void main(String[] args) {
for (String c : new String[] {"Tom", "Nancy"}) {
List<String> list = findOrders(c);
System.out.println(c + " has " + list.size() + " orders " + list); // no null check needed
}
String nickname = null;
String shown = Objects.requireNonNullElse(nickname, "guest");
System.out.println("Hello, " + shown);
}
}
Tom has 2 orders [#101, #105] Nancy has 0 orders [] Hello, guest
null instead of an empty collection or array. Callers can loop over an empty list without any check; a null list forces every caller to remember one. Map.getOrDefault() and Objects.requireNonNullElse() make defaults easy.
null is not allowed, reject it at once - in the constructor or at the start of the method - with Objects.requireNonNull(). Otherwise the null is stored, and the exception happens much later, far from the code which caused it. (See exception handling best practicesClick to read tutorial: Java Exception Handling Best Practices.)
Rejecting null in a constructor
//FailFastNull.java
import java.util.Objects;
public class FailFastNull {
static class Invoice {
private final String customer;
Invoice(String customer) {
this.customer = Objects.requireNonNull(customer, "customer must not be null");
}
String title() { return "Invoice for " + customer.toUpperCase(); }
}
public static void main(String[] args) {
System.out.println(new Invoice("Tom").title());
try {
new Invoice(null);
} catch (NullPointerException e) {
System.out.println("Rejected at creation: " + e.getMessage());
}
}
}
Invoice for TOM Rejected at creation: customer must not be null
Optional<T> instead of a value-or-null. The return type then tells callers that "no result" is possible, and gives them methods to deal with it.
Optional instead of null for a missing result
//OptionalDemo.java
import java.util.List;
import java.util.Optional;
public class OptionalDemo {
static Optional<String> findEmail(String name) {
return List.of("tom@javabrahman.com", "harry@javabrahman.com").stream()
.filter(e -> e.startsWith(name.toLowerCase() + "@"))
.findFirst();
}
public static void main(String[] args) {
System.out.println(findEmail("Tom").orElse("no email"));
System.out.println(findEmail("Nancy").orElse("no email"));
findEmail("Harry").ifPresent(e -> System.out.println("Sending newsletter to " + e));
System.out.println(findEmail("Nancy").map(String::length).orElse(0));
}
}
tom@javabrahman.com no email Sending newsletter to harry@javabrahman.com 0
Optional mainly as a return type - not for fields or parameters. The Java 8 Optional Basics tutorialClick to read the Java 8 Optional Basics tutorial covers it in depth.
Design choices that keep null out
- Initialize fields when they are declared or in the constructor - an empty list, an empty string or a default object - so they are never
null. - Make objects immutable and validate everything in the constructor; a valid object then stays valid.
- Document null - say in Javadoc whether a parameter may be
nulland whether a method may return it. Many teams use@Nullableand@NonNullannotations, which IDEs and tools check. - Do not catch NullPointerException to hide it - it is a bug signal. Fix the code that produced the
null.
null means a reference points to no object, and where it comes from - default values, explicit assignments and library methods. We then triggered a NullPointerException in eight ways, including unboxing and switching on null, and read Java's helpful messages. Next we prevented it with null checks in the right order and "constant".equals(), with safe defaults such as empty collections, by failing fast with requireNonNull(), and with Optional return types. Lastly, we went through design choices that keep null out of code altogether. In the next tutorial, we move on to file I/O with reading and writing files with Path and FilesClick to read tutorial: Reading and Writing Files in Java with Path and Files.Core Java Tutorials
Java Garbage Collection Basics and Object LifecycleClick to read tutorial: Java Garbage Collection Basics and Object Lifecycle Reading and Writing Files in Java with Path and FilesClick to read tutorial: Reading and Writing Files in Java with Path and Files Stack vs Heap Memory in Java and Object ReferencesClick to read tutorial: Stack vs Heap Memory in Java and Object References
Java Garbage Collection Basics and Object LifecycleClick to read tutorial: Java Garbage Collection Basics and Object Lifecycle Reading and Writing Files in Java with Path and FilesClick to read tutorial: Reading and Writing Files in Java with Path and Files Stack vs Heap Memory in Java and Object ReferencesClick to read tutorial: Stack vs Heap Memory in Java and Object References