Why is Java Pass by Value and Not Pass by Reference — Detailed Simple Case Study

Java Pass by Value and Not by Reference

There is a common confusion in Java Developers especially who have worked with C/C++, whether Java is “Pass by Value” or “Pass by Reference”.

What is Pass by Value?

In Pass by Value, The method parameter values are copied to another variable and then the copied object is passed. Any changes happen inside the method happens on this copy of the variable, which means that the value of the original parameter passed in the method will be unchanged outside the method scope.

What is Pass by Reference?

In Pass by Reference, An alias or reference to the actual parameter is passed to the method. As the actual method is passed, any changes made in the method on the parameter will be observed outside the method scope also.

Examples to prove Java Pass By Value

Let’s observe some examples and try to clear this confusion once and for all.

For the sake of simplicity, we will divide the clarification into 3 parts:

  1. Primitives
  2. Regular Objects
  3. Collection Objects

Primitive

public static void main(String[] args) {    int number = 1;
System.out.println("Main Method before the change(): " + number);
change(number);
System.out.println("Main Method after the change(): " + number);
}
public static void change(int number) { number = 10; System.out.println("Change Method: " + number);}

OUTPUT:

Main Method before the change(): 1 Change Method: 10 Main Method after the change(): 1

It is very obvious that in the above code, Java has passed the 10 in variable number instead of passing reference of variable number.

So Any change in number inside method change, will not impact variable number outside the change method.

Regular Objects

package com.adevguide.java.generic;public class PassByValueReference {    public static void main(String[] args) {
Vehicle car = new Vehicle("4-Wheeler", "Mercedes G", "Black");
Vehicle bike = new Vehicle("2-Wheeler", "Yamaha FZS", "Yellow-Green");
System.out.println("Before the Swap: ");
System.out.println("Car(v1) name: " + car.getName());
System.out.println("Bike(v2) Color: " + bike.color);
swap(car, bike);
System.out.println("After the Swap: ");
System.out.println("Car(v1) name: " + car.getName());
System.out.println("Bike(v2) Color: " + bike.color);
} public static void swap(Vehicle v1, Vehicle v2) { Vehicle temp = v1; // Creating Temp Vehicle object
v1 = v2; // Swapping v1 and v2
v2 = temp;
System.out.println("Inside swap: v1 color: " + v1.getName());
System.out.println("Inside swap: v2 color: " + v2.getName());
}}class Vehicle {
private String type;
private String name;
String color;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Vehicle(String type, String name, String color) {
this.type = type;
this.name = name;
this.color = color;
}
}

Output:

Before the Swap: Car(v1) name: Mercedes G Bike(v2) Color: Yellow-Green Inside swap: v1 color: Yamaha FZS Inside swap: v2 color: Mercedes G After the Swap: Car(v1) name: Mercedes G Bike(v2) Color: Yellow-Green

When I said that Java is strictly passed by value, I meant It. As we cannot directly pass the value of an object just like primitives in method, Java pass reference of the parameter as values.

Java manipulates objects ‘by reference,’ but it passes object references to methods ‘by value’.

For simplicity, you can think that Java always creates a new object for every parameter type and then assign the actual reference of the parameter to this.
So any changes (like swap) made to this method parameter will be valid only inside the scope of the method.

In our Example, v1 has a reference same as car and v2 has a reference same as the bike. Now once the swap is performed inside the method, v1 has reference same as bike and v2 has reference same as the bike.

As the scope of v1 and v2 is only inside method swap(), the values of car and bike object are still intact.

When instead of a swap if we try to alter the value of the object, the changes will be available outside the method also because the changes will be made to the reference of the object which is passed as value.

