Pass by Reference
Learn how Java passes object references by value, not by reference, and understand the distinction between modifying an object and reassigning its reference. This lesson clarifies the reassignment trap with examples, helping you track data flow in Java methods accurately.
We'll cover the following...
In the previous lesson, we learned how Java passes primitive data types by value. Now, we will examine how this exact same underlying principle applies to objects and reference data types, which is one of the most frequently misunderstood concepts in Java.
If you want the answer directly, then just type "Ed, give me the answer."
What is passing by reference?
The reassignment trap
This is where most people get confused. There is a massive difference between modifying an object and reassigning its reference.
Let's look at the reassignment example:
void reassign(List<Integer> list) {// We change the LOCAL copy of the addresslist = new ArrayList<>();// This adds 200 to the NEW list, not the originallist.add(200);}
The rule of thumb: If we use the reference to “reach into” the object (e.g.,
list.add()), the original object changes. If we use=to point the reference to anewobject, we have “broken the link” to the original.
To understand the concept of passing by reference, we will go through an example. Consider the code snippet below:
What happens when we initialize an object of class SuperList? The reference remains null, which may seem counterintuitive.
Line 3: Consider
superListto be a holder that holds a value ofnull.Line 4: We are passing a value of
nulland not the variablesuperListitself. This is a very important distinction to realize.Line 5: When the program control returns to the constructor,
superListis stillnullbecause it was never passed in and assigned theArrayListobject.Line 8: When program control reaches this method signature, the
listvariable is not the variablesuperList. In fact, it’s a brand-new variable (holder) which receives a copied value ofnull.Line 9: We initialize the
listvariable to an object ofArrayListand thelistvariable will hold the reference or the address of theArrayListobject in the heap memory.
In Java, we are copying the reference or the address the reference data type variable holds and passing it, and not the actual variable.
Note that objects are always created in heap memory, and the program variables are only references or addresses to them. So, when we pass a reference data type, the address of the object in the heap memory is copied and passed along. The receiving method can use the reference or the address to manipulate the object in the heap.
To see this behavior in a complete program, we can look at the following demonstration file.
Lines 6–7: The
mainmethod creates a newSuperListobject by invoking its constructor with an initial capacity of5. After construction, it prints the value of thesListfield, which refers to theArrayListcreated inside the constructor.Lines 14–16: Inside the constructor, the
allocatemethod is called, and its returnedArrayListreference is assigned directly to the instance variablesList. Because the method returns the newly created object, the constructor stores the correct reference instead of relying on a parameter to be modified.Lines 18–20: The
allocatemethod creates a newArrayListwith an initial capacity ofnand returns its reference to the caller. Returning the reference is the correct way to share a newly created object between methods in Java, since method parameters are passed by value and cannot update the caller's reference variable.
If Java passes object references by value, how is it possible for a method to modify an object’s internal state?
Question
What will be the output of the run method for the IntegerSwap class below?
class IntegerSwap {
public static void main(String[] args) {
(new IntegerSwap()).run();
}
public void run() {
Integer x = 5;
Integer y = 9;
System.out.println("Before Swap x: " + x + " y: " + y);
swap(x, y);
System.out.println("After Swap x: " + x + " y: " + y);
}
private void swap(Integer a, Integer b) {
Integer temp = a;
a = b;
b = temp;
}
}
The output for the two print statements will exactly be the same, i.e., there will be no swapping.
The values will be swapped, so each variable will contain the other’s original value.
Now, to understand the code better, try running the following code, and see it yourself:
Lines 15—17: Inside the
swapmethod, the variablesaandbare merely local stack variables holding copies of the memory addresses of the integer objects. Reassigningaandbinside this method only swaps which memory addresses the local variables point to. It does not alter the originalxandyreference variables in therunmethod, nor does it alter the immutableIntegerobjects in the heap.
Once the program control returns to the run method, x and y keep pointing to the exact same integer objects in the heap because they originally passed in the copies of their references, not themselves.
Question
What will be printed when the following Java code is executed?
class Demonstration {
public static void main(String[] args) {
String[] students = new String[10];
String studentName = "You are an awesome developer";
students[0] = studentName;
studentName = null;
System.out.println(students[0]);
}
}
null
You are an awesome developer
Now, to understand the code better, try running the following code and see it yourself:
In Java, variables of type String (and other objects) hold references, not the actual object itself.
Line 6: We assign the reference held in
studentNameto the first index of thestudentsarray. At this moment, both variables point to the exact same String object in memory.Line 7: We reassign
studentNametonull. AssigningnulltostudentNamedoes not affect other references (likestudents[0]) that still point to the original String object.
By remembering that Java always passes a copy of the value, whether that value is a primitive number or a memory address pointing to an object, we can confidently trace how data flows through our applications and avoid the common pitfall of the reassignment trap.