Course topics

By WebNest Studio Engineering Team

Java - Core Tutorial

Call By Value in Java

One of the most persistent myths in Java is that objects are "passed by reference." They are not. Java is, without exception, strictly pass-by-value — every single time you call a method, Java copies the value stored in the argument and hands that copy to the method. What differs between primitives and objects is what that "value" actually is.

For a primitive (int, double, boolean, and so on), the value is the literal data itself, so the method receives an independent copy of the number. For an object, the variable never holds the object — it holds a reference (essentially a memory address pointing to the object on the heap). When you pass an object variable to a method, Java copies that reference, not the object. The method now has its own copy of the address, but that copy still points to the exact same object in memory.

Why it matters

Whether a method can change the caller's data is one of the most misunderstood parts of Java, and a frequent source of bugs. The rule is a single sentence — Java always passes a copy of the value — but because the value of an object variable is a reference, the effect looks different for primitives and for objects.

caller: main() emp method: update(e) e (copy) Employee name = "Asha" new Employee only e sees this e.name = "X" changes this object e = new ... emp is unaffected
The parameter is a copy of the reference. Both point to the same object, so changing a field is visible to the caller; reassigning the parameter is not.

Watch: How Call By Value Works in Java

A quick look at how Java hands every method a copy of its arguments, and why that copy is a value for primitives but a reference for objects.

Watch on YouTube

Why Mutating Fields "Looks Like" Pass-by-Reference

Because the copied reference points to the same heap object, calling a method that changes the object's fields through that reference (e.g. obj.setName("New")) really does change the object the caller sees — there is only one object, and both the caller's reference and the parameter's reference point to it. This is the source of the confusion: people see the caller's object change and assume Java passed the object itself. In reality, Java passed a copy of the pointer to that one shared object.

Why Reassigning the Parameter Does Not Affect the Caller

Reassigning the parameter inside the method (e.g. param = new Employee("Someone Else")) only changes what the local copy of the reference points to. It does not — and cannot — reach back and change the caller's original variable, because the caller's variable is a completely separate copy of the reference. Once the method returns, the caller's variable still points to the original object, unaffected by the reassignment.

Primitives vs Objects — the Same Rule, Two Effects

The underlying rule never changes: a copy of the value is passed. It only looks different because a primitive's "value" is the data, while an object variable's "value" is an address. Keeping this single mental model — "always a copy of the value, and for objects that value is an address" — resolves nearly every confusing case you will encounter with method parameters in Java.

  • Passing a primitive: the method gets a copy of the number; changes to the parameter never affect the caller's variable.
  • Passing an object and mutating its fields: both references point to the same object, so the change is visible to the caller.
  • Passing an object and reassigning the parameter: only the local copy of the reference changes; the caller's reference is untouched.

What Happens in Memory During a Call

Each method call gets its own stack frame, and every parameter is a brand-new local variable in that frame. When the call starts, Java copies the argument's value into the parameter. When the call ends, the frame and all its parameters are thrown away. Objects live separately on the heap, so they survive the call. That is why a change made through a reference outlives the method, while a change made to the parameter variable itself disappears with the frame.

The Classic Test: Why a swap Method Fails

A swap(int a, int b) method that exchanges its two parameters looks correct but changes nothing for the caller, because it only swaps the two local copies. The same is true for swap(Employee a, Employee b): it swaps the copied references, not the caller's variables. To swap for real, swap the contents of something both sides share, such as two elements of the same array, or return the result.

Strings and Wrapper Types Look Like Primitives

String, Integer and the other wrapper classes are objects, so a copy of the reference is passed. They are also immutable: no method can change them in place. Something like text = text + "!" builds a new String and points only the local copy at it, so the caller still sees the original. A mutable type such as StringBuilder behaves differently, because append changes the one object both references share.

How to Get a Changed Value Back to the Caller

Since a method can never rebind the caller's variable, pick one of these instead:

  • Return the new value and let the caller store it (score = addBonus(score);). This is the clearest option and the usual choice.
  • Change a shared mutable object, such as a field of an object, an array element or a collection, when updating that object is the method's job.
  • Return a small result object or record when a method needs to hand back several values at once.

Examples

Mutating fields is visible to the caller; reassigning the reference is not

Java
class Employee {
    String name;
    Employee(String name) { this.name = name; }
}

public class CallByValueDemo {

    static void mutateField(Employee e) {
        e.name = "Changed Inside Method"; // same object, so caller sees this
    }

    static void reassignReference(Employee e) {
        e = new Employee("Brand New Object"); // only the local copy of the reference changes
    }

    static void changePrimitive(int value) {
        value = value + 100; // only the local copy changes
    }

    public static void main(String[] args) {
        Employee emp = new Employee("Original");
        mutateField(emp);
        System.out.println(emp.name); // field mutation IS visible

        reassignReference(emp);
        System.out.println(emp.name); // reassignment is NOT visible

        int score = 10;
        changePrimitive(score);
        System.out.println(score); // primitive change is NOT visible
    }
}
Output
Changed Inside Method
Changed Inside Method
10

A swap method fails on primitives but works on a shared array

Java
public class SwapDemo {
    static void swap(int a, int b) {
        int temp = a;
        a = b;
        b = temp; // swaps the local copies only
    }

