Traps

90. Null Pointer Exceptions in Modern Java

Java 14+ shows exactly which variable was null in NPE messages; use Objects.requireNonNull, Optional, or @NonNull annotations to prevent NPEs early; avoid returning null from methods.

Java 14+ (helpful NPEs); Java 21 (core language)

Explanation

Before Java 14, NullPointerException would just say "NullPointerException" with a stack trace pointing to the line โ€” unhelpful if the line had multiple dereferences. Since Java 14 (JEP 358), the JVM generates helpful NPE messages that identify exactly which variable was null: "Cannot invoke "String.length()" because "name" is null".

Prevention strategies: - Objects.requireNonNull(obj, "message"): validates constructor/method arguments; throws NPE with a clear message immediately. - Return Optional<T> instead of null from methods where absence is expected. - Avoid returning null from public methods โ€” use Optional or empty collections. - Use Objects.requireNonNullElse(value, default) for null-safe defaults. - Use null-safe utilities: Objects.toString(obj, "default"), Objects.isNull(obj). - Consider @NonNull / @Nullable annotations (from Checker Framework, JetBrains, etc.) for static analysis.

Example

import java.util.Objects;
import java.util.Optional;

public class NullPointerModern {
    record User(String name, Address address) {}
    record Address(String city, String country) {}

    public static void main(String[] args) {
        // Helpful NPE message (Java 14+): tells you WHAT was null
        User user = new User("Alice", null); // address is null
        try {
            String city = user.address().city(); // NPE!
        } catch (NullPointerException e) {
            System.out.println("Helpful NPE: " + e.getMessage());
            // Cannot invoke "Address.city()" because the return value of "User.address()" is null
        }

        // Objects.requireNonNull: fail-fast with clear message
        try {
            String name = null;
            Objects.requireNonNull(name, "name must not be null");
        } catch (NullPointerException e) {
            System.out.println("requireNonNull: " + e.getMessage());
        }

        // Objects.requireNonNullElse: null-safe default
        String maybeNull = null;
        String safe = Objects.requireNonNullElse(maybeNull, "default");
        System.out.println("Safe value: " + safe); // "default"

        // Optional instead of null return
        Optional<String> city = Optional.ofNullable(user.address())
                                        .map(Address::city);
        System.out.println("City: " + city.orElse("Unknown")); // Unknown

        // Null-safe string
        Object obj = null;
        System.out.println(Objects.toString(obj, "null-safe")); // "null-safe"

        // Constructor guard
        class SafeService {
            private final String url;
            SafeService(String url) {
                this.url = Objects.requireNonNull(url, "url must not be null");
            }
        }
        try { new SafeService(null); }
        catch (NullPointerException e) { System.out.println("Guard caught: " + e.getMessage()); }
    }
}

Sources