Nobody likes exceptions. If you’re new to programming, or trying to disentangle someone else’s code, they can be the bane of your life. Java has many exceptions, bur probably the most frequently encountered is the NullPointerException.

A NullPointerException is a RuntimeError thrown by your program that indicates it has attempted to access an object for which there is no memory location assigned. It can be thrown at compile time, or during execution of the code. You can check whether the object is null before accessing it using conditional statements, assertions and try-catch blocks.

We’ll first explore why the NullPointerException exists at all, and then we’ll explore four ways to avoid throwing the NullPointerException.

Overview

Why have a NullPointerException at all?

I already know this – just tell me how to fix it!

You might be asking why the NullPointerException is a thing at all. Wouldn’t it be easier if the language insisted that everything had a value when it was created – obviously this wouldn’t happen!

Before we talk about null pointers, we just need to remind ourselves how Java allocates its runtime memory. On the right, below, is the stack. Every thread gets an area, and within each thread every method gets a ‘frame’

Heap and Stack Memory is allocated separately in Java

But, whenever you create an Object, a class – anything with parameters and methods – Java reasons that these objects usually persist outside of methods. The designers made an educated judgement call that Objects would be regularly handed between methods, and can persist for the lifetime of a program. It would be very performance-intensive to copy all that data from one frame to the next each time it was passed

Instead, the Java Virtual Machine (JVM) – the runtime that’s reading your compiled code – stores the Object on the heap, but keeps a memory reference in the stack frame.

An Object is created in the main frame, with a memory reference to the location of the Object on the heap. Accessing this object will not cause a NullPointerException.

The exception to this is Java’s primitives. That’s data types like int, long, boolean and double. When they’re created in a method, the JVM stores in the stack with the method call. Accessing primitives is faster this way, but they’re automatically deleted when the method call ends.

A primitive data type is assigned. A NullPointerException is not possible with primitives as they have a default value of 0

In terms of performance, this gives a boost when compared to Object-Oriented alternatives like Integer, Boolean and Double. The Object Oriented counterparts can be convenient, but they come with a performance overhead. Instead of being stored in the stack with the method, they’re assigned a reference to the heap.

The null reference

There is practically no limit on the complexity of an Object in Java. You can more or less pack in as many properties and methods as you want, and for that reason the designers of Java included a special expression null, which represents to the virtual machine that you’ve got an Object of a certain type, but you’re not going to assign any memory for it yet.

A null reference is defined for an Object. If it is accessed or modified, it will throw a What is a NullPointerException

Java could create a memory block for every reference, but some references never get used. Class parameters can remain uninitialized, because it you don’t need them, it’s more efficient not to allocate memory. As the developer, it’s your choice what to initialize and what to keep null.

The new keyword

We make the choice whether to assign a memory reference by using the new keyword. Below, ‘me’ is uninitialized – it will be stored on the stack with a memory reference that is null. In the second line, ‘you’ gets initialized as a new Person and memory in the heap gets assigned.

    Person me;
    Person you = new Person();

Just checking back in with those primitives and you’ll notice you don’t use the new keyword with primitives. That makes sense, because we’re not asking the virtual machine to give us any memory on the heap. We can either initialize it with a value, or we can leave it uninitialized. Uninitialized, primitives are assigned their default value, which is always 0.

    int myAge = 33;
    int yourAge; //this is initialized to it's default value, which is 0.

Hang on, pointers?

You might have noticed I haven’t used the word pointer yet. Well, that’s deliberate. Java deals in memory references, not pointers. The word ‘pointer’ is a hang-up from C++. In that case, ‘pointers’ to track an object’s memory location as it is passed from function to function.

However, Java does it’s own memory management: it moves heap memory around to optimize the runtime performance of the program. Java’s Garbage Collection algorithm removes unwanted memory references, which helps to keep the runtime size of the application optimized.

Because of the frequent changes, it doesn’t make sense to store a direct pointer to the memory location. Instead Java keeps references to the memory, which it can independently manage in the background.

And with that, null pointers – or references – aren’t going anywhere – but when are they thrown?

When is the NullPointerException thrown?