    static void swapInArray(int[] values) {
        int temp = values[0];
        values[0] = values[1];
        values[1] = temp; // changes the one array both references point to
    }

    public static void main(String[] args) {
        int x = 1, y = 2;
        swap(x, y);
        System.out.println("After swap: x = " + x + ", y = " + y);

        int[] pair = {1, 2};
        swapInArray(pair);
        System.out.println("After swapInArray: " + pair[0] + ", " + pair[1]);
    }
}
Output
After swap: x = 1, y = 2
After swapInArray: 2, 1

An immutable String is unaffected; a mutable StringBuilder changes

Java
public class StringParamDemo {
    static void addMark(String text) {
        text = text + "!"; // builds a new String; only the local reference moves to it
    }

    static void addMark(StringBuilder text) {
        text.append("!"); // changes the one StringBuilder the caller also points to
    }

    public static void main(String[] args) {
        String greeting = "Hello";
        addMark(greeting);
        System.out.println(greeting);

        StringBuilder builder = new StringBuilder("Hello");
        addMark(builder);
        System.out.println(builder);
    }
}
Output
Hello
Hello!

Returning the new value is how a method hands a change back

Java
public class ReturnValueDemo {
    static int addBonus(int score) {
        return score + 100; // hand the new value back instead of changing the parameter
    }

    public static void main(String[] args) {
        int score = 10;
        addBonus(score);              // result ignored, score unchanged
        System.out.println(score);
        score = addBonus(score);      // caller chooses to store the result
        System.out.println(score);
    }
}
Output
10
110

Common Mistakes

  • Saying Java is "pass by reference for objects" — Java always passes a copy of the value; for objects that value happens to be a reference.
  • Expecting a method to replace the caller's object by reassigning the parameter (e.g. writing a "swap two objects" method that just reassigns the two parameters) — this only rebinds local copies and has no effect outside the method.
  • Concluding that because field mutation is visible, reassignment must also be visible — these are two entirely different operations on the reference.
  • Confusing Java with languages like C++ that have true reference parameters (&) or with C-style pointer-to-pointer techniques, neither of which Java supports.

Exercise

This swap method does nothing useful: the program prints 1 2 because the method only swaps its own copies. Change the program so the two values really are swapped, by passing an array and swapping its elements.

Expected output: 2 1

Java · starter code
public class SwapDemo {

    static void swap(int a, int b) {
        int temp = a;
        a = b;
        b = temp;
    }

    public static void main(String[] args) {
        int x = 1;
        int y = 2;
        swap(x, y);
        System.out.println(x + " " + y);
    }
}
Show hints
  • A method cannot change the caller's primitive variables, but it can change the contents of an object or array the caller passed in.
  • Store both values in an int[] and swap pair[0] with pair[1] inside the method.
Show solution
Java · solution
public class SwapDemo {

    static void swap(int[] pair) {
        int temp = pair[0];
        pair[0] = pair[1];
        pair[1] = temp;
    }

    public static void main(String[] args) {
        int[] values = {1, 2};
        swap(values);
        System.out.println(values[0] + " " + values[1]); // 2 1
    }
}

Quick quiz

1. How does Java pass arguments to methods?

  • A. Always by value
  • B. Primitives by value, objects by reference
  • C. Always by reference
  • D. It depends on the method
Show answer

A. Java always passes a copy of the value. For an object variable, that value is the reference, so the copy points to the same object.

2. A method receives a Person p and runs p.name = "X";. Does the caller see the change?

  • A. Only if the method is static
  • B. No, the method works on a copy of the object
  • C. Only if p is declared final
  • D. Yes, both references point to the same object
Show answer

D. The reference was copied, not the object. Changing a field through either reference changes the one shared object.

3. The same method instead runs p = new Person("Y");. What happens to the caller's variable?

  • A. It is unchanged and still refers to the original object
  • B. It now refers to the new Person
  • C. It becomes null
  • D. The code does not compile
Show answer

A. Reassigning the parameter only changes the method's local copy of the reference. The caller's variable still points to the original object.

Interview questions

Is Java pass-by-value or pass-by-reference?

Show answer

Pass-by-value, always. When you pass a primitive, the method gets a copy of the number. When you pass an object, the method gets a copy of the reference. Because that copy points to the same object, the method can change the object's fields — which looks like pass-by-reference — but it cannot make the caller's variable refer to a different object, which true pass-by-reference would allow.

Why can you not write a method that swaps two int variables in Java?

Show answer

Because the method receives copies of the two values. Swapping the copies has no effect on the variables in the caller, and Java has no way to pass the variables themselves. To get the effect, wrap the values in something the method can modify, such as an array or an object, or return the new values.

Key Points to Remember

  • Java is always pass-by-value — there is no pass-by-reference in Java, ever.
  • For primitives, a copy of the actual value is passed.
  • For objects, a copy of the reference (address) is passed — both copies point to the same object, so field mutations are visible to the caller.
  • Reassigning the parameter inside a method only changes the local copy of the reference; the caller's original reference is never affected.

Practice in the playground

Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.

Run the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

WebNest Studio

Written by

WebNest Studio Engineering Team

Practical lessons from developers working with Java, Python, React, databases and production systems.

Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering