Back to Blog
Java

Java Pass by Value: How It Really Works

Java is always pass-by-value. Learn how primitives and object references behave when passed to methods, with code examples and practical implications.

Javamethod parametersobject referencesparameter passingJava memory model
Diagram showing a method parameter holding a copy of an object reference, illustrating Java's pass-by-value behavior.

Java is always pass-by-value. Many developers believe objects are passed by reference because a method can modify an object's fields and the caller sees those changes. That behavior is real, but it is not evidence of pass-by-reference. When you pass an object to a method, Java passes the object's reference value, not the object itself. This article explains how pass-by-value works for primitives and object references and why the distinction matters in day-to-day method design.

The Common Misconception

The confusion usually comes from the word “reference.” In Java, an object variable actually holds a reference to an object. When that variable is passed to a method, the method receives a copy of the reference. Both the caller and the method now hold references to the same object, which is why field changes made inside the method are visible to the caller. The parameter is a second reference to the same object, not a second object.

How Primitives Are Passed by Value

When you call a method with a primitive argument, Java copies the actual value into the parameter. Changes to the parameter inside the method have no effect on the original variable.

public static void increment(int number) { number++; System.out.println("Inside method: " + number); } public static void main(String[] args) { int value = 5; increment(value); System.out.println("After call: " + value); }

The output is:

Inside method: 6
After call: 5

The increment method works on a copy of value. The original value remains 5. The same rule applies to objects, except the value copied is a reference.

How Object References Are Passed by Value

When you pass an object to a method, Java copies the reference value stored in the caller's variable into the parameter. The parameter and the caller's variable now both refer to the same object. This is why modifying the object's fields inside the method affects the caller.

public class Person { public String name; public Person(String name) { this.name = name; } } public static void rename(Person p) { p.name = "Alice"; } public static void main(String[] args) { Person person = new Person("Bob"); rename(person); System.out.println(person.name); // Alice }

Here, rename receives a copy of the reference stored in person. That copy still points to the same Person object, so changing p.name changes the object that person refers to. The reference itself is passed by value; the object is not copied.

Why Reassigning a Parameter Does Not Affect the Caller

A common mistake is to try to replace the object by assigning a new one to the parameter. Because the parameter is a copy of the reference, reassigning it only changes which object the parameter refers to. The caller's variable still refers to the original object.

public static void replace(Person p) { p = new Person("Charlie"); } public static void main(String[] args) { Person person = new Person("Bob"); replace(person); System.out.println(person.name); // Bob }

The replace method creates a new Person and assigns it to the local parameter p. This does not affect the person variable in main. The original object remains unchanged. This behavior follows directly from pass-by-value: the method receives a copy of the reference, not the reference itself.

Changing Which Object the Caller References

If you need the caller's variable to refer to a different object after a method call, you cannot do that by reassigning the parameter. The clearest approach is to return the new object and assign it in the caller.

public static Person createRenamed(String newName) { return new Person(newName); } public static void main(String[] args) { Person person = new Person("Bob"); person = createRenamed("Alice"); System.out.println(person.name); // Alice }

This makes the reassignment explicit and avoids confusion about the method's effect. Less readable alternatives include mutable holder objects, such as a single-element array, but they are rarely worth the added complexity.

Implications for API Design and Maintainability

Because a method can mutate the object it receives, be clear about whether your method changes the state of that object. If it does, document the mutation. If it does not need to mutate the object, prefer keeping the method side-effect-free or making the object immutable. When a caller needs to replace an object, return the new object instead of expecting parameter reassignment to propagate.

Pass-by-Value, Performance, and Memory

Passing primitive arguments copies their values, which is cheap. Passing object arguments copies only the reference value, not the entire object, so even large objects can be passed to methods without copying their data. If you intentionally copy objects or collections inside a method, that copy has its own memory and CPU cost, but pass-by-value itself does not.

What This Means for Debugging and Testing

When you debug a method that receives an object, remember that the caller's variable and the method's parameter are two different references to the same object. If the method mutates the object through its parameter, the caller sees the same mutated object. This can help you track state changes, but it can also hide bugs when a method unexpectedly mutates shared state. Writing unit tests that verify an object's state after a method call is a reliable way to catch unintended modifications.

How Java Pass-by-Value Works for Primitives and Object References | RYUSLOG DEV