What is NullPointerException ? What causes them and how to avoid them? Java

In this sneppet you will learn what is NullPointerException?, what causes them ? and how to avoid it ?

What is NullPointerException ?

A NullPointerException (NPE) is a runtime exception in Java that occurs whenever an application tries to use an object reference that has a null value. In other words, let’s say the object has not been initialized yet or the value has been set to null, and the code is trying to access or manipulate it assuming that it is a valid object.

What causes NullPointerException ?

Here are some common causes of NullPointerExceptions:

1. Variable is not initialized:

In Java when variable is declared but not initialized, it will have random value or zero value or null value depending on the type of variable.

For example,

int x; // primitive variable, contains a random value or zero value
String name; // reference variable, contains a null value

Using variable that is not initialized can lead to unexpected behavior or errors in your program.

For example:

If you are trying to use a primitive variable without initializing it, you may get a random value or a zero value.
If you are trying to use a reference variable without initializing it, you may get a NullPointerException.

How to avoid NullPointerException when variable is not initialized ?

To avoid using variables that are not initialized, you should always initialize them with a value before using them. Here are the ways to initialize variables:

  • Assign a value to the variable while declaring.
  • Use a constructor to initialize the variable.
  • Use a method to initialize the variable.
int x = 10; // initialize primitive variable
String name = "John"; // initialize reference variable

2. When method returns null

When a method returns null it would return null value, and when the caller tries to use the returned value without checking for null it can lead to a NullPointerException (NPE) being thrown.

For example, a method that returns null.

public String getUserName() {
    // some logic here
    return null;
}

And here is an example of caller, on who tries to call without checking for null.

String userName = getUserName();
System.out.println(userName.length()); // throws NullPointerException

In the above example, the caller tries to call the length() method on the returned value, which is null. This leads to a NullPointerException being thrown.

How to avoid null return values ?

Make sure before using the returned value from a method, check if it is null.

For example,

String userName = getUserName();
if (userName != null) {
    System.out.println(userName.length());
} else {
    System.out.println("User name is null");
}

Use the Optional class to wrap the returned value and handle null values in a more elegant way.

public Optional<String> getUserName() {
    // some logic here
    return Optional.ofNullable(null);
}

Optional<String> userName = getUserName();
userName.ifPresent(name -> System.out.println(name.length()));

Otherwise, instead of returning null from a method, you can throw an exception to indicate that the method was unable to return a valid value other than null.

public String getUserName() {
    // some logic here
    if (/* some condition */) {
        throw new RuntimeException("Unable to retrieve user name");
    }
    return userName;
}

You can also return a default value instead of null as shown below to avoid NullPointerException.

public String getUserName() {
    // some logic here
    return "SOME_DEFAULT";
}

3. When method is called with null argument

When a method is called with null argument and when it tries to use the argument without checking for NULL then it can lead to a NullPointerException.

For Example,

public void printName(String name) {
    System.out.println(name.length()); // throws NullPointerException
}

printName(null);

In the above example when the method tries to call the length() method on the null argument it can lead to a NullPointerException.

How to avoid Null method arguments ?

To avoid null method arguments, you can check for null before using the argument.

For example,

public void printName(String name) {
    if (name != null) {
        System.out.println(name.length());
    } else {
        System.out.println("Name is null");
    }
}

Use the Optional class to wrap the argument to handle null values.

For example,

public void printName(Optional<String> name) {
    name.ifPresent(n -> System.out.println(n.length()));
}

printName(Optional.ofNullable(null));

Throw an exception instead of using the null argument.

For example,

public void printName(String name) {
    if (name == null) {
        throw new NullPointerException("Name cannot be null");
    }
    System.out.println(name.length());
}

Use a default value instead of null, which can be used by the method.

public void printName(String name) {
    String actualName = name != null ? name : "Unknown";
    System.out.println(actualName.length());
}

Use annotations like @NonNull . This indicates that a method parameter cannot be null.

public void printName(@NonNull String name) {
    System.out.println(name.length());
}

4. While using autoboxing feature of Java

Autoboxing is a feature in Java used to convert primitive types to their corresponding object wrapper classes automatically. However, when a primitive type is automatically converted to an object, and the object is null, it can lead to a NullPointerException.

For example,

Integer count = null;
int primitiveCount = count; // throws NullPointerException

In the above example, the code attempts to assign a null Integer object to a primitive int variable, which can result in a NullPointerException.

How to avoid NPE while using autoboxing feature ?

Check for null before you use the object.

Integer count = null;
if (count != null) {
    int primitiveCount = count;
    // use primitiveCount
} else {
    // handle null case
}

Use the Optional class to wrap the object to handle null values.

Optional<Integer> count = Optional.ofNullable(null);
if (count.isPresent()) {
    int primitiveCount = count.get();
    // use primitiveCount
} else {
    // handle null case
}

Use primitive types instead of using object wrapper classes.

int count = 0; // use primitive int instead of Integer object

Avoid using null objects whenever possible, and instead use default values or throw exceptions to indicate invalid states.

5. When you delay the initialization of the object

Basically, to improve the performance, reduce memory usage etc., you can delay the initialization of the object until it is needed and this is called Lazy Initialization. When the initialization fails and not completed it may lead to cause unexpected behaviors.

For example,

public class LazyInitializedObject {
    private static LazyInitializedObject instance;

    private LazyInitializedObject() {
        // initialization code here
    }

    public static LazyInitializedObject getInstance() {
        if (instance == null) {
            instance = new LazyInitializedObject();
        }
        return instance;
    }
}

In the above example, the LazyInitializedObject class uses a lazy initialization approach to delay the creation of its object until it is actually needed. However, if the initialization code in the constructor fails or is not completed, the object will not be created, and subsequent calls to getInstance() will return null which will lead to NullPointerException.

How to avoid Lazy Initialization issues ?

Use a synchronized block to make sure that the initialization code is executed only once, and that subsequent calls to getInstance() return the same instance.

public class LazyInitializedObject {
    private static LazyInitializedObject instance;

    private LazyInitializedObject() {
        // initialization code here
    }

    public static synchronized LazyInitializedObject getInstance() {
        if (instance == null) {
            instance = new LazyInitializedObject();
        }
        return instance;
    }
}

Use a double-checked locking mechanism to make sure that the initialization code is executed only once, and that subsequent calls to getInstance() return the same instance.

public class LazyInitializedObject {
    private static volatile LazyInitializedObject instance;

    private LazyInitializedObject() {
        // initialization code here
    }

    public static LazyInitializedObject getInstance() {
        if (instance == null) {
            synchronized (LazyInitializedObject.class) {
                if (instance == null) {
                    instance = new LazyInitializedObject();
                }
            }
        }
        return instance;
    }
}

Use a thread-safe initialization mechanism, such as the AtomicReference class, to make sure that the initialization code is executed only once, and that subsequent calls to getInstance() return the same instance.

public class LazyInitializedObject {
    private static final AtomicReference<LazyInitializedObject> instance = new AtomicReference<>();

    private LazyInitializedObject() {
        // initialization code here
    }

    public static LazyInitializedObject getInstance() {
        LazyInitializedObject obj = instance.get();
        if (obj == null) {
            obj = new LazyInitializedObject();
            instance.set(obj);
        }
        return obj;
    }
}

Conclusion:

By following the above strategies listed for each scenarios that can cause NullPointerException you can make your code more robust and reliable.

Hope you find this sneppet helpful 🙂

References

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments