Call by Value and Call by Reference in Java

Is Java pass-by-value or pass-by-reference? The answer is unambiguous: Java is always pass-by-value. The confusion comes from how object references behave β€” a reference is a value too, and it is that value which gets copied.

Pass-by-value in plain terms

When you pass a variable to a method, Java copies the value of that variable into the method's parameter. Changes to the parameter never affect the caller's variable β€” only the object the reference points to can be mutated.

Primitives: no surprise

public static void doubleIt(int x) {
    x = x * 2;
    System.out.println("Inside:  " + x);
}

public static void main(String[] args) {
    int n = 10;
    doubleIt(n);
    System.out.println("Outside: " + n);
}
// Inside:  20
// Outside: 10

The parameter x receives a copy of n. Modifying x is local to the method β€” exactly what "pass by value" means.

Objects: the tricky part

class User {
    String name;
    User(String name) { this.name = name; }
}

public static void rename(User u) {
    u.name = "Bob"; // mutates the shared object
}

public static void replace(User u) {
    u = new User("Charlie"); // rebinds the local parameter only
}

public static void main(String[] args) {
    User alice = new User("Alice");

    rename(alice);
    System.out.println(alice.name); // Bob

    replace(alice);
    System.out.println(alice.name); // Bob (still) β€” replace had no effect outside
}

What's happening?

  • alice holds the value 0x3f21 (a reference to a User object on the heap).
  • When calling rename, Java copies that reference into the parameter u. Both variables now point to the same heap object.
  • u.name = "Bob" mutates the object β€” visible through both u and alice.
  • u = new User("Charlie") rebinds the local u to a fresh reference. alice never changes.

If Java were pass-by-reference

This is a quick thought experiment β€” something Java explicitly does not do:

// Hypothetical pass-by-reference β€” not legal Java
public static void swap(int& a, int& b) { int t = a; a = b; b = t; }

int x = 1, y = 2;
swap(x, y);
// With real pass-by-reference: x == 2, y == 1
// In actual Java, you cannot swap two ints via a method at all.

C++ and C# (with the ref keyword) support true pass-by-reference. Java does not.

Arrays behave like objects

Arrays are objects in Java, so they follow the same rule as any other reference:

public static void modify(int[] arr) {
    arr[0] = 99;     // visible outside β€” mutation through the shared reference
    arr = new int[]{1, 2, 3}; // rebind local only β€” caller sees no change
}

public static void main(String[] args) {
    int[] numbers = {10, 20, 30};
    modify(numbers);
    System.out.println(numbers[0]); // 99
}

Common workarounds

Returning the new value

The cleanest solution β€” a method that computes a new value should return it:

public static int doubleIt(int x) { return x * 2; }

int n = 10;
n = doubleIt(n); // n == 20

Wrapping in an array or container

When you genuinely need to mutate a primitive argument (rare), wrap it in a one-element array or an AtomicInteger:

public static void increment(int[] holder) { holder[0]++; }

int[] counter = {0};
increment(counter);
System.out.println(counter[0]); // 1

Returning multiple values

Since Java 16, use a record to return structured data without designing a class:

record Stats(int min, int max, double avg) { }

public static Stats analyze(int[] data) {
    // compute ... 
    return new Stats(min, max, avg);
}

Quick reference

Type passedCan be reassigned?Can be mutated?
Primitive (int, boolean, etc.)No effect outsideN/A
Object / Array (reference)No effect outsideYes β€” visible to caller
String, Integer, other immutablesNo effect outsideNo (class is immutable)

Keeping "Java is pass-by-value β€” even for objects" in mind eliminates the majority of surprises. If a method needs to modify the caller's variable, return a new value; don't try to rebind the parameter.