Question

Is Java Pass-by-Reference or Pass-by-Value? A Clear Beginner Guide

javamethodsparameter-passingpass-by-referencepass-by-value

Question

I often hear that Java passes objects by reference, but I also read that Java is strictly pass-by-value. That seems contradictory.

What does Java actually do when values are passed to methods? How do primitive types and object references behave, and why can a method change an object's contents but not replace the caller's variable with a different object?

For example, what is the correct explanation for this distinction in Java?

Short Answer

By the end of this page, you will understand that Java is always pass-by-value. For primitive types, the copied value is the actual number, boolean, or character. For objects, the copied value is the reference to the object, not the object itself. That is why a method can mutate an object through its copied reference, but it cannot reassign the caller's original variable.

Concept

In Java, every method argument is passed by value.

That rule never changes.

The confusion comes from the fact that Java has two broad kinds of values:

  • Primitive values like int, double, boolean, and char
  • Reference values that point to objects like String, ArrayList, or custom class instances

When you pass a primitive to a method, Java copies the primitive value.

int x = 5;

If x is passed to a method, the method receives its own copy of 5.

When you pass an object variable to a method, Java copies the reference value stored in that variable.

Person p = new Person("Ava");

The variable p does not contain the whole object. It contains a reference to the object. When p is passed to a method, Java copies that reference.

So after the copy:

  • the caller's variable and
  • the method parameter

both refer to the same object in memory.

This explains two important behaviors:

  1. A method can modify the object's internal state
    • because both references point to the same object
  2. A method cannot make the caller's variable point somewhere else
    • because the parameter is only a local copy of the reference

That is the key idea.

Java is not pass-by-reference in the language-definition sense. In true pass-by-reference languages, a method can directly change the caller's variable itself. Java does not allow that with normal parameter passing.

Why this matters in real programming:

  • It helps you predict whether code will mutate data
  • It prevents confusion when debugging method calls
  • It explains why reassigning a parameter does not affect the original variable
  • It helps you design safer APIs and avoid accidental side effects

Mental Model

Think of a variable that stores an object as a piece of paper with an address written on it.

  • The object is the house
  • The reference is the address written on the paper
  • Passing a variable to a method means copying the paper, not moving the house

Now both the caller and the method have separate papers, but both papers contain the same address.

That means:

  • If either one goes to the house and paints the door, both will later see the painted door
  • If the method writes a new address on its own copied paper, the caller's paper does not change

So:

  • Mutating the object works because both references lead to the same object
  • Reassigning the parameter does not affect the caller because only the local copy of the reference changes
Take Quiz

Syntax and Examples

Core idea

public class Main {
    static void changePrimitive(int x) {
        x = 99;
    }

    static void changeObject(Person p) {
        p.name = "Liam";
    }

    static void reassignObject(Person p) {
        p = new Person("Mia");
    }

    public static void main(String[] args) {
        int number = 10;
        changePrimitive(number);
        System.out.println(number); // 10

        Person person = new Person("Noah");
        changeObject(person);
        System.out.println(person.name); // Liam

        reassignObject(person);
        System.out.println(person.name); // Liam
    }
}

class Person {
    String name;

    Person(String name) {
        this.name = name;
    }
}

What this shows

1. Primitive values are copied

number contains 10. The method gets its own copy of 10. Changing x to 99 changes only the local parameter.

2. Object references are also copied

person contains a reference to a Person object. The method gets a copy of that reference. Since both references point to the same object, changing p.name changes the shared object.

3. Reassigning the parameter changes only the local copy

Inside reassignObject, p is made to point to a new Person. But that does not change the caller's person variable. The original variable still points to the original object.

Step by Step Execution

Consider this example:

public class Main {
    static void update(User user) {
        user.name = "Updated";
        user = new User("New Object");
        user.name = "Changed Again";
    }

    public static void main(String[] args) {
        User u = new User("Original");
        update(u);
        System.out.println(u.name);
    }
}

class User {
    String name;

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

Step-by-step

  1. In main, u points to a User object whose name is "Original".
  2. update(u) is called.
  3. Java copies the reference stored in u into the parameter user.
  4. Now u and user both point to the same original object.
  5. user.name = "Updated";
    • This modifies the shared object.
  6. user = new User("New Object");
    • The local parameter user now points to a different object.
    • u still points to the original object.
  7. user.name = "Changed Again";
    • This changes the new object only.
  8. Back in main, u.name is printed.
    • u still points to the original object, whose name is now "Updated".

Output

Updated

This is the clearest proof of Java's parameter behavior:

  • object mutation through a copied reference is visible
  • parameter reassignment is not visible to the caller

Real World Use Cases

This concept appears constantly in Java code.

1. Updating objects in service methods

A service method may receive a domain object and update its fields.

void applyDiscount(Product product) {
    product.price = product.price * 0.9;
}

The caller sees the updated price because the object itself was mutated.

2. Working with collections

void addDefaultUser(List<String> users) {
    users.add("guest");
}

The method changes the same List object the caller passed in.

3. Validation and normalization

void normalize(User user) {
    user.name = user.name.trim();
}

This is common when cleaning input before saving data.

4. Reassignments that do not escape the method

void resetList(List<String> items) {
    items = new ArrayList<>();
    items.add("new");
}

The caller's list is unchanged, because only the local parameter was reassigned.

5. Defensive API design

Developers often avoid mutating passed-in objects unless that behavior is intended and documented. Understanding pass-by-value helps you decide when to mutate, copy, or return a new object instead.

Take Quiz

Real Codebase Usage

In real Java projects, developers use this concept in several common patterns.

Guard clauses before mutation

void updateEmail(User user, String email) {
    if (user == null || email == null || email.isBlank()) {
        return;
    }
    user.email = email;
}

The method mutates the object only after validation.

Returning a new object instead of reassigning a parameter

Because reassigning a parameter does not affect the caller, developers often return the replacement.

User upgrade(User user) {
    return new User(user.name, "PREMIUM");
}

Usage:

user = upgrade(user);

Mutating collections passed to helper methods

void addAuditEntry(List<String> logs, String message) {
    logs.add(message);
}

This is common in data processing and request handling.

Using immutable objects to avoid side effects

Classes like String cannot be changed in place.

void tryChange(String text) {
    text = text.toUpperCase();
}

The caller's variable does not change unless the returned value is assigned.

Error handling and validation

Developers often validate inputs because a copied reference can still be null.

void printLength(String text) {
    if (text == null) {
        System.out.println("No text");
        return;
    }
    System.out.println(text.length());
}

Common Mistakes

1. Thinking Java changes to a parameter variable affect the caller

Broken assumption:

void setToZero(int x) {
    x = 0;
}

Calling this does not change the original variable.

How to avoid it

Return the new value instead:

int setToZero(int x) {
    return 0;
}

2. Thinking object reassignment changes the caller's variable

Broken code:

void replacePerson(Person p) {
    p = new Person("Emma");
}

This only changes the local parameter.

How to avoid it

Return the new object:

Person replacePerson(Person p) {
    return new Person("Emma");
}

3. Confusing mutation with reassignment

void change(Person p) {
    p.name = "Olivia";      // affects caller's object
    p = new Person("Ava");  // does not affect caller's variable
}

These are very different operations.

4. Forgetting that String is immutable

Broken expectation:

void upper(String s) {
    s.toUpperCase();
}

This does not change the original string.

Correct version

String upper(String s) {
    return s.toUpperCase();
}

5. Accidentally mutating shared objects

void markDone(Task task) {
    task.done = true;
}

This is valid, but it can cause bugs if callers do not expect mutation.

How to avoid it

  • Document mutating behavior
  • Use immutable objects where possible
  • Create copies when needed

Comparisons

Java parameter passing compared

ConceptWhat gets copied?Can method mutate shared object?Can method reassign caller's variable?
Primitive argument in JavaPrimitive valueNoNo
Object argument in JavaReference valueYesNo
True pass-by-referenceReference to caller's variable itselfYesYes

Primitive vs object example

CaseExampleEffect outside method
Primitive reassignmentx = 5;No effect
Object field changep.name = "Sam";Visible to caller
Object parameter reassignmentp = new Person("Sam");No effect

Java vs common misunderstanding

StatementCorrect?Why
Java passes primitives by valueYesThe value itself is copied
Java passes objects by referenceNoThe reference is copied, not passed by reference
Java is always pass-by-valueYesThis applies to both primitives and references

A useful phrase is:

Java passes everything by value. For objects, the value being copied is the reference.

Take Quiz

Cheat Sheet

Quick rules

  • Java is always pass-by-value
  • Primitive variables store actual values
  • Object variables store references to objects
  • Method parameters receive copies of values
  • A copied object reference still points to the same object
  • Reassigning a parameter does not change the caller's variable
  • Mutating an object through a copied reference does affect the shared object

Mini examples

Primitive

void test(int x) {
    x = 100;
}

Caller unchanged.

Object mutation

void test(Person p) {
    p.name = "Kai";
}

Caller sees changed name.

Object reassignment

void test(Person p) {
    p = new Person("Kai");
}

Caller unchanged.

Safe pattern when replacement is needed

Person update(Person p) {
    return new Person("Kai");
}

Important edge case

String, Integer, and many other wrapper/value-like classes are immutable. Even though a reference is passed by value, you cannot change the internal object state in the same way you can with a mutable custom object.

FAQ

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

Java is pass-by-value only. For objects, the copied value is the reference.

Why can a Java method change an object if Java is pass-by-value?

Because the method receives a copy of the reference that still points to the same object. Both sides can access that shared object.

Why does reassigning an object parameter not affect the caller?

Because the parameter is only a local copy of the reference. Reassigning it changes that local copy, not the caller's variable.

Are arrays in Java passed by reference?

No. Arrays are objects, so their references are passed by value. A method can change array contents, but it cannot reassign the caller's array variable.

Is String behavior different?

String is still passed by value as a reference, but strings are immutable. Methods cannot change the original String object.

How do I replace an object for the caller in Java?

Return the new object and assign it in the caller.

Does this apply to collections like List and Map?

Yes. A method can mutate the collection contents, but reassigning the parameter will not change the caller's variable.

Take Quiz

Mini Project

Description

Build a small Java program that demonstrates the difference between changing an object's fields and reassigning a method parameter. This project helps make Java's pass-by-value behavior visible through printed output.

Goal

Create a program that proves Java passes object references by value, not by reference.

Requirements

  1. Create a class with at least one mutable field.
  2. Write one method that mutates the object.
  3. Write another method that reassigns the parameter to a new object.
  4. Print values before and after each method call.
  5. Show clearly which changes are visible in the caller.
Take Quiz

Keep learning

Related questions

View more