Green Files

What is NullPointerException and what are its main causes?

Day: 06/02/2022 - Time: 12:13:15

-What are NullPointerException exceptions? -What are your main causes? -What methods/practices can be used to prevent it?

The NullPointerException is thrown when trying to use null as if it were an object. That is, it is when you try to manipulate the properties, fields, attributes or methods of an object, but without having that object.

In many other programming languages there is also the same concept. For example, in C# there is NullReferenceException. In JavaScript, trying to handle a null incorrectly produces a TypeError. Likewise, handling None incorrectly in Python produces an AttributeError.

The rest of this answer applies to Java as referenced in the question. But in other programming languages, what usually happens is very similar to what is detailed below.

In the specific case of Java, NullPointerException is thrown in the following situations:

1. Attempting to access a field from an instance whose reference is null.

Person p = null; String name = p.name; // <-- NullPointerException here.

2. Attempting to access a method of an instance whose reference is null.

Person p = null; String name = p.getName(); // <-- NullPointerException here.

3. Trying to use null autounboxing. This one in particular tends to be a prank for Java beginners.

Integer x = null; int z = x; // <-- NullPointerException here.

Another example:

Integer x = null; int z = x + 5; // <-- NullPointerException here.

4. Throw NullPointerException directly

throw new NullPointerException();

5. Attempting to throw null as an exception.

Exception x = null; throw x; // <-- NullPointerException here.

6. Trying to access the size of an array variable that has the value null.

String[] x = null; int size = x.length; // <-- NullPointerException here.

7. Trying to access an element of an array variable that has the value null.

String[] x = null; String y = x[5]; // <-- NullPointerException here.

8. Attempting to iterate over null using the for-each syntax.

