By WebNest Studio Engineering Team
Java - Core Tutorial
Call By Value in Java
One of the most persistent myths in Java is that objects are "passed by reference." They are not. Java is, without exception, strictly pass-by-value — every single time you call a method, Java copies the value stored in the argument and hands that copy to the method. What differs between primitives and objects is what that "value" actually is.
For a primitive (int, double, boolean, and so on), the value is the literal data itself, so the method receives an independent copy of the number. For an object, the variable never holds the object — it holds a reference (essentially a memory address pointing to the object on the heap). When you pass an object variable to a method, Java copies that reference, not the object. The method now has its own copy of the address, but that copy still points to the exact same object in memory.
Why it matters
Whether a method can change the caller's data is one of the most misunderstood parts of Java, and a frequent source of bugs. The rule is a single sentence — Java always passes a copy of the value — but because the value of an object variable is a reference, the effect looks different for primitives and for objects.
Watch: How Call By Value Works in Java
A quick look at how Java hands every method a copy of its arguments, and why that copy is a value for primitives but a reference for objects.
Why Mutating Fields "Looks Like" Pass-by-Reference
Because the copied reference points to the same heap object, calling a method that changes the object's fields through that reference (e.g. obj.setName("New")) really does change the object the caller sees — there is only one object, and both the caller's reference and the parameter's reference point to it. This is the source of the confusion: people see the caller's object change and assume Java passed the object itself. In reality, Java passed a copy of the pointer to that one shared object.
Why Reassigning the Parameter Does Not Affect the Caller
Reassigning the parameter inside the method (e.g. param = new Employee("Someone Else")) only changes what the local copy of the reference points to. It does not — and cannot — reach back and change the caller's original variable, because the caller's variable is a completely separate copy of the reference. Once the method returns, the caller's variable still points to the original object, unaffected by the reassignment.
Primitives vs Objects — the Same Rule, Two Effects
The underlying rule never changes: a copy of the value is passed. It only looks different because a primitive's "value" is the data, while an object variable's "value" is an address. Keeping this single mental model — "always a copy of the value, and for objects that value is an address" — resolves nearly every confusing case you will encounter with method parameters in Java.
- Passing a primitive: the method gets a copy of the number; changes to the parameter never affect the caller's variable.
- Passing an object and mutating its fields: both references point to the same object, so the change is visible to the caller.
- Passing an object and reassigning the parameter: only the local copy of the reference changes; the caller's reference is untouched.
What Happens in Memory During a Call
Each method call gets its own stack frame, and every parameter is a brand-new local variable in that frame. When the call starts, Java copies the argument's value into the parameter. When the call ends, the frame and all its parameters are thrown away. Objects live separately on the heap, so they survive the call. That is why a change made through a reference outlives the method, while a change made to the parameter variable itself disappears with the frame.
The Classic Test: Why a swap Method Fails
A swap(int a, int b) method that exchanges its two parameters looks correct but changes nothing for the caller, because it only swaps the two local copies. The same is true for swap(Employee a, Employee b): it swaps the copied references, not the caller's variables. To swap for real, swap the contents of something both sides share, such as two elements of the same array, or return the result.
Strings and Wrapper Types Look Like Primitives
String, Integer and the other wrapper classes are objects, so a copy of the reference is passed. They are also immutable: no method can change them in place. Something like text = text + "!" builds a new String and points only the local copy at it, so the caller still sees the original. A mutable type such as StringBuilder behaves differently, because append changes the one object both references share.
How to Get a Changed Value Back to the Caller
Since a method can never rebind the caller's variable, pick one of these instead:
- Return the new value and let the caller store it (
score = addBonus(score);). This is the clearest option and the usual choice. - Change a shared mutable object, such as a field of an object, an array element or a collection, when updating that object is the method's job.
- Return a small result object or record when a method needs to hand back several values at once.
Examples
Mutating fields is visible to the caller; reassigning the reference is not
class Employee {
String name;
Employee(String name) { this.name = name; }
}
public class CallByValueDemo {
static void mutateField(Employee e) {
e.name = "Changed Inside Method"; // same object, so caller sees this
}
static void reassignReference(Employee e) {
e = new Employee("Brand New Object"); // only the local copy of the reference changes
}
static void changePrimitive(int value) {
value = value + 100; // only the local copy changes
}
public static void main(String[] args) {
Employee emp = new Employee("Original");
mutateField(emp);
System.out.println(emp.name); // field mutation IS visible
reassignReference(emp);
System.out.println(emp.name); // reassignment is NOT visible
int score = 10;
changePrimitive(score);
System.out.println(score); // primitive change is NOT visible
}
}
Changed Inside Method
Changed Inside Method
10
A swap method fails on primitives but works on a shared array
public class SwapDemo {
static void swap(int a, int b) {
int temp = a;
a = b;
b = temp; // swaps the local copies only
}
static void swapInArray(int[] values) {
int temp = values[0];
values[0] = values[1];
values[1] = temp; // changes the one array both references point to
}
public static void main(String[] args) {
int x = 1, y = 2;
swap(x, y);
System.out.println("After swap: x = " + x + ", y = " + y);
int[] pair = {1, 2};
swapInArray(pair);
System.out.println("After swapInArray: " + pair[0] + ", " + pair[1]);
}
}
After swap: x = 1, y = 2
After swapInArray: 2, 1
An immutable String is unaffected; a mutable StringBuilder changes
public class StringParamDemo {
static void addMark(String text) {
text = text + "!"; // builds a new String; only the local reference moves to it
}
static void addMark(StringBuilder text) {
text.append("!"); // changes the one StringBuilder the caller also points to
}
public static void main(String[] args) {
String greeting = "Hello";
addMark(greeting);
System.out.println(greeting);
StringBuilder builder = new StringBuilder("Hello");
addMark(builder);
System.out.println(builder);
}
}
Hello
Hello!
Returning the new value is how a method hands a change back
public class ReturnValueDemo {
static int addBonus(int score) {
return score + 100; // hand the new value back instead of changing the parameter
}
public static void main(String[] args) {
int score = 10;
addBonus(score); // result ignored, score unchanged
System.out.println(score);
score = addBonus(score); // caller chooses to store the result
System.out.println(score);
}
}
10
110
Common Mistakes
- Saying Java is "pass by reference for objects" — Java always passes a copy of the value; for objects that value happens to be a reference.
- Expecting a method to replace the caller's object by reassigning the parameter (e.g. writing a "swap two objects" method that just reassigns the two parameters) — this only rebinds local copies and has no effect outside the method.
- Concluding that because field mutation is visible, reassignment must also be visible — these are two entirely different operations on the reference.
- Confusing Java with languages like C++ that have true reference parameters (&) or with C-style pointer-to-pointer techniques, neither of which Java supports.
Exercise
This swap method does nothing useful: the program prints 1 2 because the method only swaps its own copies. Change the program so the two values really are swapped, by passing an array and swapping its elements.
Expected output: 2 1
public class SwapDemo {
static void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}
public static void main(String[] args) {
int x = 1;
int y = 2;
swap(x, y);
System.out.println(x + " " + y);
}
}
Show hints
- A method cannot change the caller's primitive variables, but it can change the contents of an object or array the caller passed in.
- Store both values in an
int[]and swappair[0]withpair[1]inside the method.
Show solution
public class SwapDemo {
static void swap(int[] pair) {
int temp = pair[0];
pair[0] = pair[1];
pair[1] = temp;
}
public static void main(String[] args) {
int[] values = {1, 2};
swap(values);
System.out.println(values[0] + " " + values[1]); // 2 1
}
}
Quick quiz
1. How does Java pass arguments to methods?
- A. Always by value
- B. Primitives by value, objects by reference
- C. Always by reference
- D. It depends on the method
Show answer
A. Java always passes a copy of the value. For an object variable, that value is the reference, so the copy points to the same object.
2. A method receives a Person p and runs p.name = "X";. Does the caller see the change?
- A. Only if the method is static
- B. No, the method works on a copy of the object
- C. Only if p is declared final
- D. Yes, both references point to the same object
Show answer
D. The reference was copied, not the object. Changing a field through either reference changes the one shared object.
3. The same method instead runs p = new Person("Y");. What happens to the caller's variable?
- A. It is unchanged and still refers to the original object
- B. It now refers to the new Person
- C. It becomes null
- D. The code does not compile
Show answer
A. Reassigning the parameter only changes the method's local copy of the reference. The caller's variable still points to the original object.
Interview questions
Is Java pass-by-value or pass-by-reference?
Show answer
Pass-by-value, always. When you pass a primitive, the method gets a copy of the number. When you pass an object, the method gets a copy of the reference. Because that copy points to the same object, the method can change the object's fields — which looks like pass-by-reference — but it cannot make the caller's variable refer to a different object, which true pass-by-reference would allow.
Why can you not write a method that swaps two int variables in Java?
Show answer
Because the method receives copies of the two values. Swapping the copies has no effect on the variables in the caller, and Java has no way to pass the variables themselves. To get the effect, wrap the values in something the method can modify, such as an array or an object, or return the new values.
Key Points to Remember
- Java is always pass-by-value — there is no pass-by-reference in Java, ever.
- For primitives, a copy of the actual value is passed.
- For objects, a copy of the reference (address) is passed — both copies point to the same object, so field mutations are visible to the caller.
- Reassigning the parameter inside a method only changes the local copy of the reference; the caller's original reference is never affected.
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering