java is pass-by-value
always. even for objects. here's the proof, step by step.
- chapter
- chapter 02.12
- reading time
- 6 min read
java always passes arguments by value: the method gets a copy of whatever is in the caller's variable. for a primitive, that's a copy of the number. for an object, the variable holds a reference, so the method gets a copy of the reference: it can change the object both arrows point at, but it can never make the caller's variable point somewhere else.
the picture
you know how a method can change an object you pass it, but can't swap two ints? people explain that as "primitives by value, objects by reference". it's a tempting story, and it's wrong. there's one rule, and it covers both.
think of a house and its address on a slip of paper. passing a variable to a method is photocopying the slip and handing over the copy. the method can drive to the house and repaint it (you'll see the new paint, it's the same house). it can scribble a different address on its photocopy. that changes nothing on your slip. your slip still says where your house is.
the proof, step by step
three methods, three experiments. swap gets two ints, fill changes an object's field, replace points its parameter at a new object.
- 1public class Main {
- 2 static class Box {
- 3 int value;
- 4 Box(int value) { this.value = value; }
- 5 }
- 6
- 7 static void swap(int a, int b) {
- 8 int temp = a;
- 9 a = b;
- 10 b = temp;
- 11 }
- 12
- 13 static void fill(Box b) {
- 14 b.value = 99;
- 15 }
- 16
- 17 static void replace(Box b) {
- 18 b = new Box(-1);
- 19 }
- 20
- 21 public static void main(String[] args) {
- 22 int x = 1, y = 2;
- 23 swap(x, y);
- 24 Box box = new Box(7);
- 25 fill(box);
- 26 replace(box);
- 27 System.out.println(x + " " + y + " " + box.value);
- 28 }
- 29}
·main starts.
replace moved only its own arrow, and the new Box turns into garbage the moment it returns.the program prints 1 2 99, and every piece of that is the same rule:
swap(x, y):aandbare copies of 1 and 2. swapping copies swaps copies.fill(box):bis a copy of the reference. copy or not, it leads to the sameBox, andb.value = 99changes thatBox.replace(box):b = new Box(-1)changes whatbholds.bis a copy, soboxin main never moves.
fill, two separate variables in two separate frames, holding two copies of the same reference. one object. change the object and both see it; change a variable and only that variable changes.what "by reference" would mean
"pass by reference" is a real thing in other languages: the parameter isn't a copy at all, it's another name for the caller's variable. C++ has it (int&), C# has it (ref). java doesn't, for anything.
a and b are separate slots holding copies. swapping them is real, but it happens to the copies, and they're thrown away when swap returns.
void shout(String s) {
s = s.toUpperCase();
}
void main() {
String name = "duke";
shout(name);
IO.println(name);
}void reset(int[] arr) {
arr[0] = 99;
arr = new int[0];
}
void main() {
int[] nums = {1, 2, 3};
reset(nums);
IO.println(Arrays.toString(nums));
}so how do you "return" more than one thing?
since a method can't reassign the caller's variables, you hand results back instead:
record Pair(int first, int second) {}
Pair swap(int a, int b) {
return new Pair(b, a);
}
void main() {
var p = swap(1, 2);
IO.println(p.first() + " " + p.second());
}2 1
return a value (or a small record holding several), and let the caller decide what to assign. it's also easier to read: you can see the change happening at the call site. the "mutate an object you were passed" style works too, and the JDK uses it (Arrays.sort(a)), but it's easy to surprise people with. immutability is the story of avoiding those surprises.
- java passes everything by value: the parameter is a new variable holding a copy.
- for objects, the copy is a copy of the reference. both arrows lead to one object.
- changing the object through the parameter is visible to the caller.
- re-pointing the parameter (
param = new ...) never is. - want more than one result? return it, in a record if needed.