What is Java Null?
What is Java Null?
Java's use of null is somewhat infamous. Running into issues with the infamous NullPointerException is arguably a milestone for all Java developers. Understanding how Null operates will help you know when to use it and how to avoid issues. We'll get to that later in this article
When you want to indicate the lack of a value, you can always refer to the special constant null, which is a literal in the Java language. A typical misunderstanding that novices to the Java language must contend with is that it is neither an object nor a type.
In Java, a literal is a notation that represents a fixed value in the source code. In lexical analysis, literals of a given type are generally known as tokens, they are the constant values that appear directly in the program and can be assigned directly to a variable. Java has various types of literal. The following figure represents a literal.
Types of Literals in Java
Learn more about literals here.
It's crucial that our audience be familiar with Java variable types before we begin this session. Let's take a quick look.
In Java, variables are categorized into two types: primitive types and reference types. Primitive types store actual values, while reference types store references or addresses to the memory locations of objects. Examples of primitive types are int, boolean, char, short, float, long, and double. Reference variables don't store the actual object, but rather the memory location of where the object is stored. Importantly, primitive types always have a default value and can never be null. On the other hand, reference types can be null, indicating the absence of a value. This fundamental distinction separates primitive and reference types.
Let's delve into an example in Java for a clearer understanding:
In the above Java code, we have three variables:
- Object djObject: Reference Type
- int firstInt: Primitive Type
- int secondInt: Primitive Type
Here, the first two variables are class-level variables and they get default values. The third variable is a local variable, and it's initialized with the value 10. Because firstInt is a primitive type (int), its default value is 0. Since djObject is a reference type (Object), its default value is null. The output from the code snippet is:
Properties of Java Null
- Null is not a reserved keyword in Java. Instead, it is a literal, representing the absence of a value. It is not comparable to True or False which are booleans and are not keywords either.
- Null is case sensitive in Java, and it should always be written in lowercase.
Java programmers should always write null in lowercase. Writing it as Null or NULL will result in compile-time errors.
Null Used as Default
Null is the default value for reference types, just as there is a default value for basic types (e.g., 0 for integers, false for booleans). Uninitialized reference variables, such as instance variables and static variables, default to null. However, note that uninitialized local variables will give a compiler warning.
Let's understand better with the help of the following Java code example.
Output
Thus we can see, for regInt that was initialized with the value 50, and printed 50 in the output as well whereas for the other two uninitialized variables, we see that emptyInt of type Integer prints 0 as it's the default value and emptyObj of reference type prints null as it's the default value.
Using null with the "instanceOf" Operator
The instanceOf operator is used to determine if an object belongs to a particular class, subclass, or implements a particular interface. Importantly, if the instanceOf operator is used on a reference variable containing a null value or the null literal itself, it always returns false.
In the Java code snippet below, we can observe it in action.
Output
null is Used for Casting to Other Types
Null can be typecast to any reference type without causing a compile-time or runtime error.
Primitive variables, such as int, double, float, or boolean, cannot be set to null. If we attempt to accomplish this, the compiler will object.
NullPointer Exception (NPE)
A NullPointerException is a runtime exception in Java. It occurs when the Java Virtual Machine (JVM) tries to access an object or call a method on an object that's set to null. This exception often indicates a programming oversight or error.
Example:
Autoboxing and unboxing
When autoboxing and unboxing occur, especially with null values, Java developers need to be cautious.
Output
If a null value is assigned to a primitive boxed data type during auto-boxing and unpacking processes, the compiler simply throws a Nullpointer exception error
Learn more about Autoboxing and unboxing in Java here.
Static vs Non-static Methods
A static method belongs to the class, not to any specific instance of the class, so it can be invoked even if the calling reference is null. However, invoking a non-static method on a null reference will throw a NullPointerException.
Let's see how null values can work in Static and Non-static methods in Java.
Output:
As you can see in the provided output, NullPointerException is thrown since a non-static method is called on a reference variable with a null value; however, static methods can be used on reference variables with null values which is why here, the Static method didn't cause a Null Pointer Exception since they are bound via static binding.
Learn more about static methods in Java here.
‘+’ Operator on Null
The null value can be concatenated with String objects in Java, resulting in the string "null" being concatenated.
Output
== and !=
The equality (==) and inequality (!=) operators can be used with null in Java, allowing for null checks on objects.
Output:
What Does the Null Statement Do?
Null are used as a special value to signify, in general:
- Uninitialized state
- Termination condition
- Non-existing object
- An unknown value
Null was created to provide a way to point to the absence of something. When you declare a variable in Java, you must assign the type it expects to store in that variable. You might think of variables as containers that store values of one of the two major categories of types:
Primitives are predefined data types provided by the programming language. When you declare a variable as a primitive type (e.g., int, float) your variable directly contains the underlying value.
References are pointers that point to the values being represented. When you declare a variable as a reference type you are storing the address that points to the value rather than the value itself. Classes, arrays, and strings are examples of reference types.
Primitive types cannot have null values, but null can be assigned to any reference type. Here’s an example:
Null objects can also be cast to any type at both compile time and runtime
Null Operations
Operations involving null are extremely fast and easy to perform at run-time. There are only two kinds of operations;
Initialize or set a reference to null (e.g name = null): The only thing to do is to change the content of one memory cell (e.g. setting it to 0).
Check if a reference points to null (e.g. if name == null): The only thing to do is to check if the memory cell of the reference holds the value 0.
NullPointerException (NPE)
When an object has a null value, Java throws the java.lang.NullPointerException. This infamous pointer exception is frequently encountered by Java programmers who fail to initialize a variable (because null is the default value for uninitialized reference variables).
The following situations are typical ones where a programmer might run into a NullPointerException:
Null objects can be used for a variety of purposes, including:
- Calling an uninitialized variable
- Accessing or modifying a data field or member with a null object
- Passing a null object as an argument to a method
- Invoking a method with a null object
- Synchronizing a null object
- Throwing a null object
- Treating a null object as a Java array
Now the question arises,
How do we avoid NullPointerException?
Well, for starters we can't return null but worry not that's not the only solution we have to offer here
You can use a various strategies to handle a NullPointerException in addition to the obvious (though not always simple) requirement of making sure that all variables are initialized correctly and that all object references lead to valid values. Let's look at some ways we can avoid NullPointerException.
How to Avoid the NullPointerException
Make sure that all of your objects are initialized correctly before you use them to prevent the NullPointerException. You should be aware that when you declare a reference variable, you are actually making a pointer to an object. Before asking the object for a method or field, you must make sure the reference is not null.
Use the data found in the exception's stack trace as well, if one is thrown. The JVM provides the execution stack trace in order to support application debugging. Find the method and line where the exception was caught, and then determine which reference in that particular line is equal to null.
We will discuss some methods that address the aforementioned exception in the remaining portions of this section. They do not, however, solve the issue, and a programmer should always use caution when creating applications.
- String comparison with literals In the execution code of an application, comparing a String variable to a literal is a relatively frequent scenario. The literal could be a String or an Enum element. Consider calling the method from the literal rather than the null object when calling it. Think about the following scenario, for instance:
A NullPointerException will be thrown by the code line above. However, the execution sequence resumes correctly if we call the method from the literal:
- Check the arguments of a method Make sure to check the arguments of your own method for null values before running the method's body. Only once the arguments have been properly checked should you continue to execute the method. In any other case, you can throw an IllegalArgumentException and tell the calling method that the arguments it received are invalid.
For example:
- Prefer String.valueOf() method instead of toString() Avoid utilizing the function toString() method of an object when your application's code needs the object's String representation. A NullPointerException will be thrown if the reference to your object is null.
Instead, think about utilizing the static String.valueOf method, which prints "null" if the function's input is null and does not raise any exceptions.
- Use the Ternary Operator The ternary operator can be very useful and can help us avoid the NullPointerException. The operator has the form:
boolean expression? value1 : value2; The boolean expression is first assessed. The value1 is returned if the expression is true; else, the value2 is returned. The following is how the ternary operator can be used to handle null pointers: String message = (str == null) ? "" : str.substring(0, 10); If the reference for str is null, the message variable will be empty. Otherwise, the message will only get the first 10 characters of the data if str points to actual data.
- Create methods that return empty collections instead of null Making methods that return an empty collection rather of a null value is an excellent technique. The code in your application can access the empty collection's methods and fields without raising a NullPointerException. For instance:
- Make use of Apache’s StringUtils class A package called Apache's Commons Lang offers helper tools for the java.lang API, like methods for manipulating strings. StringUtils.java is an example class that offers string manipulation and handles null input Strings discretely.
To avoid the NullPointerException, utilize the StringUtils.isNotEmpty, StringUtils.IsEmpty, and StringUtils.equals methods. For example:
- Use the contains(), containsKey(), containsValue() methods Use the contains, containsKey, and containsValue methods if your application code uses collections like Maps. Retrieve the value of a certain key, for instance, after confirming that it exists in the map:
The returned value in the excerpt above could be null because we don't check to see if the key actually exists inside the Map. The following is the safest method:
-
Check the return value of external methods Utilizing external libraries is a practice that is fairly widespread. These libraries have methods with reference returns. Verify that the reference is not null when it is returned. Consider reading the method's Javadoc as well to gain a better understanding of its functionality and return values.
-
Use Assertions When testing your code, assertions are highly helpful and can be used to prevent executing code fragments that will cause a NullPointerException. The assert keyword is used to implement Java assertions, and it raises an AssertionError.
Keep in mind that while running the JVM, you must explicitly enable the assertion flag by passing the -ea parameter. Otherwise, the claims will be totally disregarded.
The following is a sample Java assertions example:
If you run the aforementioned line of code and provide getLength as a null parameter, the following error message will show up:
The jUnit testing framework also includes the Assert class, which you may utilize as a last option.
- Unit Tests When evaluating the functionality and validity of your code, unit tests can be quite helpful. Spend some time creating a few test cases that confirm that no NullPointerException is thrown while the code for your application follows a particular execution path.
Existing NullPointerException Safe Methods
- Accessing static members or methods of a class The JVM does not raise a NullPointerException when your code tries to access a static variable or method of a class, even if the object's reference is null. This is because, during the compilation process, the Java compiler stores the static methods and fields in a specific location. As a result, the static fields and methods aren't linked to objects; rather, they're linked to the class name.
For instance, the code shown below does not cause a NullPointerException to occur:
You'll see that the method will still work even though the instance of the SampleClass equals null. However, it is preferable to access static methods or fields in a static manner, such as SampleClass.printMessage().
- The instanceof operator Even if the object's reference is equal to null, the instanceof operator can still be utilized. When the reference is null, the instanceof operator returns false and does not raise a NullPointerException. Consider the following line of code as an example:
The result of the execution is, as expected:
Examples of Java Null
Let's see an example of Null in Java
Three situations are present in the code sample above: one involves the object obj and int numOne, both of which are not initialized, and another involves int num, which is initialized to 10. obj has null stored in it and numOne has 0 stored in it since the Object is a reference type and the int is a primitive type
Let's take a look at the output of Main.java
When to Use Java Null and When Not to Use?
The fundamental guideline is straightforward: an object reference should only be permitted to have "no value associated with it" when it makes sense.
Note: An object reference can be an input/output parameter, variable, constant, property (class field), etc.
For example, suppose type person with fields name and dateOfFirstMarriage:
Every individual has a name. As a result, it is illogical for a field name to have "no value associated with it." The field name cannot be null. It is forbidden to provide it with a null value.
However, the value for the field dateOfFirstMarriage is not necessary. Not all people are married. The fact that dateOfFirstMarriage has "no value linked with it" makes sense. As a result, the field dateOfFirstMarriage is nullable. When a person's dateOfFirstMarriage field contains the value null, it merely indicates that they have never been wed.
The majority of widely used programming languages, regrettably, do not distinguish between nullable and non-nullable types. Null can never be assigned to a specific object reference, however, this cannot be said with any degree of confidence. Annotations can be used in some languages, such as Java's non-standard annotations @Nullable and @NonNullable
Here's an illustration:
The compiler does not, however, employ these annotations to guarantee null-safety. Nevertheless, they can be exploited by IDEs and tools like static code analyzers and are helpful for the human reader.
It's vital to remember that incorrect circumstances shouldn't be indicated by the use of null.
Think about a program that receives configuration information from a file. An empty file or an empty file should result in a default configuration being returned. The function's signature is as follows:
What should happen in case of a file read error?
Simply return null?
NO!
Every language includes a set of rules for how to report errors and provide information about them, such as a description, kind, stack trace, and so on. There are exception mechanisms in many languages (such as C#, Java, etc.), and in these languages, exceptions should be used to indicate run-time issues. In order to indicate an error, readConfigFromFile shouldn't return null. Instead, the function's signature needs to be modified to explicitly state that the function may fail:
Remember: Allow null only if it makes sense for an object reference to have 'no value associated with it'.
Don’t use null to signal error conditions.
Some More Tips to Handle Null
Use Exceptions Instead of Nulls
One peculiar circumstance in which null usage may be observed is in extraordinary circumstances. This method is inherently error-prone since crucial faults may be missed or reappear in many system components, making troubleshooting difficult. Therefore, if something goes wrong, always throw an exception rather than return null.
Test Your Code
This suggestion, however, applies to all issues, not only those that result in unexpected nulls. A great strategy to avoid NPEs is to extensively test your code in a setting similar to production. Never publish code before making sure it functions. "A quick, straightforward remedy that doesn't require testing" does not exist.
Validate Public API Arguments
The rule of avoiding passing nulls can be applied with some degree of success, but if you provide a public API, you have no control over the input that users pass to your services. Check the validity of the arguments sent to your public APIs at all times for this reason. Consider utilizing the requireNonNull function from the Objects class if the argument's nullness is your primary concern;
Use Objects Methods as Stream Predicts
Objects.isNull and Objects.nonNull are ideal for use with streams even if they are not the greatest options for standard null checks. They read (arguably) far better than operators in filtering or matching lines. They were included in the JDK for just this purpose.
Optional is Not for Fields
Optional was created to represent omitted return values. Class fields are one alluring scenario for which it was not intended and is most definitely not required. You should have complete control over the field's value, including null, through encapsulation. Making fields clearly Optional, however, could result in odd issues, such as:
How should you write a constructor or setter for such a field?
Even when you are certain that the value is present, you still have to deal with the Optional.
How should automapper handle those fields?
Use direct references for fields instead, and consider carefully if a field can be null at any time. If your class is properly isolated, this ought to be a simple task.
Learn More
We've covered a lot of terms in this article, you can know more about them here,
Conclusion
- It is usually assumed that there is no value associated with a reference if it points to null.
- Null often has a more precise meaning that changes depending on the situation.
- Additional information is required to distinguish between the potential scenarios if we need to know why a reference has no value.
- If it makes sense for an object reference to have "no value associated with it," only allow null.
- Null should not be used to indicate incorrect circumstances.
- Null is a concept that only applies to reference types. The same is true for value types.
- The default value for reference types in some programming languages is null.
- null operations are incredibly quick and inexpensive.