If you create any sort of complex object – a class, an array – you’re creating a reference object. It’s absolutely fine to create the reference without initializing it – often it’s efficient. But if you use a method, or try to access a field of a null object, you’re in for a bad time. From the documentation of the NullPointerException these are the way you can throw it:

  1. Calling the instance method of a null object.
  2. Accessing or modifying the field of a null object.
  3. Taking the length of null as if it were an array.
  4. Accessing or modifying the slots of null as if it were an array.
  5. Throwing null as if it were a Throwable

These can be more-or-less separated into “Calling, Accessing and Modifying” (1-4) and “Throwing” (5). Let’s take a look at each of these in turn.

Calling, Accessing And Modifying null

In the below code snippet, let’s call createEmployee() and follow it line-by-line.

createEmployee(){
    int age = 33;
    Person me;
    System.out.println(me.getAge());
}
  • Inside the method, for some reason all employees are 33 years old. Aside from a ‘quick chat with HR’, that’s fine from the point of view of Java – it just initializes the ‘int’ primitive and moves on.
age is initialised as 33. A NullPointerException is not possible with primitives
  • We define me, a Person, but we don’t initialize it. That’s fine too – maybe we’re just being efficient
Person is initialized to null. This could cause a NullPointerException if accessed, as we will see.
  • Finally, we try to call getAge() on our uninitialized Person, me. The JVM diligently heads off to the stack frame to get the memory address for the Person, only to find a null. It can’t complete the instruction we gave it – to execute getAge() – because there’s nothing there to operate on! Out of ideas, the virtual machine throws an error.
a NullPointerException is thrown by the program

So, if you’re writing the code that is generating the null reference, checking your references are initialized is an easy way to avoid throwing the exception.

Throwing null.

The correct syntax to throw an exception of any type is throw new Exception();. The keyword throw tells the virtual machine to stop executing the thread because something’s gone wrong. But, importantly, the new keyword tells us an Exception is a reference object – just like any other class. In fact, an Exception, a NullPointerException, or any custom Exception you create yourself is a class that extends Throwable, the base class that deals with determining the cause and printing the stack trace when it all goes wrong.

Just like any reference type, you can replace new Exception() with null. That’s OK if you’re defining an exception – maybe you’re keeping the reference for safe keeping – but if you try to throw null, you’re asking the virtual machine to access methods and properties – it’s expecting a Throwable – where they don’t exist. Moving on…

Avoiding the NullPointerException

There are four ways to avoid a NullPointerException at runtime. The first is just to catch it at compile time. Not much better, but at least you don’t have to debug a running program! The last three deal with avoiding generating the NullPointerException in the first place.

Scope (catching at compile time)

Compilers do a pretty good job of catching errors. Left out a semi-colon? Error. Missing a curly brace? Error. In this way, the compiler tries to identify flaws that will prevent a program from executing properly and stop it as early as possible. If it can catch a RuntimeException at compile time, great!

It’s exactly the same with the NullPointerException and it comes down to whether the compiler can reasonably predict that a value will be null when it is used.

As we covered earlier, if we define an Object within a method, it is stored on the stack. Once the method has completed, the JVM clears it from memory.

public class Main {
    public static void main(String[] args) {
        Double salaryDollars;    
        String mySalary = divideByOneThousand(salaryDollars) + "k";
    }
}

So if you’ve defined the reference within a method, the compiler can work out that any initialization of that Object must come from within that method. Not initialized before it’s accessed? The compiler will catch it.

Class parameters are different because there are multiple ways we can access and initialize them. It’s therefore not practical for the compiler to work out access-modifiers, getters and setters so it won’t be caught.

public class Main {
    static Double salaryDollars;
    public static void main(String[] args) {
        String mySalary =  divideByOneThousand(salaryDollars) + "k";
    }
}

In the above example, the compiler can’t identify whether the Double object is assigned even though it’s used in the access point (the main method) of the application. In this case, the program will compile correctly, but not run.

Avoid using null as a return value

Other people’s code

If you’re hooking into someone else’s code – like when you’re using a toolkit or a library – it can be impossible to avoid getting null as a return value. Some programmers use a null return to tell you something’s gone wrong with the execution of the method.

public List<String> libraryMethod(){
    if(executesCorrectly){
        return libraryList;
    } else {
        return null;
    }

I’d argue that’s a bad use of the null reference – but if it’s there, you’ll have to decide what the impact is and deal with it appropriately. Being aware of the contracts you’re participating in is an important part of using a library. So, if you’re calling a method and you think about defensively checking for nulls, check the method itself – is null a proper return value.

Your code

It’s a different story if you’re writing the method. In that case, consider returning something else depending on the intended use of the Object being returned.

ObjectPurposeAlternative
List<>List for the user to iterate throughReturn an empty list – no error, but no iteration
Date objectFor use in a calendarReturn a today’s date
Shape objectDisplaying a shape to the screenReturn a Shape with some default parameters

That way, the code calling the method doesn’t have to defensively check for null values, and will always have a useable return value to work with.

Catch NullPointerException with assertions

One of the issues with the compiler catching null references was it’s ability to scrape through the code and predict whether any object could be null. If you have a huge code base, it’s impractical to search through all the links at compile time, so it gives up.

If you’re using an IDE – and most people do these days – you can get around this to some extent by labelling your code to signal to the IDE whether an object can return null.

  • @Nullable: the Object return could be, but isn’t necessarily null
  • @NotNull: the Object returned will definitely not be null

The compiler itself will ignore these annotations, but when you click that Intellij Play Button button that executes your code, your IDE checks through your code in a way the compiler can’t.

In the below code, we might have decided that convertToThousands() doesn’t make sense for a number that’s less than a thousand, so in those cases we return null. We label the method as @Nullable, and the IDE will now warn us if we don’t consider null values when we call this method.

    @Nullable
    private Double convertToThousands(Double number) {
        if(number < 1000){
            return null;
        } else {
            return number / 1000;
        }
    }

Catching NullPointerException manually

An important part of designing a program is anticipating and handling errors and nulls gracefully so the program continues to behave as expected. Here’s two ways how.

1. try-catch blocks

When I was learning Java during my PhD, a professor said, “no one likes Java, because it accumulates errors“. After 8 years of coding in Java, I can safely say programmers accumulate errors!

It’s possible to ignore errors – usually with a //FIXME to remind us to handle the error later! For example, we use the default try-catch block and just print the stack trace in the catch. Your program keeps going – ignoring the impact of the Exception unless you specifically tell it to.

2. conditional statements

Most beginner programmers default to this option – and it’s not necessarily bad. As we discussed above, if you’re hooking into a library that returns nulls to report an error, this might be the only way to check your code is running normally.

As with try-catch blocks, I’d encourage you to think about the impact of the conditional statement not being met. The concise, and tempting code is to test for null in one line:

if(obj != null) doMore().

But what if the user was trying to add a new Employee to a database? Have we told them it didn’t work? If the result of a null response would cause additional problems, we can use the else statement to put some remedial actions in place.

if(newEmployee != null) {
    uploadEmployeeToDB(newEmployee);
} else {
    informUserOfError();
    returnToDataInputScreen();
}

3. Informative errors

The last way to catch nulls is not to. Sort of.

In this case, we receive a null response on trying to set up a Window – perhaps we’re rendering some graphics and using a library like GLFW. GLFW

if (myWindow = null)
    throw new RuntimeException("Unable to initialize GLFW");

Here, we want to throw an error – there’s no point going on with the program if we can’t open a window! But instead of allowing the program to continue and throw a NullPointerException later, we defensively check for initialization and throw a more informative error with a message to describe the cause.

Conclusions

NullPointerExceptions are a part of Java because of the way it stores Objects in memory. null references allow us to define an Object without committing to using up that memory at the same time. The responsibility, therefore, is with the developer to ensure they check their Objects before they use them.

Being aware of the contracts you’re partaking in is the most important way to avoid NullPointerExceptions. If the method you’re calling can’t return null, you don’t have to worry about checking.

If you can’t get away from nulls, the next best bet is to know about them. Assertions help your IDE recognize your method returns, so it can warn you when you might run into a null value and remind you to account for it.

Finally, you can manually check for null values by using conditional statements and try-catch blocks. In both situations, it’s also an opportunity to account for whether a null response indicates something deeper has done wrong in the code and set into motion some actions to remedy it.