String[] x = null; for (String y : x) { // <-- NullPointerException here. // ... }

9. Attempt to synchronize (with synchronized block) at null.

Object x = null; synchronized (x) { // <-- NullPointerException aqui. // ... }

10. Trying to get a method reference from null:

Person p = null; Supplier f = p::toString; // <-- NullPointerException aqui. // ... }

The following situations DO NOT cause NullPointerException, at least not directly:

1. Assign null to object type variables.

String x = null;

2. Assign null to variables of primitive types. This doesn't compile, so it won't throw NullPointerException :)

int x = null; // Compilation error!

3. Pass null as a parameter of methods or constructors.

System.out.println(null); // OK. String message = JOptionPane.showInputDialog(null); // OK. JPanel panel = new JPanel(null); // OK.

4. Return null or get null as a return from some method.

public String getXpto() { return null; } public void someMethod() { String xpto = getXpto(); // OK. Receives null. }

5. Put the null value in an array or read the null value from an array.

String[] array = new String[] {"a", "b", "c"}; array[1] = null; String value = array[1]; // OK. Receives null.

6.Passing null as a varargs parameter.

private void method(String... parameters) { System.out.println(parameters); } private void otherMethod() { // No NullPointerException. // Pass as a parameter an array with a single element that is null. method(null); // No NullPointerException. // Pass an array with two null elements as a parameter. method(null, null); // No NullPointerException. // Pass null as a parameter. method((String[]) null); }

7. Iterate over an array or Collection with null elements.

String[] x = new String[] {"a", "b", null, "c"}; for (String z : x) { System.out.println(z); }

However, despite this being valid and even useful at times, it is important to keep in mind that this situation is still very inviting to possible NullPointerExceptions, since it is normally not expected that the loop variable can be null:

String[] x = new String[] {"a", "b", null, "c"}; for (String z : x) { System.out.println(z.length()); // <-- NullPointerException here. }

8. Access a static field or method from a null reference. This is a Java language trick, because in this case the only thing that matters is the type of the variable, and not the value.

Integer t = null; t.parseInt("123"); // Gotcha: t is null, but it DOESN'T throw a NullPointerException! System s = null; Object x = s.out; // No NullPointerException!

It is also important to point out that using a variable to access a static method is bad programming practice, as it confuses the code by using a variable unnecessarily. Do not do it.

9. Attempting to use an uninitialized or potentially uninitialized local variable. This gives a compilation error, so it doesn't give NullPointerException.

person p; p.setName("Mary"); // Compilation error, variable p is not initialized. person q; if (x > y) q = new Person(); // May or may not be initialized, depending on the if condition. // Compilation error, variable q was possibly not initialized. q.setName("Mary");

It is worth mentioning that this rule only applies to local variables within methods and constructors. It does NOT apply to object fields and static variables.

10. Remember that the this reference will never be null. So a NullPointerException will never be caused due to trying to manipulate fields and invoke methods on the this object.

11. A constructor never returns null. Therefore, whenever a variable is assigned the result of a constructor call, it is guaranteed to be something that is not null. So, taking the code below as an example, it is impossible for the variable p to receive null, regardless of what is happening inside the constructor.

Person p = new Person();

12. The instanceof operator always returns false when tested with null, and never throws NullPointerException. So, if it returns true, the tested value is guaranteed not to be null.

Animal p = ...; if (p instanceof Dog) { // If you enter here, in addition to knowing that p is an instance of Dog, // we also know that p is not null. }

13. Casts never throw NullPointerExceptions (but can throw ClassCastExceptions). In particular, the cast performed with the null value will always be successful.

Animal a = null; // Get null. It does not throw NullPointerException or ClassCastException. Dog c = (Dog) a;

14 Some people think that accessing an array at an invalid index causes a NullPointerException. This is not true. The exception thrown in this case will be ArrayIndexOutOfBoundsException. Likewise, trying to access the characters of a String in invalid positions also does not cause a NullPointerException, in which case the exception will be StringIndexOutOfBoundsException.

15. Attempting to concatenate null to a String.

String x = "a" + null + "b"; // Get "anullb". It doesn't give NullPointerException.

Almost every case where a NullPointerException occurs is due to some programming error (and because of that, it almost never makes sense to try to handle it). So if you get a NullPointerException, chances are you (or someone else) did something wrong in the code. For this reason, to protect against NullPointerException, the main thing to do is examine your program's logic to make sure that you never fall into a case where a NullPointerException might be thrown, as shown in the situations above at the beginning of this answer (and with the counter-examples above as well). Your main weapon against this type of error is the if:

if (x == null) { // Do something. Don't use the x here. } else { // You can safely use x. }

It is also valid to put guards on methods (and constructors) to protect against null references coming from "outside", using if. This will not make the programming error involving null references go away, but it will make them manifest closer to their origin, so they are easier to identify and therefore easier to track and correct. Also, this way makes it simpler to ensure that your method is error-free, as this eliminates a whole category of possible programming errors that it could have and, as a bonus, you guarantee that it won't cause any weird side effects from running it. only halfway through before being aborted by NullPointerException. The idea is that if null is being used improperly (which is a programming error), then the programming error will not be your method's responsibility, but the one who called it improperly. Here's a simple example:

public void cadastrarNome(String nome) { if (nome == null) throw new NullPointerException("Não pode cadastrar um nome nulo."); // ... Resto do método. }

Or, you can use a different exception:

public void registerName(String name) { if (name == null) { throw new IllegalArgumentException("Cannot register a null name."); } // ... Rest of the method. }

This is also not limited to just parameter validation:

public double addParcels() { if (parcel1 == null || parcel2 == null) { throw new IllegalStateException("Not all parcels were acquired"); } // ... Rest of the method. }

Another way to implement these guards is using the Objects.requireNonNull() method. This method throws a NullPointerException if it receives null as a parameter:

public void registerName(String name) { Objects.requireNonNull(name, "You cannot register a null name."); // ... Rest of the method. } public double addParcels() { Objects.requireNonNull(parcel1, "The first parcel has not yet been acquired."); Objects.requireNonNull(parcel2, "The second parcel has not yet been acquired."); // ... Rest of the method. }

It is also valid to wrap your references with the java.util.Optional class:

String x = ...; Optional opt = Optional.ofNullable(x); // At this point, opt will never be null, so it can be // always used safely (although its contents may be null).

As well as protecting yourself from nulls coming in from the outside with the guards shown above, it's also important not to propagate nulls values ​​if you can avoid it. Because of this, it's also good to avoid returning nulls in methods when it's going to return something that represents "empty", "uninitialized", "does not apply", "does not exist", or "not found". In these cases, here's what you could do:

-If your method returns a String, it might be better to return "" (an empty String) instead of null.

-If your method returns a packaged primitive type (Integer, Double, Long, etc.), it might be better to return zero rather than null. And if it's a case of returning zero, changing the return type to the primitive type, if possible, would also be a good idea.

-If your method returns an array, it might be better to return a zero-sized array instead of null.

-If your method returns a Collection, it might be better to return Collections.emptyCollection(), Collections.emptyList() or Collections.emptySet() instead of null.

-If your method returns a Map, it might be better to return Collections.emptyMap() instead of null.

-If your method returns a Stream, it might be better to return Stream.empty() instead of null.

-If your method returns an Optional, then it's a bad idea to return null because that goes directly against the idea of ​​the Optional. In this case it would be better to return Optional.empty() instead of null.

-In case your method returns something XYZ for which there is not something that represents "empty", "uninitialized", "does not apply", "does not exist" or "not found", maybe change the return type to Optional is a good idea. Or you could use the Null Object design pattern (which I'll explain a little below).

In practice, we can do something like this:

public class MyBean { private String name; private List people; // Other methods... public String getName() { return name == null ? "" : Name; } public List getPeople() { return people == null ? Collections.emptyList() : people; } }

And by the way, going back to the parameters, we can do something similar with the guards. Instead of just rejecting nulls with exceptions we can replace them with empty objects:

public void registerName(String name) { String nameToRegister = name == null ? "Unnamed" : name; // ... Rest of the method. }

This approach has the advantage that, unlike the previous one, errors referring to the improper use of null tend to actually disappear rather than just move around. However, this approach is not always possible or adequate.

And now, let's take a closer look at the Null Object design pattern. The idea is that you represent the concepts of "empty", "does not exist", "not found", etc. with an instance of an object, rather than using null for this. Here is an example of this approach:

public class Person { private String name; private int age; // ... Methods. // Method that returns a Null Object. public static Person nobody() { Person does not have = new Person(); has.name = "Nobody"; no.age = 0; return does not have; } } public class Car { private String template; private String color; private int year; private Person owner; // ... Methods. // Method that returns a Null Object. public static Empty car() { Car emptycar = new Car(); emptycar.model = "none"; emptycar.color = "none"; emptycar.year = 0; empty car.owner = Person.nobody(); return emptycar; } }

If you can work with interfaces, it's better to implement the Null Object pattern:

public interface Animal { public String makeNoise(); } public class Dog implements Animal { @Override public String makeNoise() { return "Wow-wow"; } } public class Cat implements Animal { @Override public String makeNoise() { return "Meow"; } } public class AnimalNo implements Animal { @Override public String makeNoise() { return ""; } }

Again, it's worth noting that you can adopt one of these approaches to avoid null if possible. However, not always one of them is possible and it is common for some situations to appear where you really should return null or accept that some field, parameter or local variable can be null under normal circumstances, and you have to know how to live with it. Knowing how to live with nulls when they appear without causing a NullPointerException is part of what a Java programmer should do.

In the end, all the ways to avoid the NullPointerException lie in organizing your program's logic in order to avoid falling into any of the situations where it might appear. And in almost all situations where a NullPointerException appears, it should be treated as a programming error (as in fact it almost always is): understand why it occurs and correct the code so it doesn't occur again.

Finally, it is worth recommending SpotBugs, successor to the famous FindBugs. It is an excellent and powerful Java code analysis tool, capable of detecting a large number of logic errors, bugs, dangerous situations and bad programming practices. And all this obviously includes many situations that can result in a NullPointerException.

And if you are going to use SpotBugs (or some other code checking tools with similar characteristics), you might find an @NotNull, @NonNull or @Nonnull annotation, along with some @Null, @Nullable or @CheckForNull annotation. Several people from different projects created several versions of these annotations for similar purposes (which is a bad thing, as it would be much better if there was a single canonical @NotNull and a single canonical @Nullable). You can use these annotations on fields, variables, parameters and methods to tell tools capable of understanding them that a null in a certain place is explicitly prohibited or allowed. SpotBugs is able to understand such annotations, although it doesn't need them (but if they are present, it can do an even deeper analysis).

Another powerful tool is the Checker Framework, which uses an annotation-based approach (including @NonNull and @Nullable) and works as a pluggable annotation processor directly in the compiler. With it, it is even possible to transform some places where a NullPointerException could occur in compilation errors, avoiding the headache of having to track them in tests and/or debugging.

Reference

https://pt.stackoverflow.com/questions/63617/o-que-%c3%a9-a-nullpointerexception-e-quais-s%c3%a3o-suas-principais-causas

GO UP
GO TO INDEX