Object References

IB Syllabus: B3.1 – Understand how objects are stored and referenced in memory.

Table of Contents

  1. Key Concepts
    1. Primitives vs Objects in Memory
    2. Aliasing – Two References, One Object
    3. Contrast with Primitives
    4. null – No Object
    5. == vs .equals() – Comparing Objects
    6. Passing Objects to Methods
  2. Worked Examples
    1. Example 1: Tracing Aliased References
    2. Example 2: Null Check Pattern
  3. Quick Check
  4. Trace Exercise
  5. Spot the Error
  6. Predict the Output
  7. Practice Exercises
    1. Core
    2. Extension
    3. Challenge
  8. Connections

Key Concepts

Primitives vs Objects in Memory

Java stores primitive types (int, double, boolean, char) directly in the variable – the variable holds the actual value.

Java stores objects differently. The variable does not hold the object itself – it holds a reference (a memory address that points to where the object lives in memory).

Primitive:   int age = 25;
             age → [ 25 ]          ← the value IS in the variable

Object:      Student s = new Student("Alice");
             s → [ address ] → [ name: "Alice", grade: 0 ]
                  ↑ reference       ↑ the actual object (in heap memory)

This distinction matters because it changes how assignment, comparison, and method parameters behave.

Aliasing – Two References, One Object

When you assign one object variable to another, you copy the reference, not the object. Both variables now point to the same object in memory. This is called aliasing.

BankAccount a1 = new BankAccount("Alice", 500.0);
BankAccount a2 = a1;  // a2 copies the reference, NOT the object

a2.deposit(200.0);

System.out.println(a1.getBalance());  // 700.0
System.out.println(a2.getBalance());  // 700.0

Both a1 and a2 point to the same object. Depositing through a2 changes the object that a1 also references.

Before:   a1 ──┐
               ├──► [ Alice, 500.0 ]    ← ONE object, two references
          a2 ──┘

After:    a1 ──┐
               ├──► [ Alice, 700.0 ]    ← still ONE object, both see the change
          a2 ──┘

This is one of the most common sources of bugs. Students expect a2 = a1 to create an independent copy. It does not – it creates a second name for the same object. To create an actual copy, you must construct a new object and copy the values.

Contrast with Primitives

Primitives behave differently because the variable holds the value directly:

int x = 10;
int y = x;   // copies the VALUE, not a reference

y = 20;

System.out.println(x);  // 10 -- unchanged
System.out.println(y);  // 20

Changing y does not affect x because each variable holds its own independent copy of the value.

null – No Object

A reference variable that does not point to any object holds the special value null. It means “this variable exists but references nothing.”

Student s = null;  // s exists but points to no object

Calling a method on a null reference causes a NullPointerException – one of the most common runtime errors in Java:

Student s = null;
System.out.println(s.getName());  // NullPointerException!

The fix: always check for null before calling methods, especially when working with arrays of objects where not every slot may be filled:

if (s != null) {
    System.out.println(s.getName());
}

When working with object arrays, always check for null before calling methods on array elements. new Student[30] creates 30 null references – not 30 Student objects. This null check is worth a mark in almost every IB exam method question.

== vs .equals() – Comparing Objects

For primitives, == compares values:

int a = 5;
int b = 5;
System.out.println(a == b);  // true -- same value

For objects, == compares references (memory addresses), not the data inside:

Student s1 = new Student("Alice", 11);
Student s2 = new Student("Alice", 11);

System.out.println(s1 == s2);  // false -- different objects in memory

Even though s1 and s2 have identical data, they are two separate objects at different memory addresses. == checks whether both variables point to the same object, not whether the objects contain the same values.

To compare the content of objects, use .equals():

String a = new String("hello");
String b = new String("hello");

System.out.println(a == b);      // false -- different objects
System.out.println(a.equals(b)); // true -- same content

For Strings, always use .equals() for comparison, never ==. The == operator checks if two String variables point to the exact same object in memory, which is usually not what you want. "hello".equals(other) checks if the characters match.

Passing Objects to Methods

When you pass a primitive to a method, Java copies the value. The method works with its own copy – changes do not affect the original.

When you pass an object to a method, Java copies the reference. The method receives a reference to the same object – changes to the object inside the method ARE visible to the caller.

public static void doubleBalance(BankAccount acc) {
    double current = acc.getBalance();
    acc.deposit(current);  // modifies the ORIGINAL object
}

BankAccount myAcc = new BankAccount("Alice", 100.0);
doubleBalance(myAcc);
System.out.println(myAcc.getBalance());  // 200.0 -- changed!

