JB Header

Is Java Pass-by-Value or Pass-by-Reference?

Introduction "Is Java pass-by-value or pass-by-reference?" is one of the most frequently asked - and most frequently mis-answered - Java questions. The short answer is: Java is always pass-by-value. But when the value being passed is a reference to an object, the results can look like pass-by-reference, which is where the confusion comes from. This tutorial settles the question with examples. We will start by defining the two terms. We will then pass primitives to a method, and then object references. Next we will run the experiment that proves Java is not pass-by-reference - the swap test. Lastly, we will look at how immutable objects such as String fit in, and what this means for the methods you write.

(Note - This tutorial builds on Java MethodsClick to read tutorial: Java Methods: Defining, Calling and Returning Values, and on the idea that array and object variables hold references, from Java ArraysClick to read tutorial: Java Arrays Tutorial: Declare, Create and Initialize.) Pass-by-value vs pass-by-reference
  • Pass-by-value: the method receives a copy of the argument's value in its parameter. Assigning a new value to the parameter has no effect on the caller's variable.
  • Pass-by-reference: the parameter becomes an alias for the caller's variable itself. Assigning to the parameter changes the caller's variable. (C++ reference parameters and C# ref parameters work this way.)
In Java, every argument is passed by value. The key is to know what the value is -
  • for a primitive variable, the value is the number, character or boolean itself;
  • for an object variable (including arrays and strings), the value is a reference - something like the object's address. The object itself is never copied.
Passing primitives
Passing a primitive to a method
//PassPrimitive.java
public class PassPrimitive {
  static void addBonus(int score) {
    score = score + 10;
    System.out.println("  inside addBonus: score = " + score);
  }

  public static void main(String[] args) {
    int score = 70;
    addBonus(score);
    System.out.println("after addBonus: score = " + score);
  }
}
 OUTPUT of the above code
  inside addBonus: score = 80
after addBonus: score = 70
The parameter score received a copy of 70. Changing the copy to 80 did not affect the caller's score - even though the two variables have the same name, they are different variables in different stack frames. To get the new value back, the method must return it: score = addBonus(score);.
Passing object references When an object is passed, the parameter receives a copy of the reference. The caller's variable and the parameter now point to the same object, so the method can change that object's contents, and the caller will see the change.
Passing an array and an object to a method
//PassReference.java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class PassReference {
  static void doubleAll(int[] values) {
    for (int i = 0; i < values.length; i++) {
      values[i] *= 2;                 // changes the shared array
    }
  }

  static void addGuest(List<String> guests) {
    guests.add("Nancy");              // changes the shared list
  }

  public static void main(String[] args) {
    int[] prices = {10, 20, 30};
    doubleAll(prices);
    System.out.println("prices after doubleAll: " + Arrays.toString(prices));

    List<String> guests = new ArrayList<>(List.of("Tom", "Harry"));
    addGuest(guests);
    System.out.println("guests after addGuest : " + guests);
  }
}
 OUTPUT of the above code
prices after doubleAll: [20, 40, 60]
guests after addGuest : [Tom, Harry, Nancy]
This is why many people think Java passes objects by reference. But look closely: the method changed the object the reference points to. It did not change the caller's variable. The next experiment shows the difference. The swap test - proof that Java is pass-by-value With real pass-by-reference, a method could swap two of the caller's variables. In Java, it cannot - and the same is true of reassigning a parameter to a new object.
Swapping and reassigning reference parameters
//SwapTest.java
import java.util.ArrayList;
import java.util.List;

public class SwapTest {
  static void swap(StringBuilder a, StringBuilder b) {
    StringBuilder temp = a;
    a = b;
    b = temp;
    System.out.println("  inside swap: a = " + a + ", b = " + b);
  }

  static void replaceList(List<String> list) {
    list = new ArrayList<>();         // the parameter now points to a NEW list
    list.add("Ethan");
    System.out.println("  inside replaceList: " + list);
  }

  public static void main(String[] args) {
    StringBuilder first = new StringBuilder("first");
    StringBuilder second = new StringBuilder("second");
    swap(first, second);
    System.out.println("after swap: first = " + first + ", second = " + second);

    List<String> names = new ArrayList<>(List.of("Tom"));
    replaceList(names);
    System.out.println("after replaceList: " + names);
  }
}
 OUTPUT of the above code
  inside swap: a = second, b = first
after swap: first = first, second = second
  inside replaceList: [Ethan]
after replaceList: [Tom]
Explanation of the code
  • Inside swap(), the parameters a and b were swapped - but they are only copies of the references. The caller's first and second still point to the same objects as before.
  • replaceList() pointed its parameter at a brand-new list. The caller's names still points to the original list, which was never touched.
  • If Java were pass-by-reference, both changes would have been visible to the caller. They were not, which proves that Java passes references by value.
A useful way to remember it: a method can change the object a reference points to, but not which object the caller's variable points to.
What about String and other immutable objects? A String is passed like any object - as a copy of its reference. But strings are immutableClick to read tutorial: Java String Basics and Why Strings Are Immutable, so there is no method which can change the shared object. Anything that looks like a change creates a new String, and assigning it to the parameter only changes the parameter. The same is true for the wrapper classes, such as Integer.
Passing a String to a method
//PassString.java
public class PassString {
  static void shout(String message) {
    message = message.toUpperCase() + "!";
    System.out.println("  inside shout: " + message);
  }

  static String shoutAndReturn(String message) {
    return message.toUpperCase() + "!";
  }

  public static void main(String[] args) {
    String message = "hello";
    shout(message);
    System.out.println("after shout: " + message);
    message = shoutAndReturn(message);
    System.out.println("after shoutAndReturn: " + message);
  }
}
 OUTPUT of the above code
  inside shout: HELLO!
after shout: hello
after shoutAndReturn: HELLO!
To change a string - or any immutable value - in the caller, return the new value and assign it there, as shoutAndReturn() does. What this means for your methods
  • Return new values for primitives and immutable objects. A method cannot change the caller's variables.
  • Be careful when changing objects you are given. Changes to a passed array or list affect the caller, which can surprise them. Document it if a method does this, or work on a copy instead.
  • Make parameters final if it helps. Declaring a parameter final prevents accidentally reassigning it, which, as we saw, never affects the caller anyway.
Summary In this tutorial we first defined pass-by-value, where a method receives a copy of each value, and pass-by-reference, where a parameter is an alias for the caller's variable. We then saw that passing a primitive copies its value, so the caller's variable cannot be changed. Next we passed arrays and lists, whose references are copied, so a method can change the shared object. We then ran the swap test and the reassignment test, which prove that Java passes references by value. Lastly, we saw how immutable objects such as String behave, and what this means for the methods you write. Java is always pass-by-value. In the next tutorial, we will define several methods with the same name using method overloadingClick to read tutorial: Method Overloading in Java with Examples.