Definition
One rule, two consequences:
- Primitives: The value is copied.
int fee = 50; process(fee);passes a copy of 50. Changing the parameter inside process() does not change fee. - Object references: The reference is copied.
Account a = new Account(); process(a);passes a copy of the reference. Both the originalaand the parameter point to the same object. Modifying the object through the parameter is visible to the caller. But reassigning the parameter to a new object does not affecta.
Passing a primitive is like giving someone a photocopy of a bank slip – they can scribble on their copy, but your original is unchanged. Passing an object reference is like giving someone a copy of a key to a locker – they can open the locker and modify the contents (the object), but giving them a different key (reassigning the parameter) does not change your key.
Understanding the Concepts
Pass-by-value for primitives
public static void addFee(int amount) {
amount += 50; // modifies the LOCAL copy
System.out.println("Inside: " + amount); // 150
}
int amount = 100;
addFee(amount);
System.out.println("Outside: " + amount); // 100 (unchanged!)
The value 100 is copied into the parameter. The original is unaffected.
Pass-by-value for object references
public static void debit(Account account, BigDecimal amount) {
account.debit(amount); // modifies the OBJECT (same object, two references)
}
Account rajesh = new Account("SAV2001", new BigDecimal("1000000"));
debit(rajesh, new BigDecimal("50000"));
System.out.println(rajesh.getBalance()); // 950000 (changed! same object)
The reference is copied. Both rajesh and account (the parameter) point to the same heap object. Modifying the object through the parameter is visible to the caller.
Why reassignment does not work
public static void replace(Account account) {
account = new Account("CUR3001", BigDecimal.ZERO); // reassigns the LOCAL copy
System.out.println("Inside: " + account.getAccountId()); // CUR3001
}
Account rajesh = new Account("SAV2001", new BigDecimal("1000000"));
replace(rajesh);
System.out.println("Outside: " + rajesh.getAccountId()); // SAV2001 (unchanged!)
Reassigning the parameter to a new object changes only the local copy. The caller's variable still points to the original object.
The swap test (proof that Java is pass-by-value)
public static void swap(Account a, Account b) {
Account temp = a;
a = b;
b = temp;
// a and b are swapped LOCALLY, but the originals are unchanged
}
Account rajesh = new Account("SAV2001", new BigDecimal("1000000"));
Account priya = new Account("CUR3001", new BigDecimal("250000"));
swap(rajesh, priya);
System.out.println(rajesh.getAccountId()); // SAV2001 (not swapped!)
System.out.println(priya.getAccountId()); // CUR3001 (not swapped!)
If Java were pass-by-reference, the swap would work. It does not, proving Java passes copies of the references.
How is this different from C++ pass-by-reference?
In C++, you can declare a parameter as a reference (void swap(Account& a, Account& b)). This gives the method direct access to the caller's variable. Reassigning the parameter reassigns the caller's variable. Java does not have this feature.
What are common mistakes?
- Saying "objects are passed by reference." Objects are not passed at all. References to objects are passed by value.
- Expecting swap to work. It does not, because parameters are copies.
- Confusing "can modify the object" with "pass-by-reference." You can modify the object because both references point to the same object. But you cannot make the caller's variable point elsewhere.
What should a senior engineer know?
- This is why Java methods return values. Since you cannot modify the caller's variable, you return the new value:
BigDecimal newBalance = calculate(balance); - Builder and fluent APIs return
thisto allow chaining:account.withBalance(100).withName("Rajesh"). Each method returns the object, not modifying a parameter. - Immutable objects are safe to pass because the method cannot modify their state. This is why
StringandBigDecimalare immutable.
Visual Overview
Primitive: int amount = 100;
addFee(amount) → copies 100 into parameter
parameter += 50 → local copy becomes 150
amount is still 100 (unchanged)
Object: Account rajesh = new Account(...);
debit(rajesh, 50000) → copies the REFERENCE into parameter
parameter.debit(50000) → modifies the SHARED OBJECT
rajesh.getBalance() → 950000 (changed via shared reference)
Reassignment: replace(rajesh)
parameter = new Account(...) → local copy points to new object
rajesh still points to original (unchanged)
Swap: swap(rajesh, priya)
local copies swapped → originals unchanged
PROOF: Java is pass-by-value
Key Takeaway
Java is always pass-by-value. For primitives, the actual value is copied. For object references, the reference (memory address) is copied. You can modify the object through the copied reference because both references point to the same heap object. But you cannot make the caller's variable point to a different object by reassigning the parameter.
The swap test proves this: swapping two object parameters inside a method does not swap the caller's variables, because the method only has copies of the references.
One sentence to remember: Java copies the reference, not the object – you can modify the shared object but cannot reassign the caller's variable.
---