8BitMiscreant

The Illusion of pass-by-reference in Java

TLDR: Java is always pass-by-value! The only difference is what's being passed a primitive value or a reference to an object.

You might've noticed that sometimes assignments or passing variables to a function feels inconsistent. Sometimes, assigning one variable to another creates an independent copy like:

int a = 5;
int b = a;

a = 10;
System.out.println("a: " + a); // a: 10
System.out.println("b: " + b); // b: 5; b is independent of a

Whereas, other times it seems to pass a reference:

int[] arr1 = {1,2,3};
int[] arr2 = arr1;
arr1[0] = 11;
System.out.println("arr1: " + Arrays.toString(arr1)); // arr1: [11, 2, 3]
System.out.println("arr2: " + Arrays.toString(arr2)); // arr2: [11, 2, 3]

This makes it look like sometimes Java is pass-by-value whereas other times it's pass-by-reference. But, Java is always pass-by-value. The only difference is what's being passed a primitive value or a reference to an object.

Primitives

For primitives, Java always copies the actual value directly as shown in the first example. Hence, both variables are independent of one another. Another example follows:

// This method receives a copy of the value, therefore the original value
// remains unchanged.
void incrementInt(int var) {
    var += 10;
}

void sampleFunc() {
    int var = 1;
    incrementInt(var);
    System.out.println("var: " + var); // var: 1
}

When incrementInt(var) is invoked by the sampleFunc(), it create a local copy of var(=1) and performs the operation on it unlike C/C++ where you have the option of passing-by-reference.

Objects - Still pass-by-value

In case of objects, it is still pass-by-value, but the value being "passed" is actually the reference to that object. Hence, this allows a shared mutation of object state and is not actually pass-by-reference.

// Since arrays are objects in Java, hence their reference is passed-by-value.
// Thus, modifying elements will change the original array as well.
void incrementIntArray(int[] arr) {
    // Increment the first element by 10
    arr[0] += 10;
}

void sampleFunc() {
    int[] arr = {0, 1, 2, 3, 4};
    incrementIntArray(arr);
    System.out.println("arr: " + Arrays.toString(arr)); // arr: [10, 1, 2, 3, 4]
}

In the above example, when incrementIntArray(arr) is being invoked by sampleFunc(), it the value being passed is the reference to arr, hence the increment being within incrementIntArray() is actually being done to the arr in sampleFunc().