In Java, all arguments are passed by value, without exception.
There is no "call by reference" in the language, though the behavior with objects can often be mistaken for it. The key distinction lies in the type of value being copied: for primitives, it's the data itself, and for objects, it's a copy of the object's memory address (a reference).
What is call by value?
In call by value, a method receives a copy of the argument's value. Any changes made to this copied value within the method do not affect the original variable in the calling method.
Case 1: Primitives (e.g., int, double, boolean)
When you pass a primitive type to a method, a new copy of the primitive's value is created on the stack for the method's parameter.
Example:
public class PassByValueExample {
public static void main(String[] args) {
int num = 10;
System.out.println("Before method call: " + num); // Prints 10
changePrimitive(num);
System.out.println("After method call: " + num); // Prints 10
}
public static void changePrimitive(int value) {
value = 20; // This changes only the local copy
System.out.println("Inside method: " + value); // Prints 20
}
}
Use code with caution.
Explanation:
- In the
mainmethod,numis initialized to 10. - When
changePrimitive(num)is called, the value10is copied into thevalueparameter. - Inside
changePrimitive, the local parametervalueis changed to20. This has no effect on the originalnumvariable in themainmethod.
The object reference illusion: Why it seems like call by reference
The confusion around Java's parameter passing arises when dealing with objects. In this case, Java is still passing by value, but the value being copied is the object's reference, which is the memory address of the actual object.
Case 2: Objects
When an object is passed to a method, a copy of the object reference is passed. Both the original variable and the method's parameter will hold a reference to the exact same object in the heap memory. This means:
- Modifying the object's state: If the method changes the state (e.g., a field) of the object using the copied reference, those changes will be visible to the caller. This is because both references point to the same object.
- Reassigning the reference: If the method reassigns its parameter to a new object, this will not affect the original reference in the calling method. The parameter's reference will simply point to a different object, while the caller's reference remains unchanged.
Example:
class Dog {
String name;
Dog(String name) { this.name = name; }
}
public class PassByReferenceIllusion {
public static void main(String[] args) {
Dog myDog = new Dog("Max");
System.out.println("Before method call (name): " + myDog.name); // Prints "Max"
changeDogName(myDog);
System.out.println("After changeDogName (name): " + myDog.name); // Prints "Fifi"
reassignDog(myDog);
System.out.println("After reassignDog (name): " + myDog.name); // Still prints "Fifi"
}
// Modifies the object's state, visible to the caller
public static void changeDogName(Dog dog) {
dog.name = "Fifi";
}
// Reassigns the local reference, not visible to the caller
public static void reassignDog(Dog dog) {
dog = new Dog("Buddy");
System.out.println("Inside reassignDog (name): " + dog.name); // Prints "Buddy"
}
}
Use code with caution.
Explanation:
- Initially,
myDogis a reference pointing to aDogobject with the name "Max". changeDogName(myDog)is called. The reference is copied, so bothmyDogand thedogparameter point to the same object. Thedog.name = "Fifi"line modifies the actual object on the heap, somyDog.namealso reflects this change.reassignDog(myDog)is called. The reference is again copied. The linedog = new Dog("Buddy")creates a newDogobject. The localdogparameter is now updated to point to this new object, but the originalmyDogvariable still points to the firstDogobject.
Pass by reference vs. Pass by value: Summary
| Feature | Pass by Value | Pass by Reference | Java's Behavior |
|---|---|---|---|
| Data passed | A copy of the value. | An alias (reference) to the original variable. | Always a copy of the value. For objects, this value is the reference (memory address). |
| Modification visibility | Changes to the parameter do not affect the original argument. | Changes to the parameter directly affect the original argument. | For primitives: No changes visible. For objects: Changes to the object's internal state are visible, but reassigning the reference is not. |
| Mechanism | A new memory space is allocated on the stack for the parameter's copy. | The parameter directly accesses the same memory location as the original argument. | For primitives: New stack space for the copied value. For objects: New stack space for the copied reference, which points to the original object in the heap. |
Conclusion
The definitive answer is that Java is strictly pass-by-value. The confusion stems from a crucial distinction in what "value" means for different data types. For primitives, the value is the data itself. For objects, the value is the reference (memory address), so when this reference is passed by value, multiple variables end up pointing to the same underlying object. Understanding this mechanism is vital for predicting how methods will interact with different types of data in Java.