J David Eisenberg
Posted on March 23, 2022
“Any headline that ends in a question mark can be answered by the word ‘no’.”
-- Betteridge’s Law of Headlines
When you first learned about methods in Java, you might have read something like “arguments are always passed to parameters by value.” This means that a parameter is always a copy of the argument. You can change the value of the parameter all you want, and the original argument will remain unaffected. In this code:
public class ByValue1 {
public static void printCube(int n) {
n = n * n * n;
System.out.println("The cube is " + n);
}
public static void main(String[] args) {
int n = 12;
System.out.println("Before call, n is " + n);
printCube(n);
System.out.println("After call, n is " + n);
}
}
The output shows that n
in main()
remains unchanged:
Before call, n is 12
The cube is 1728
After call, n is 12
This is a good thing; imagine calling printCube(3);
you certainly wouldn’t want Java to change the literal 3 to 27!
Further along in your adventures with Java, you encountered arrays, and may have seen a program like this:
import java.util.Arrays;
public class ByValue2 {
public static void cubeArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
arr[i] = arr[i] * arr[i] * arr[i];
}
}
public static void main(String[] args) {
int [] data = {10, 2, 4};
System.out.println("Before call, data is " +
Arrays.toString(data));
cubeArray(data);
System.out.println("After call, data is " +
Arrays.toString(data));
}
}
When you run this program, you get this output:
Before call, data is [10, 2, 4]
After call, data is [1000, 8, 64]
What’s going on here? The array contents have changed! Is this an exception to “always call by value“? No, it isn’t. The key point here is that arrays are references. Here’s what a memory diagram looks like when we enter the cubeArray
method:
When doing the multplication arr[i] * arr[i] * arr[i]
, Java is changing element i
in the memory that arr
refers to; it is not changing arr
itself. We can prove this by printing out the reference, which will show the memory address of the arrays:
import java.util.Arrays;
public class ByValue3 {
public static void cubeArray(int[] arr) {
System.out.println("At start of cubeArray, arr is "
+ arr);
for (int i = 0; i < arr.length; i++) {
arr[i] = arr[i] * arr[i] * arr[i];
}
System.out.println("At end of cubeArray, arr is "
+ arr);
}
public static void main(String[] args) {
int [] data = {10, 2, 4};
System.out.println("Before call, data is " +
Arrays.toString(data) + " reference " + data);
cubeArray(data);
System.out.println("After call, data is " +
Arrays.toString(data) + " reference " + data);
}
}
When you print an array with System.out.println
, you will get something like [I@764c12b6
, which means you have an array ([
) of integers (I
) at memory location 764c12b6 (@764c12b6
). Running the program shows that the references have never changed, but the referred-to array has:
Before call, data is [10, 2, 4] reference [I@764c12b6
At start of cubeArray, arr is [I@764c12b6
At end of cubeArray, arr is [I@764c12b6
After call, data is [1000, 8, 64] reference [I@764c12b6
So, in fact, this program doesn’t settle the question of call by value one way or the other, since nothing here has changed the reference arr
itself. Let’s put that to the test by doing something that will change arr
—we’ll set arr
to refer to a brand new array:
public class ByValue4 {
public static void cubeArray(int[] arr) {
System.out.println("At start of cubeArray, arr is "
+ arr);
for (int i = 0; i < arr.length; i++) {
arr[i] = arr[i] * arr[i] * arr[i];
}
// make arr refer to a different area of memory
arr = new int[4];
System.out.println("At end of cubeArray, arr is "
+ arr);
}
public static void main(String[] args) {
int [] data = {10, 2, 4};
System.out.println("Before call, data is " +
Arrays.toString(data) + " reference " + data);
cubeArray(data);
System.out.println("After call, data is " +
Arrays.toString(data) + " reference " + data);
}
}
Here’s the memory diagram just before we leave cubeArray()
:
If call by value really works everywhere, the reference to data
in main()
should not change. Let’s see the output:
Before call, data is [10, 2, 4] reference [I@764c12b6
At start of cubeArray, arr is [I@764c12b6
At end of cubeArray, arr is [I@4e0e2f2a
After call, data is [1000, 8, 64] reference [I@764c12b6
The third line of the output shows that we changed the arr
reference (the parameter) in the cubeArray()
method, but the fourth line shows that the original data
reference in main()
was not affected. The reference is, indeed, passed by value.
Thus, the answer to the article headline: Do Arrays in Java Violate “Call by Value”? The answer is “no.“ Java is always call by value.
Posted on March 23, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.