Pass by Value in Java

Java always passes arguments by value. For objects the value copied is the reference, which explains every result that looks like pass by reference.

The rule

Java is always pass by value. There is no exception and no reference passing mode. What varies is what the value is: for a primitive it is the number itself, and for an object it is the reference.

Primitives

public static void increase(int number) {
    number = number + 10;      // changes the local copy only
}

int score = 5;
increase(score);
System.out.println(score);     // 5

The method received a copy of the value 5. Reassigning that copy cannot reach the caller.

Objects: the part that confuses everyone

public static void rename(StringBuilder text) {
    text.append(" updated");   // changes the object both names point to
}

StringBuilder message = new StringBuilder("draft");
rename(message);
System.out.println(message);   // draft updated

This looks like pass by reference, but it is not. The method received a copy of the reference. Both copies point at the same object, so a change made through either one is visible to both.

The test that settles it

public static void replace(StringBuilder text) {
    text = new StringBuilder("something else");   // repoints the local copy
}

StringBuilder message = new StringBuilder("draft");
replace(message);
System.out.println(message);   // draft - unchanged
If Java were pass by reference, reassigning the parameter would replace the caller variable too. It does not. That single example is the clearest proof, and it is worth remembering for interviews.

A picture of what happens

caller:  message ---> [ StringBuilder: "draft" ]
                            ^
method:  text ---------------|      (a second reference, same object)

text.append(...)   changes the object       -> caller sees it
text = new ...     changes only the arrow   -> caller sees nothing

Why immutability changes the feel

public static void shout(String word) {
    word = word.toUpperCase();   // creates a new String, rebinds the local copy
}

String greeting = "hello";
shout(greeting);
System.out.println(greeting);    // hello

String has no method that changes its contents, so the only thing a method can do is rebind its own copy. That is why strings feel like primitives when passed around, even though they are objects.

Arrays

public static void zeroFirst(int[] data) {
    data[0] = 0;                 // visible to the caller
    data = new int[]{9, 9, 9};   // not visible to the caller
}

An array is an object, so the same two rules apply: changing an element is shared, replacing the array is not.

Returning results instead

// Instead of trying to modify the parameter
public static int increased(int number) {
    return number + 10;
}

int score = increased(5);   // 15

Java has no output parameters, so a method that must produce a new value should return it. When several values are needed, return a small record.

record Split(int quotient, int remainder) { }

public static Split divide(int a, int b) {
    return new Split(a / b, a % b);
}

Common mistakes

  • Believing Java passes objects by reference. It passes references by value, which is a different statement.
  • Expecting a swap method to work. In Java it cannot swap the caller variables.
  • Modifying a caller collection inside a method as a hidden side effect.
  • Assuming a final parameter makes the object immutable. It only prevents rebinding the parameter.

Best practices

  • Prefer returning new values over modifying arguments.
  • If a method does modify an argument, say so in the name and in the documentation.
  • Copy a mutable argument defensively when the object must not be shared.
  • Use records to return more than one value instead of filling an array supplied by the caller.

Practice

  1. Write a swap(int a, int b) method and explain why it cannot work as intended.
  2. Predict the output when a method calls list.add("x") and then list = new ArrayList<>().
  3. Why does passing a String feel different from passing a StringBuilder?
  4. Rewrite a method that fills a caller supplied array so that it returns a new array instead, and state one advantage.

Conclusion

Java copies the argument, always. For objects the copy is the reference, so shared changes travel and reassignment does not. Reassigning a parameter is the experiment that proves it.

Written by Lorens Mishra

Default administrator account created by the installer.

Continue reading

All Java notes →
Java

Methods in Java

A method groups a piece of behaviour behind a name, a parameter list and a return type, and that signature is the contract callers depend on.

Read more
Java

Recursion in Java

A recursive method calls itself on a smaller problem, and it works only when a base case guarantees the shrinking stops.

Read more
Java

Varargs in Java

A variable arity parameter lets a method accept any number of arguments, and inside the method it is simply an array.

Read more

Discussion

0 comments
Sign in to join the discussion.

No comments yet. Be the first to say something.