The method received a copy of the reference (pointing to the same object), so acc.deposit() modifies the same object that myAcc references.


Worked Examples

Example 1: Tracing Aliased References

BankAccount a = new BankAccount("Alice", 1000.0);
BankAccount b = new BankAccount("Bob", 500.0);
BankAccount c = a;  // c is an alias for a

c.deposit(300.0);
b.deposit(100.0);
After line a.balance b.balance c.balance Notes
Lines 1-2 1000.0 500.0 Two separate objects
Line 3 1000.0 500.0 1000.0 c points to the SAME object as a
Line 4 1300.0 500.0 1300.0 c.deposit changes a’s object too
Line 5 1300.0 600.0 1300.0 b is independent

Example 2: Null Check Pattern

Student[] roster = new Student[5];
roster[0] = new Student("Alice", 95);
roster[1] = new Student("Bob", 82);
// roster[2], [3], [4] are still null

int count = 0;
for (int i = 0; i < roster.length; i++) {
    if (roster[i] != null && roster[i].getGrade() > 90) {
        count++;
    }
}
System.out.println(count);  // 1 (only Alice)

The roster[i] != null check MUST come first. Java uses short-circuit evaluation – if the left side of && is false, the right side is never evaluated. Without the null check, roster[2].getGrade() would throw a NullPointerException.


Quick Check

Q1. What does BankAccount b = a; do?

Q2. What happens when you call s.getName() and s is null?

Q3. Two Student objects have identical name and grade. What does s1 == s2 return?

Q4. If you pass a BankAccount object to a method and the method calls deposit(), is the original object changed?


Trace Exercise

Trace the references and object state through this code.

Trace: Aliasing and Independence

BankAccount x = new BankAccount("Alice", 100.0);
BankAccount y = new BankAccount("Bob", 200.0);
BankAccount z = x;
z.deposit(50.0);
y.deposit(25.0);
After linex.balancey.balancez points to
Lines 1-2 100.0 200.0 --
Line 3: z = x
Line 4: z.deposit(50)
Line 5: y.deposit(25)

Spot the Error

This code prints "different" even though both strings contain "hello". Click the buggy line, then pick the fix.

1String a = new String("hello"); 2String b = new String("hello"); 3 4if (a == b) { 5 System.out.println("same"); 6} else { 7 System.out.println("different"); 8}

Pick the fix:


Predict the Output

What does this print?

BankAccount a = new BankAccount("Alice", 500.0);
BankAccount b = a;
b.deposit(100.0);
System.out.println(a.getBalance());

What does this print?

int x = 10;
int y = x;
y = 99;
System.out.println(x);

Practice Exercises

Core

  1. Predict the output – Trace this code and predict what each println outputs. Explain why.
    Student s1 = new Student("Alice", 90);
    Student s2 = s1;
    Student s3 = new Student("Alice", 90);
    
    System.out.println(s1 == s2);
    System.out.println(s1 == s3);
    
  2. Null check practice – Write a method countPassing(Student[] students) that returns how many students have a grade of 50 or above. The array may have null entries. Include the null check.

  3. Explain the difference – In your own words, explain why == returns true for int a = 5; int b = 5; but false for two Student objects with identical data.

Extension

  1. Reference tracing – Draw a memory diagram showing what happens at each step:
    Student a = new Student("Alice", 90);
    Student b = new Student("Bob", 80);
    Student c = a;
    a = b;
    b = c;
    

    After all five lines, what do a, b, and c reference? Has any data been lost?

  2. Method parameter behaviour – Explain why this method changes the original account’s balance but does NOT change which object myAcc points to:
    public static void reset(BankAccount acc) {
        acc.withdraw(acc.getBalance());  // empties the account
        acc = new BankAccount("New", 0); // does NOT affect the caller
    }
    

Challenge

  1. Deep copy – The Student class has name (String) and grade (int). Write a method copyStudent(Student original) that creates and returns a completely independent copy (a new object with the same values). Prove your copy is independent by modifying the copy and showing the original is unchanged.

Connections

  • Prerequisites: Classes and Objects – creating objects with new
  • Prerequisites: The this Keywordthis is a reference to the current object
  • Related: Strings – String comparison with .equals() vs ==
  • Next: UML Class Diagrams – visual representation of class structure and relationships
  • Forward: Aggregation – objects as attributes create reference chains

Back to top

© EduCS.me — A resource hub for IB Computer Science

This site uses Just the Docs, a documentation theme for Jekyll.