Back to Blog
Java

Java Pass by Value: How It Really Works

java pass by value: Understand why Java is always pass-by-value, including object references, with clear code examples and practical implications for method design.

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 pass by value requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

The Common Misconception About Java's Parameter Passing

Many developers believe Java uses pass-by-reference for objects because when you pass an object to a method, the method can modify its fields and those changes are visible to the caller. That behavior is real, but it is not evidence of pass-by-reference. Java is strictly pass-by-value. The confusion arises because the value being passed for an object is the reference to that object, not the object itself. This article explains exactly how java pass by value works for both primitives and object references, and why the distinction matters in your daily code.

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. This is straightforward, but the same principle applies to object references.

How Object References Are Passed by Value

When you pass an object to a method, Java copies the reference variable's value—the memory address that points to the object—into the parameter. The parameter and the caller's variable now both point 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 where that copy points. The caller's variable still points 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 is a direct consequence of pass-by-value: the method receives a copy of the reference, not the reference itself.

Modifying Objects Through Method Parameters

If you need a method to change the object that a caller's variable points to, you cannot do it with a simple parameter assignment. You have a few options:

  • Return the new object and assign it in the caller.
  • Use a mutable container, such as an array or a wrapper class, to hold the reference.
  • Modify the object's fields rather than replacing the reference.

Returning the new object is usually the clearest approach.

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 any confusion about what the method is doing. The other approaches, like using a single-element array, work but are less readable and should be reserved for rare cases.

Implications for API Design and Maintainability

Understanding pass-by-value helps you design cleaner method signatures. If a method modifies an object's state, document that behavior. If a method does not need to modify the object, consider passing a copy or making the object immutable. This reduces surprising side effects.

When you know that reassigning a parameter has no effect on the caller, you can avoid writing methods that try to do so. Instead, return the new object. This keeps the data flow explicit and makes the code easier to follow.

When Pass-by-Value Affects Performance and Memory

Pass-by-value means that every method call copies the value of the argument. For primitives, this is cheap. For object references, the copy is just the reference, not the entire object. So passing a large object to a method does not copy the object's data; it only copies the pointer. This is efficient.

However, if you are not careful, you might accidentally create copies of objects when you intended to share them. For example, using the clone method or copying collections inside a method can increase memory usage and processing time. Pass-by-value itself does not cause performance problems; the problem is when you treat object parameters as if they were the objects themselves and perform expensive operations on them.

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 you see a change in one, it will appear in the other. This can be helpful for tracking state changes, but it can also hide bugs if you forget that the method is mutating shared state. Writing unit tests that verify the state of the object after a method call is a reliable way to catch unintended modifications.

java pass by value: Practical Usage and Code Examples | RYUSLOG DEV