In the previous section, we showed an example of an
Airplane class in Java and how we can define the class name, its fields (state) and methods (behavior).
However, no objects from that class were created in that example.
There are different ways to create an instance of a class, the most standard one being the use of the
new keyword in Java. This keyword asks Java to reserve memory for a new instance of a particular class and creates the requested object.
In its simplest form, for our
Airplane example, to create a new instance of that class, all we need to do is invoke
Airplane plane1 = new Airplane();
In this example, we can see three main actions taking place:
The creation and initialization of a new
The declaration of a new variable called
The assignment of the new object to the
plane1reference that was defined (the
=that connects the 2 parts above).
Visually, if we assume the application memory was empty before executing the line above, we can think of our memory as an empty area that Java will start filling as we create objects, and at a high level we can see it as follows:
Objects vs References
It is important to make a distinction between a
reference to an object and an
In our example above,
plane1 is a reference to an instance of class
plane1 isn’t an
Airplane object as such.
object reference points to an
object of the declared type. As seen in the figure above,
plane1 is pointing to the 'Airplane Object 1' that was created by the
new Airplane() statement.
So, in this case, we have one
Airplane object and one reference of type
Let’s make a few simple modifications to our example code to help clarify the difference with the examples below.
Airplane plane1 = new Airplane(); Airplane plane2;
In this case, there is still only one
Airplane object being created (via the
new Airplane() statement), but two object references,
plane2. In this example,
plane2 isn’t pointing to anything, and we could represent it like this:
Airplane plane1 = new Airplane(); Airplane plane2; plane2 = plane1; System.out.println("plane1 is pointing to the same instance as plane2: " + (plane1 == plane2));
plane1 is pointing to the same instance as plane2: true
Similar to the previous example, one object and two references. However, in this case, we are changing the
plane2 reference to point to the same object that
plane1 is pointing to.
If we represented this visually, we could think about it like this:
Airplane plane1 = new Airplane(); Airplane plane2 = plane1; Airplane plane3 = new Airplane(); System.out.println("plane1 is pointing to the same instance as plane2: " + (plane1 == plane2)); System.out.println("plane1 is pointing to the same instance as plane3: " + (plane1 == plane3));
plane1 is pointing to the same instance as plane2: true plane1 is pointing to the same instance as plane3: false
Here, we now have two calls to
new Airplane() which will result in two different objects being allocated in memory. However, we have 3 references of type
In the code, we are making
plane2 point to the same object as
plane3 points to the second object that was created.
|This differentiation between objects and references is important to get right and it will become even more relevant later when we talk about memory management and garbage collection, and also when we introduce the concept of a null reference.|
Now that we understand that an object and an object reference are different, it is important to note that object references can have a special value called
This value represents that the object reference isn’t pointing to an object.
null as a value has no memory representation (there is no actual object).
Airplane plane = null; System.out.println("The plane is: " + plane); plane.land(); System.out.println("End of program");
The plane is: null Exception in thread "main" java.lang.NullPointerException at NullExampleApplication.main(NullExampleApplication.java:15)
Here, we are telling Java that our
plane reference will be
null, this means that no
Airplane object is associated with it.
Invoking methods or accessing fields on
When we then try to invoke a method on that reference,
plane.land() in our example, Java will throw an exception, indicating that it cannot operate over a
null reference (there is no object that Java can use in this case). The same will occur if we try to access a field when having a
null object reference.
We’ll cover the basics of exceptions in Chapter 3, however, they are events that disrupt the normal execution of our application. In our case, the exception that is thrown by Java is called a NullPointerException, indicating that we tried to perform an operation on a
In our output, this exception is what causes the application to print out: "Exception in thread "main" java.lang.NullPointerException…"
You can see that the last line of the code
System.out.println("End of program"); wasn’t executed due to the exception (it disrupted the normal application flow and in our case exited the main method at the point of the exception).
|Null references and NullPointerExceptions (a.k.a NPEs) or their equivalent in other languages are called the "billion-dollar mistake" as they are the cause of several application errors/crashes and vulnerabilities. See https://en.wikipedia.org/wiki/Tony_Hoare for more details.|
|We’ll cover exceptions in more detail later, how to handle them and what options we have at hand when they occur.|