Pass By Value
Does Java pass objects by reference or by value? Decipher the memory myth and master how variables actually point to data.
One of the most confusing topics for new developers is how data moves between functions. You might hear people say "Java passes objects by reference." This is actually a myth.
To be a true LLD engineer, you must understand how the Stack and Heap work together.
The Essentials
The "Memory Truth" for this post:
- Always Value: In Java and JavaScript, everything is passed by Value.
- The "Remote" Analogy: For objects, the "value" being passed is actually the memory address (the remote), not the object itself.
- Stack vs Heap: Variables live on the Stack, while the actual objects live on the Heap.
- Copies: When you pass a variable to a function, the computer creates a copy of what was inside that variable's box on the stack.
The Remote Control Analogy
Imagine you have a giant TV (the Object). Because it's too big to move, it stays in one place (the Heap). To control it, you use a small Remote (the Reference/Address).
- Passing the Remote: If you give your friend a "copy" of your remote, you are passing the Value of the remote.
- Changing the TV: If your friend uses their remote to change the channel, the channel on your TV changes too. Why? Because you both have remotes pointing to the same physical TV.
- Changing the Remote: If your friend throws their remote in the trash and buys a new one for a different TV, your remote still points to the original TV.
This is how Pass-By-Value works with objects. You are passing a copy of the "Remote" (the memory address).
Stack and Heap: The Internal Map
- The Stack (Fast & Small): This is where your local variables and function calls live. It's like a small desk where you keep your current remotes.
- The Heap (Large & Shared): This is where all objects live. It's like a giant warehouse where all the TVs are kept.
Pass By Value in Action: The Remote Problem
If you try to swap objects by reassigning them, it fails. You are only swapping local "remotes" on the function stack.
let temp = s1;
s1 = s2;
s2 = temp;
}
The original variables in the main function stay exactly the same. The "value" (address) copy was swapped, not the original.
If you use the copy of the remote to reach inside the TV and change a setting, the change is real and persistent.
s.name = "New";
}
update(myStudent);
// myStudent.name IS changed!
Both the main remote and the copy point to the same TV on the Heap. The data itself was modified.
Code Implementation: The "Copy" Mechanics
Here is the proof that Java/TS pass the value of the address, not the address itself:
class Student {
name: string;
constructor(n: string) { this.name = n; }
}
function attemptSwap(s1: Student, s2: Student) {
let temp = s1;
s1 = s2;
s2 = temp;
// Local s1 and s2 are swapped, but they are just copies!
}
function updateName(s: Student) {
s.name = "Changed"; // Follows the address to the HEAP
}
let a = new Student("A");
let b = new Student("B");
attemptSwap(a, b);
console.log(a.name); // Still "A"! (Swap failed)
updateName(a);
console.log(a.name); // Now "Changed"! (Mutation worked)The "Swap" Trap
If you try to swap two objects inside a function, it will fail.
- The Mistake: You swap the local "remotes" inside the function's stack.
- The Result: When the function ends, those local copies are destroyed. The original remotes on the main stack never moved.
With memory mysteries solved, let's look at the second pillar of OOP: Inheritance.
Practice what you just read.