You might know that Java passes by value, but it helps to understand why. Here's what happens when you pass mutable and immutable object references in Java.
Many programming languages allow passing objects by reference or by value. In Java, we can only pass object parameters by value. This imposes limits and also raises questions. For instance, if the parameter value is changed in the method, what happens to the value following method execution? You may also wonder how Java manages object values in the memory heap. This article helps you resolve these and other common questions about object references in Java.
Passing object references in Java
In this article you’ll learn the difference between passing by reference and passing by value in Java and how to pass Java object references:
- ‘Pass by reference’ and ‘pass by value’ defined
- Java object references are passed by value
- Passing primitive types in Java
- Passing immutable object references in Java
- Passing mutable object references in Java
- What to avoid when passing object references
- What to remember about passing object references
‘Pass by reference’ and ‘pass by value’ defined
Pass by reference means we pass the location in memory where the variable’s value is stored and pass by value means we pass a copy of the variable’s actual value. It’s a bit more complicated than that, of course, but this definition is a good starting point.
- Passing by value means we pass a copy of the value.
- Passing by reference means we pass the real reference to the variable in memory.
Java object references are passed by value
All object references in Java are passed by value. This means that a copy of the value will be passed to a method. The tricky part is that passing a copy of the value changes the real value of the object. This example should help you understand why:
public class ObjectReferenceExample {
public static void main(String... doYourBest) {
Simpson simpson = new Simpson();
transformIntoHomer(simpson);
System.out.println(simpson.name);
}
static void transformIntoHomer(Simpson simpson) {
simpson.name = "Homer";
}
}
class Simpson {
String name;
}
What do you think the simpson.name
will be after the transformIntoHomer
method is executed?
In this case, it will be Homer! The reason is that Java object variables are simply references that point to real objects in the memory heap. Therefore, even though Java passes parameters to methods by value, if the variable points to an object reference, the real object will also be changed.
If you’re still not sure how this works, consider the following diagram:
Passing primitive types in Java
Like object types, primitive types are also passed by value. Can you guess what will happen to the primitive types in the following code example?
public class PrimitiveByValueExample {
public static void main(String... primitiveByValue) {
int homerAge = 30;
changeHomerAge(homerAge);
System.out.println(homerAge);
}
static void changeHomerAge(int homerAge) {
homerAge = 35;
}
}
If you determined that the value would change to 30, you are correct. It’s 30 because (again) Java passes object parameters by value. The number 30 is just a copy of the value, not the real value. Primitive types are allocated in the stack memory, so only the local value will be changed. In this case, there is no object reference.
Passing immutable object references in Java
What if we did the same test with an immutable String
object? Notice what happens when we change the value of a String
:
public class StringValueChange {
public static void main(String... doYourBest) {
String name = "";
changeToHomer(name);
System.out.println(name);
}
static void changeToHomer(String name) {
name = "Homer";
}
}
What do you think the output will be? If you guessed “” then congratulations! That happens because a String
object is immutable, which means that the fields inside the String
are final and can’t be changed.
Making the String
class immutable gives us better control over one of Java’s most used objects. If the value of a String
could be changed, it would create many bugs. Note, also, that we are not changing an attribute of the String
class; instead, we’re simply assigning a new String
value to it. In this case, the “Homer” value will be passed to name
in the changeToHomer
method. The String
“Homer” will be eligible to be garbage collected as soon as the changeToHomer
method completes execution. Even though the object can’t be changed, the local variable will be.
Passing mutable object references in Java
Unlike String
, most objects in the JDK are mutable, like the StringBuilder
class. The example below is similar to the previous one, but features StringBuilder
rather than String
:
static class MutableObjectReference {
public static void main(String... mutableObjectExample) {
StringBuilder name = new StringBuilder("Homer ");
addSureName(name);
System.out.println(name);
}
static void addSureName(StringBuilder name) {
name.append("Simpson");
}
}
Can you guess the output for this example? In this case, because we’re working with a mutable object, the output will be “Homer Simpson.” You could expect the same behavior from any other mutable object in Java.
Summary of what you’ve learned
You’ve learned that Java objects are passed by value, meaning that a copy of the value is passed. Just remember that the copied value points to a real object in the Java memory heap. Also, remember that passing by value changes the value of the real object.
What to avoid when passing Java object references
- Don’t try to change an immutable value by reference.
- Don’t try to change a primitive variable by reference.
- Don’t expect the real object won’t change when you change a mutable object parameter in a method (it will change).
What to remember about passing Java object references
- Java always passes parameter variables by value.
- Object variables in Java always point to the real object in the memory heap.
- A mutable object’s value can be changed when it is passed to a method.
- An immutable object’s value cannot be changed, even if it is passed a new value.
Test what you’ve learned about object references
Now, let’s test what you’ve learned about object references. In the code example below, you see the immutable String
and the mutable StringBuilder
class. Each is being passed as a parameter to a method. Knowing that Java only passes by value, what do you believe will be the output once the main method from this class is executed?
public class DragonWarriorReferenceChallenger {
public static void main(String... doYourBest) {
StringBuilder warriorProfession = new StringBuilder("Dragon ");
String warriorWeapon = "Sword ";
changeWarriorClass(warriorProfession, warriorWeapon);
System.out.println("Warrior=" + warriorProfession + " Weapon=" + warriorWeapon);
}
static void changeWarriorClass(StringBuilder warriorProfession, String weapon) {
warriorProfession.append("Knight");
weapon = "Dragon " + weapon;
weapon = null;
warriorProfession = null;
}
}
Here are the options, check the end of the article for the answer.
A: Warrior=null Weapon=null
B: Warrior=Dragon Weapon=Dragon
C: Warrior=Dragon Knight Weapon=Dragon Sword
D: Warrior=Dragon Knight Weapon=Sword
Solving the challenge
The first parameter in the above example is the warriorProfession
variable, which is a mutable object. The second parameter, weapon, is an immutable String
:
static void changeWarriorClass(StringBuilder warriorProfession, String weapon) {
...
}
At the first line of this method, we append the Knight
value to the warriorProfession
variable. Remember that warriorProfession
is a mutable object; therefore the real object will be changed, and the value from it will be “Dragon Knight.”
warriorProfession.append("Knight");
In the second instruction, the immutable local String
variable will be changed to “Dragon Sword.” The real object will never be changed, however, since String
is immutable and its attributes are final:
weapon = "Dragon " + weapon;
Finally, we pass null
to the variables here, but not to the objects. The objects will remain the same as long as they are still accessible externally—in this case through the main method. And, although the local variables will be null, nothing will happen to the objects:
weapon = null;
warriorProfession = null;
From this we can conclude that the final values from our mutable StringBuilder
and immutable String
will be:
System.out.println("Warrior=" + warriorProfession + " Weapon=" + warriorWeapon);
The only value that changed in the changeWarriorClass
method was warriorProfession
, because it’s a mutable StringBuilder
object. Note that warriorWeapon
did not change because it’s an immutable String
object.
The correct output from our Challenger code would be:
D: Warrior=Dragon Knight Weapon=Sword
.
Video challenge! Debugging object references in Java
Debugging is one of the easiest ways to fully absorb programming concepts while also improving your code. In this video, you can follow along while I debug and explain object references in Java.
Learn more about Java
- Get more quick code tips: Read all of Rafael’s articles in the InfoWorld Java Challengers series.
- Check out more videos in Rafael’s Java Challengers video playlist.
- Find even more Java Challengers on Rafael’s Java Challengers blog and in his book, with more than 70 code challenges.