Object References
IB Syllabus: B3.1 – Understand how objects are stored and referenced in memory.
Table of Contents
- Key Concepts
- Worked Examples
- Quick Check
- Trace Exercise
- Spot the Error
- Predict the Output
- Practice Exercises
- 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 = a1to 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
nullbefore calling methods on array elements.new Student[30]creates 30nullreferences – 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 line | x.balance | y.balance | z 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.
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
- Predict the output – Trace this code and predict what each
printlnoutputs. 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); -
Null check practice – Write a method
countPassing(Student[] students)that returns how many students have a grade of 50 or above. The array may havenullentries. Include the null check. - Explain the difference – In your own words, explain why
==returnstrueforint a = 5; int b = 5;butfalsefor two Student objects with identical data.
Extension
- 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, andcreference? Has any data been lost? - Method parameter behaviour – Explain why this method changes the original account’s balance but does NOT change which object
myAccpoints 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
- Deep copy – The
Studentclass hasname(String) andgrade(int). Write a methodcopyStudent(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
thisKeyword –thisis 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