Quick Answer

A NullPointerException means you called a method or accessed a field on a reference that holds null. The frequent sources are an uninitialised object field, a method returning null, an unfound map key, an autoboxed null Integer being unboxed to int, and a null returned from a database or API. Modern Java prints helpful messages naming the exact expression that was null. Prevent them by returning empty collections instead of null, validating inputs, and using Optional at API boundaries.

What It Means and How to Read It

In Java, an object variable holds a reference. If it holds null, it points at nothing — so any attempt to use it as an object fails.

String name = null;
int length = name.length();   // NullPointerException

Five operations trigger it: calling a method on null, reading or writing a field on null, taking .length of a null array, indexing a null array, and unboxing a null wrapper.

Older Java printed only a line number, which was painful when several things on that line could be null. Since JDK 14, helpful NullPointerException messages name the exact culprit:

Exception in thread "main" java.lang.NullPointerException:
  Cannot invoke "String.length()" because "<local1>.name" is null

Read it as: the thing after because is what was null; the thing after Cannot invoke is what you tried to do with it. On a chained call like a.getB().getC() it tells you precisely which link failed, which used to require a debugger.

If you are on JDK 14 or later and not seeing these, they can be enabled with -XX:+ShowCodeDetailsInExceptionMessages; they are on by default from JDK 15.

Where Nulls Actually Come From

Uninitialised fields. Object fields default to null, unlike local variables which the compiler forces you to initialise.

class User {
    String name;          // defaults to null
    List<String> roles;   // also null, not an empty list
}

new User().roles.add("admin");   // NPE

Methods that return null. A search that finds nothing often returns null, and the caller forgets to check.

User u = repository.findByEmail(email);   // null if absent
System.out.println(u.getName());          // NPE

Map lookups. map.get(key) returns null for a missing key rather than throwing. Use getOrDefault when a sensible default exists.

int count = counts.get("missing");        // NPE on unboxing
int safe  = counts.getOrDefault("missing", 0);   // fine

Autoboxing. That last example is worth pausing on. A Map<String, Integer> returns a null Integer, and assigning it to an int unboxes it by calling intValue() on null. The NPE appears on a line with no visible method call, which makes it genuinely confusing the first time.

Uninitialised arrays of objects. new String[5] creates five null references, not five empty strings.

Checking Properly

The basic guard is a null check, but where you put it matters.

if (user != null && user.isActive()) { ... }

This is safe because && short-circuits — if the first test fails, the second never runs. Reversing the order would throw.

Compare constants first when using equals:

if (status.equals("ACTIVE"))    // NPE if status is null
if ("ACTIVE".equals(status))    // safe: literal is never null

Objects.equals(a, b)            // null-safe both ways

Fail fast on invalid inputs rather than letting a null travel deep into your code and explode somewhere unrelated:

public void register(User user) {
    Objects.requireNonNull(user, "user must not be null");
    // now the failure names the real problem, at the boundary
}

A useful principle: an NPE thrown three layers below where the null entered is expensive to debug. Validating at the boundary converts it into a clear message at the point of the actual mistake.

For strings, String.valueOf(obj) returns the text "null" instead of throwing, which is occasionally what you want for logging.

Using Optional Correctly

Optional makes absence explicit in the type, so the compiler reminds the caller to handle it.

public Optional<User> findByEmail(String email) {
    return Optional.ofNullable(database.lookup(email));
}

// The caller cannot forget
String name = findByEmail(email)
        .map(User::getName)
        .orElse("Unknown");

Use ofNullable when the value may be null and of when it must not be — Optional.of(null) throws immediately, which is a useful assertion.

The mistake that defeats the purpose:

Optional<User> result = findByEmail(email);
User u = result.get();      // throws NoSuchElementException if empty

Calling get() without checking has simply renamed the exception. Use orElse, orElseGet, orElseThrow, map or ifPresent instead.

Optional is designed for return types. Using it for fields or method parameters is discouraged — it is not serialisable, adds allocation, and a nullable parameter is better expressed with an overload.

And for collections, prefer an empty list to an Optional or a null:

return results.isEmpty() ? Collections.emptyList() : results;

An empty collection iterates safely with no checks at all, which is why it is the better default.

Designing Nulls Out

Prevention beats defensive checks scattered through the codebase.

  • Never return null from a method returning a collection. Return an empty one. Every caller then works without a guard.
  • Initialise fields at declaration where a sensible empty value exists — private List<String> roles = new ArrayList<>();.
  • Validate at the boundary with Objects.requireNonNull, so failures name the caller rather than some distant internal line.
  • Use Optional for lookups that may find nothing, so the type documents it.
  • Prefer getOrDefault over get for maps with numeric or string values.
  • Use nullability annotations such as @Nullable and @NonNull. Your IDE and static analysis will then warn at compile time rather than runtime.

One caution on the opposite extreme: wrapping everything in null checks makes code unreadable and hides genuine bugs. If a value should never be null, let it fail loudly rather than silently continuing with a substituted default. A crash at the point of the mistake is far cheaper than corrupted data discovered later.

Frequently Asked Questions

What causes a NullPointerException in Java? Using a reference that holds null as if it were an object — calling a method on it, reading a field, indexing it as an array, or unboxing a null wrapper into a primitive. Common sources are uninitialised fields, methods returning null and missing map keys.
How do I read the newer NullPointerException messages? From JDK 14 onwards the message names the exact expression that was null. The part after because identifies the null value, and the part after Cannot invoke shows what you attempted. On chained calls it pinpoints which link failed.
Does Optional eliminate NullPointerException? It makes absence explicit in the type so callers are prompted to handle it, but calling get() without checking simply throws a different exception. Use orElse, orElseThrow or map instead, and treat Optional as a return type rather than a field type.
Why do I get an NPE on a line with no method call? Almost certainly autoboxing. Assigning a null Integer to an int calls intValue() on null, so the NPE occurs on what looks like a plain assignment. Map lookups returning null Integers are the usual source.
Should I return null or an empty list from a method? An empty list, always. Callers can iterate it safely without a null check, which removes an entire category of bugs. The same applies to arrays, maps and sets.