1.2. Reference Type Examples

In Java, the reference types (§4.3) are class types, interface types, and array types. Of these three, you are likely familiar with declaring variables of both class and array types. Below are two example declarations of reference type variables:

Person ada;      // A class type variable (valid only if we have a class called Person on the classpath)
double[] array;  // An array type variable

An object is really just a collection of variables that are defined by a class. It is not uncommon to describe Java objects as dynamically constructed instances of a class. When an object is constructed, its collection of variables is stored contiguously in some location in memory, which we usually call the object’s reference. This is important because, in Java, the possible values of a reference type are references to compatible objects (or null).

Note

A reference variable either stores:

  • null

  • The location of an object in memory (a valid reference)

Below we describe various scenarios in which you may encounter/use reference variables in your code.

Example 1: Refer to No Object

Consider the following declaration and initialization:

Scanner s = null;

The variable s has Scanner as its type and null as its value. In Java, null is a reference that can always be assigned to any reference type, and it is used to denote that no object is being referred to. Therefore, we might say that variable s does not currently refer to any object.

Under the Hood: Remember, s is an alias for some location in the computer’s primary memory (RAM). Since we don’t know exactly where s will be located when the program runs, we will use an arbitrary number to denote this location. Here, we will use the value 800 (we could have chosen any arbitrary value). The mapping of s to a memory location all happens under the hood and requires no explicit actions by the programmer. The way this looks internally can be seen below. In the diagram, we use 800 instead of s since that’s what really happens in the computer. Since we assigned null to s, the value at location 800 is null.

800: [ null ]

Since using the variable name is more readable and does not require us to choose an arbitrary address, it is more common to diagram this using the variable name:

  +------+
s | null |
  +------+
Example 2: Refer to Some Object

Now consider the following declaration/initialization:

1Scanner s;
2s = new Scanner(System.in);

Despite its length, there is a lot happening in that code:

  1. the variable s is declared with Scanner as its type (line 1);

  2. a Scanner object is constructed (starting with new on line 2); and

  3. the object’s reference is assigned to s (using = on line 2).

Since the value of s is now a reference to some Scanner object, we might say that s refers to some Scanner object.

Under the Hood: Let 800 again denote the memory location that stores s‘s value and 12048 denote the memory location of the constructed Scanner object. Again, these values are arbitrary. The diagram below demonstrates the values that are stored at these two memory locations. Pay close attention to fact that 800 (or s) does not store the Scanner object’s contents. Instead, s is the address where the Scanner object resides in memory. Understanding this idea is the key to understanding how reference variables work in Java.

800: [ 12048 ]
           ...
12048: [ scanner object contents ... ]

It is more common to diagram this using the variable name and an arrow to the object (since we may not know or care what the actual value of the memory address of s):

  +--+     +----------------+
s | -|----◆|                |
  +--+     |                |
           +----------------+

Important: When a method is called using s (for example, s.nextLine()), the object that s refers to is known as the calling object. Non-static methods called using s are said to operate on the calling object. Since the reference in the value of s can change over time, the calling object can also change.

Test Yourself (Example 3)

Consider the following initializations:

1Scanner s = new Scanner(System.in);
2Scanner t = s;

Before moving on, take a moment to draw a diagram on a sheet of paper that shows how this looks under the hood (sometimes called a memory map or a memory diagram). Try drawing both versions of the diagram. You can come up with any arbitrary memory addresses that you wish. This example is a bit tricky but critical to understanding reference variables.

Do the best you can before looking at the solution!

Test Yourself Solution (Don’t open until completing the question above)

Your drawing should look very similar to the previous example with an extra variable for t that contains the same value as the variable s. We provide the more common version of this diagram below.

The first line is the same as in Example 2. On the second line: i) the variable t is declared with Scanner as its type; ii) the value of s is retrieved by the computer; then iii) the value is assigned to t. Since the value of t is now the same as the value of s, we might say that s and t both refer to the same Scanner object.

In our solution, we changed the memory addresses to other values, but any numbers are allowed. Here are the values we chose:

Object / Variable

Address

s

21

t

481

Scanner object

78421

Diagram 1:

21: [ 78421 ]
481: [ 78421 ]
           ...
78421: [ scanner object contents ... ]

Diagram 2:

  +--+     +----------------+
s | -|----◆|                |
  +--+     |                |
           +----------------+
              ◆
  +--+        |
t | -|--------+
  +--+

This is an interesting scenario because the calling object for s and the calling object for t now refer to the same object!

Rapid Fire Review
  1. What is stored in a reference variable in Java?

    1. The actual object.

    2. The memory address where the object is stored.

    3. The class type of the object.

    4. The size of the object in memory.

  2. In the following declaration, what does the variable s store?

    Scanner s = null;
    
    1. A reference to an object of type Scanner.

    2. The value null, indicating that s does not refer to any object.

    3. The specific memory location 800.

    4. The Scanner class.

  3. Given the following code, what happens when the second line executes?

    Scanner s;
    s = new Scanner(System.in);
    
    1. A new Scanner object is created, and its reference is assigned to s.

    2. The value null is assigned to s.

    3. The Scanner object is destroyed.

    4. The value of s is set to an arbitrary memory address.

  4. In the memory diagram below, what does the line from s to the Scanner object represent?

      +--+     +----------------+
    s | -|----◆|                |
      +--+     |                |
               +----------------+
    
    1. The actual Scanner object.

    2. The method that s calls.

    3. The reference stored in s that points to the Scanner object.

    4. The size of the Scanner object in memory.