1. The introduction

NullPointerException is probably the most common problem in Java development and the most common mistake Java programmers make. It may seem like a small mistake, but the impact is huge. Tony Hoare (the inventor cited by NULL) said in 2009 that Npes cost companies billions of dollars. Within six months of working here, I’ve stepped in several NPE pits. For example, I need to add a piece of code to the original logic, but the new code throws an NPE error without exception handling, which directly causes the following logic not to run, affecting the whole original logic, terrible. So you have to be careful to avoid the NPE pit.

This paper will start from the following two aspects:

  1. Possible conditions for NPE
  2. Avoid NPE advice

2. Possible situation of NPE

First we need to understand how NPE occurs.

String s;
String[] ss;
Copy the code

When declaring a reference variable without specifying what it points to, Java defaults it to null, an empty address that means “nothing points to.” If no value is subsequently assigned to the variable, NPE is thrown when the contents of the variable are used.

For example, to access a method or variable, [] to access an array slot:

System.out.println(s.length());
System.out.println(ss[0]);
Copy the code

Here are six possible scenarios outlined by THE Javadoc for NPE:

  1. Invoke instance methods on empty objects. NPE is not reported when a static or class method is called on an empty object, because static methods do not require an instance to call any method.

  2. When accessing or changing any variable or field on an empty object;

  3. Throws null when an exception is thrown;

  4. If the array is null, access the array length.

  5. When the array is null, access or change the slot of the array.

  6. Synchronize an empty object or use NULL within a synchronized block.

3. Avoid NPE advice

This section provides some advice on how to avoid NPE during development.

(1) Avoid calling equals() and equalsIgnoreCase() on unknown objects. Instead, call them on known string constants

Because equals() and equalsIgnoreCase() have symmetry, they can be flipped directly, which is easy to implement.

Object unknowObject = null;
if (unknowObject.equals("knowObject")) {
    System.out.println("If unknowObject is null, NPE will be thrown.");
}
if ("knowObject".equals(unknowObject)) {
    System.out.println("Avoid the NPE");
}
Copy the code

(2) Avoid toString() and use string.valueof () instead.

This is because string.valueof () is non-null and also calls toString(), so the result is the same.

Object unknowObject = null;
System.out.println(unknowObject.toString());
System.out.println(String.valueOf(unknowObject));
Copy the code

(3) Use null safe methods and libraries

Methods in open source libraries usually have non-null validation, such as isBlank() and isNumeric() in the StringUtils utility class in the Apache Common library, so you don’t have to worry about NPE when using them. When we use a third-party library, we must know whether it is null-safe, if not, we need to do our own non-null verification.

System.out.println(StringUtils.isBlank(null));
System.out.println(StringUtils.isNumeric(null));
Copy the code

(4) When a method returns a collection or array, avoid returning NULL. Instead, return an empty collection or array

When an empty collection or array is returned, it is guaranteed that no NPE will occur when calling methods such as size() and length(). And the Collections class provides convenient empty lists, sets, and maps, collections. EMPTY_LIST, collections. EMPTY_Set, and collections.empty_map.

public List fun(Customer customer){
   List result = Collections.EMPTY_LIST;
   return result;
}
Copy the code

5. Use @notnull and @nullable annotations

  • @NonNullIt can be marked above methods, fields, and parameters to indicate that corresponding values cannot be null
  • @NullableCan be annotated above methods, fields, and parameters to indicate that the corresponding value can be null

These two annotations have no effect on the program as it runs and are only prompted when the IDE, compiler, FindBugs check, and documentation are generated.

There are several @notnull and @nullable that I haven’t figured out yet, but I won’t go into the details. But even without testing, simply being a marker can serve as a document.

(6) Avoid unnecessary packing and unpacking

If the wrapper object is NULL, NPE is prone to occur during unpacking.

Integer integer = null;
int i = integer;
System.out.println(i);
Copy the code

(7) Define reasonable defaults

Provide reasonable defaults when defining member variables.

public class Main {
    private List<String> list = new ArrayList<>();
    private String s = "";
}
Copy the code

(8) Use empty object mode

An empty object is a special instance of design that provides default behavior for methods, such as EMPTY_List in Collections, whose size() we can still use and will return 0 instead of throwing an NPE.

Taking another example from Jackson, path() returns a MissingNode object when the child node does not exist, and continues to return MissingNode when the path() method of the MissingNode object is called. Such a chain call will not throw an NPE. When the result is returned, the user can determine if it has been found simply by checking for a MissingNode.

JsonNode child = root.path("a").path("b");
if (child.isMissingNode()) {
    / /...
}
Copy the code

(9) Optional

Optional is a new Java8 feature that can be a null container object. If the value isPresent but not null, the isPresent() method returns true, and the get() method returns the object. What it does is avoid null validation of our display.

Here’s a common null validation example:

/ / the most outer layer
public class Outer {
    Nested nested;
    Nested getNested(a) {
        returnnested; }}Copy the code
/ / the second floor
public class Nested {
    Inner inner;
    Inner getInner(a) {
        returninner; }}Copy the code
/ / the bottom
public class Inner {
    String foo;
    String getFoo(a) {
        returnfoo; }}Copy the code

Inner foo = Outer; Inner foo = Outer; foo = Outer;

Outer outer = new Outer();
if(outer ! =null) {
    if(outer.nested ! =null) {
        if(outer.nested.inner ! =null) { System.out.println(outer.nested.inner.foo); }}}Copy the code

Such nested judgment statements are common in null-value verification. And the use of Optional combined with Java8 features Lambda expression, stream processing, you can use the chain operation, more concise.

Optional.of(new Outer())
    .map(Outer::getNested)
    .map(Nested::getInner)
    .map(Inner::getFoo)
    .ifPresent(System.out::println);
Copy the code

Optional.of() returns an Optional

object and places the Outer object in the container. An empty object of type Optional

is returned, without affecting subsequent chain calls. If this looks familiar, it’s similar to the empty object pattern we talked about in point 8, which is also used in Optional’s implementation.

(10) Carefulness

Hey, hey, let’s make it ten.

Finally, I wish you all success in avoiding NullPointerException. If you have any other good suggestions, please leave a comment!

4. Reference

  1. Java Tips and Best practices to avoid NullPointerException in Java Applications
  2. How to avoid null pointer exception in Java8

If you like my article, you can scan the code and follow my public account: “Grass Niazi”.