Java Pass-by-Value
›Short versionOpen if you only have a minute.
- Java passes a copy of each argument value. The parameter is a different variable from the caller’s.
- For an object, the copied value is the reference. Both variables identify the same heap object.
- Assigning to the parameter reseats only the callee’s variable. The caller’s variable is unchanged.
- Writing a field, or an array element, mutates the shared object. The caller sees that write.
- null is a reference value. Copying it does not allocate an object.
- To make the caller’s variable identify a new object, return the reference and assign it in the caller.
Two account ids need to trade places. The method uses a temporary variable, the ordinary swap. After the call, the caller’s variables still hold the original ids. The exchange happened to the copies.
Java is . The value that was copied for each String is a . Assigning a = b inside the method the parameter. It does not write the caller’s variable. That is the whole rule. The rest of this page is what the rule does to primitives, to mutable objects, and to String.
A copied number
A argument contributes its bits. change receives a copy of 10 in x. x = 99 stores 99 in that parameter. a is a different variable in the caller’s frame, and it still holds 10.
static void change(int x) {
x = 99;
}
int a = 10;
change(a);
System.out.println(a); // 10There is no second channel that would let change reach back and store into a. If the caller should keep the new number, the method returns it and the caller assigns it: a = change(a), with change written to return the updated int.
A copied reference
new Account() allocates the object on the and stores a reference in account. The call rename(account) copies that reference into the parameter. The parameter and account are two variables holding the same reference, so they identify one object.
account.name = "Asha" inside rename is a . It writes a field of the object. The caller’s variable was not assigned, and it still identifies that object, so the next read of account.name is Asha.
replace does the other operation. account = new Account() stores a reference to a second object into the parameter. The caller’s variable still identifies the first object, whose name is still Asha. The object named Ravi is reachable only from the parameter, and that variable disappears when replace returns.
public class Account {
String name;
static void rename(Account account) {
account.name = "Asha";
}
static void replace(Account account) {
account = new Account();
account.name = "Ravi";
}
public static void main(String[] args) {
Account account = new Account();
account.name = "Mira";
rename(account);
System.out.println(account.name);
replace(account);
System.out.println(account.name);
}
}$ javac Account.java$ java AccountAshaAsha
Arrays are objects. nums[0] = 7 mutates the array both variables identify. nums = new int[] { 7, 8, 9 } reseats the parameter. After the reassignment, the caller’s array variable still identifies the original array, element by element.
static void mutate(int[] nums) {
nums[0] = 7;
}
static void reseat(int[] nums) {
nums = new int[] {7, 8, 9};
}
int[] data = {1, 2, 3};
reseat(data);
System.out.println(data[0]); // 1
mutate(data);
System.out.println(data[0]); // 7Strings, null, and the way back
A String variable holds a reference. note = note + "!" builds a new String and stores that new reference in the parameter. That is a reseat of the parameter. The caller’s variable still identifies the original string. String objects do not change their characters, so there is no field write on the original that would show up in the caller. The familiar “strings don’t change when you pass them” result is this rule plus immutability, not a third passing mechanism.
static void shout(String note) {
note = note + "!";
}
String message = "hello";
shout(message);
System.out.println(message); // hellonull is a reference value. Passing it copies null into the parameter. The body then has a parameter that identifies no object. Reading account.name through that null throws NullPointerException. Assigning new Account() to the parameter allocates an object the caller’s variable still does not identify. The caller’s variable remains null.
When the method’s job is to hand the caller a different object, return the reference. The assignment has to happen in the caller, because that is the variable that must be reseated.
static Account withName(String name) {
Account created = new Account();
created.name = name;
return created;
}
Account account = null;
account = withName("Ravi");The swap of two ids fails for the same reason replace fails. The method can exchange its own parameters. The caller’s two variables are not those parameters. A swap the caller can observe has to return both results, or mutate fields inside one shared object that holds both ids. There is no parameter form in Java that passes the location of a variable so the callee can assign it.
replace allocates a new Account, sets its name to Ravi, and returns nothing. The caller printed Asha before the call. What does account.name print after replace returns?
Asha. replace stored the new reference in its parameter. The caller’s account variable still identifies the object rename already set to Asha. Ravi’s object was only reachable from the parameter, and that frame is gone. Assigning the result of a method that returns the new Account would reseat the caller’s variable, because the assignment would be written in the caller.