Reference types in Java are all the types that are not one of the primitive types.
Primitive types are the most basic data types in Java – the only purpose of these types are to store simple values of one kind.
The 8 primitive data types in Java are:

  • short
  • int
  • long
  • float
  • double
  • booolean
  • byte
  • char

All other types in Java, apart from these 8, are known as reference types, like String, Integer and List.
But what is a reference type in Java? What does it mean?

A reference variable is a variable that contain the memory address of an object. The variable references, or points to, the memory address.
Reference types in Java are often called objects.

Let’s get into the basics of reference types in Java with some examples. As we know, objects are initialized with the new keyword.
Assuming we have a class name User, we can create a new object of type User like this:

User s = new User("John");

What actually happens under the hood here is as follows:

  • User s: The first part is the declaration of the reference variable. This declares a variable that can refer to an object of type User.
  • new User(“John”): This creates the actual object. The new keyword will instantiate a User class (object), and dynamically allocate memory for that object. The User constructor can do anything it likes with the parameter “John”.  Finally, it will return a reference to that memory location.

Reference type variable pointing to a memory location

But, what happens if we don’t initialize the variable?

User s;

The reference variable is now only declared, but not initialized. This means that the variable s currently doesn’t reference anything! In Java, this is referred to as being null.
Note: null is not a valid object, and there is no memory allocated for null!

Reference types in Java

As you can see, the reference variable is currently not pointing to anything. It is null.

Operations on reference types

Let’s start off with operations that are not allowed directly on reference types in Java; arithmetic operations.

Moving back to our previous example, where we made User s store a reference to memory location 1024, which contained the phrase “John”.
Can’t we just make the reference variable s point to 1025, instead of 1024?

User s = new User("John");
s++;

This clearly indicates that we want our s variable to point the memory location 1025. However, it doesn’t work, your compiler will throw an error.
There are two reasons to why this doesn’t work:

  • If we change the reference, we have no way of knowing if we actually point to a valid User object.
  • Security reasons. If left unhandled, one can fall victim of a buffer overflow attack (I’ll write an article on this later on!)

Note: In general, arithmetic operations are not allowed directly on reference types in Java, with Strings being the exception.

However, there are no reason to worry about this. The Java programming language does all the work for us!

Now that we’ve had a quick look at operations that are not allowed on reference types, let’s look at what actually is allowed!

Starting off with the most basic operation available for reference types; assignment, done with =. You have already seen this in work, when we assigned new User(“John”) to the declared User named s.

Equality comparison is also allowed on reference types, done with == and !=.

You’re probably already familiar with equality comparison in Java, using primitive data types. The catch is that equality comparison works quite different with reference types.

When comparing reference types in Java using == and !=, you are comparing the memory address the variables are pointing at.

Consider the following declaration of two reference variables;

String s = new String("Hello");
String s2 = new String("Hello");

These two variables are not equal. The following picture explains why.

Reference types pointing to memory locations

If we introduce a third variable, we can make it point to the same memory location as the variable s. Then the reference types will be equal.

String s = new String("Hello");
String s2 = new String("Hello");
String s3 = s;

A comparison using == for and s3 will now report back as being equal.

Two reference types can point to the same memory location in Java

This pretty much sums up the operations that are allowed directly on the reference types in Java.

The other operations which are supported out-of-the-box deals with the object itself, and I’ll walk you through the 2 most basic operations;

  • Accessing internal fields and methods.
  • Using the instanceof operator to check if the object is of a current type.

Accessing fields and methods with the dot(.) operator

The dot operator (.) is used to access internal fields and methods within an object. Let’s assume our User class from the previous examples look like this:

public class User {
    public String name;
    
    public String getName() {
        return name;
    }
}

Using the dot operator we can access both the name variable, and the method getName().
Please note that this is not how you would write a User class in a real application. You would want a constructor / Builder Pattern, private fields and so on.

Now, let’s declare a User object, and access the internal fields and methods!

User u = new User();
u.name = "John";
System.out.println( u.getName() );

The u.name = “John” tells the compiler that we want to update the internal field on the User object u to be “John”. The next command performed with the dot operator is u.getName().
This states that we want to take the User object which u currently is referring to, and call it’s getName() method.

The instanceof operator

The instanceof operator is a test that is performed at runtime. It will simply test if a reference variable currently points to an object that is an instance of some class.

Let’s assume we want to verify in our code that a variable named superuser currently is an object of type User.

User superuser = new User;
if( superuser instanceof User )
    System.out.println("The variable is a user");

The above code snippet will print out that the variable is a user. Said in other words, superuser is an instance of the class User.
If we were to compare superuser to another class, like String, it would return false.

And this is where our introduction to reference types in Java ends.
You now know what a reference data type is, how it is stored in memory, and how you can access fields in the object that the variable points to.