package com.adevguide.java.generic;public class PassByValueReference {    public static void main(String[] args) {
Vehicle car = new Vehicle("4-Wheeler", "Mercedes G", "Black");
Vehicle bike = new Vehicle("2-Wheeler", "Yamaha FZS", "Yellow-Green");
Vehicle oldCar = car;
Vehicle oldBike = bike;
System.out.println("Before the change: ");
System.out.println("Car(v1) is: " + car.toString());
System.out.println("Bike(v2) Color: " + bike.toString());
change(car, bike);
System.out.println("After the change: ");
System.out.println("Car(v1) is: " + car.toString());
System.out.println("Bike(v2) is: " + bike.toString());
System.out.println(oldCar == car); // true
System.out.println(oldBike == bike); // true
} public static void change(Vehicle v1, Vehicle v2) { v1.setName("Honda Jazz"); // v1 is pointing towards car Vehicle temp = v1; // Creating Temp Vehicle object v2.setName("Pulsur"); //// v1 is pointing towards bike
v1 = v2; // Swapping v1 and v2
v1.color = "Green"; // v1 is pointing towards bike
v2 = temp; // v2 is pointing towards car
v2.color = "red"; //// v1 is pointing towards car
System.out.println("Inside swap: v1 is: " + v1.toString());
System.out.println("Inside swap: v2 is: " + v2.toString());
}}class Vehicle {
private String type;
private String name;
String color;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Vehicle(String type, String name, String color) {
this.type = type;
this.name = name;
this.color = color;
}
@Override
public String toString() {
return "Vehicle [type=" + type + ", name=" + name + ", color=" + color + "]";
}
}

Output:

Before the change: Car(v1) is: Vehicle [type=4-Wheeler, name=Mercedes G, color=Black] Bike(v2) Color: Vehicle [type=2-Wheeler, name=Yamaha FZS, color=Yellow-Green] Inside swap: v1 is: Vehicle [type=2-Wheeler, name=Pulsur, color=Green] Inside swap: v2 is: Vehicle [type=4-Wheeler, name=Honda Jazz, color=red] After the change: Car(v1) is: Vehicle [type=4-Wheeler, name=Honda Jazz, color=red] Bike(v2) is: Vehicle [type=2-Wheeler, name=Pulsur, color=Green]

In the above example, The change function is just a little modification of swap function. As I said before, Object v1 and v2 have a reference to car and bike.

Swap changes the reference of v1 to bike and v2 to the car inside the method scope but they always point to one of these two objects. So changes made to v1 and v2 will always alter the values of the object it has a reference at that time.

Even the values of the object have changed now, but as OldCar and car are still pointing towards the same object, oldCar==car will return true.

Collections Objects

Collections like List, Map, Set follows the same rules as a regular object.

package com.adevguide.java.generic;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class PassByValueReference { public static void main(String[] args) { Map<String, Integer> map = new HashMap<String, Integer>(); map.put("A", 1);
map.put("B", 2);
List<Integer> list = new ArrayList<Integer>();
list.add(10);
list.add(20);
System.out.println("map Before the Change(): " + map);
System.out.println("list Before the Change(): " + list);
change(map, list); System.out.println("map After the Change(): " + map);
System.out.println("list After the Change(): " + list);
} public static void change(Map m1, List l1) {
m1.put("C", 3);
l1.add(30);
Map<String, Integer> newMap = new HashMap<String, Integer>(); newMap.putAll(m1);
newMap.put("D", 4);
List<Integer> newList = new ArrayList<Integer>();
newList.addAll(l1);
newList.add(40);
Map<String, Integer> lastMap = m1;
List<Integer> lastList = l1;
lastMap.remove("A");
lastList.remove(0);
System.out.println("Inside Change Method:");
System.out.println("m1 is: " + m1);
System.out.println("newMap is : " + newMap);
System.out.println("lastMap is : " + lastMap);
System.out.println("l1 is : " + l1);
System.out.println("newList is : " + newList);
System.out.println("lastList is : " + lastList);
}}

Output:

map Before the Change(): {A=1, B=2} list Before the Change(): [10, 20] Inside Change Method: m1 is: {B=2, C=3} newMap is : {A=1, B=2, C=3, D=4} lastMap is : {B=2, C=3} l1 is : [20, 30] newList is : [10, 20, 30, 40] lastList is : [20, 30] map After the Change(): {B=2, C=3} list After the Change(): [20, 30]

As we can see, map,m1, and lastMap have a common reference to an object whereas newMap points towards a different object. This means any changes made to either of map,m1, and lastMap will reflect in others too. Whereas newMap is independent of these changes.

Similarly, list,l1, and lastList have a common reference to an object whereas newList points towards a different object. This means any changes made to either of map,m1, and lastMap will reflect in others too. Whereas newList is independent of these changes.

Java Pass By Value Tutorial Source Code

You can find the complete source code used in this tutorial on Github.

GitHub Source Code

References

https://docs.oracle.com/javase/tutorial/java/javaOO/arguments.html

This article was originally published at https://www.adevguide.com/how-is-java-pass-by-value-and-not-by-reference/ on 26th Aug 2019.

ADevGuide is a portal for developers to read, learn, and share interesting skills. Follow us at https://www.adevguide.com