Question
Is Java Pass-by-Reference or Pass-by-Value? A Clear Beginner Guide
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, andchar - 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:
- A method can modify the object's internal state
- because both references point to the same object
- 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
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
- In
main,upoints to aUserobject whosenameis"Original". update(u)is called.- Java copies the reference stored in
uinto the parameteruser. - Now
uanduserboth point to the same original object. user.name = "Updated";- This modifies the shared object.
user = new User("New Object");- The local parameter
usernow points to a different object. ustill points to the original object.
- The local parameter
user.name = "Changed Again";- This changes the new object only.
- Back in
main,u.nameis printed.ustill 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.
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
| Concept | What gets copied? | Can method mutate shared object? | Can method reassign caller's variable? |
|---|---|---|---|
| Primitive argument in Java | Primitive value | No | No |
| Object argument in Java | Reference value | Yes | No |
| True pass-by-reference | Reference to caller's variable itself | Yes | Yes |
Primitive vs object example
| Case | Example | Effect outside method |
|---|---|---|
| Primitive reassignment | x = 5; | No effect |
| Object field change | p.name = "Sam"; | Visible to caller |
| Object parameter reassignment | p = new Person("Sam"); | No effect |
Java vs common misunderstanding
| Statement | Correct? | Why |
|---|---|---|
| Java passes primitives by value | Yes | The value itself is copied |
| Java passes objects by reference | No | The reference is copied, not passed by reference |
| Java is always pass-by-value | Yes | This applies to both primitives and references |
A useful phrase is:
Java passes everything by value. For objects, the value being copied is the reference.
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.
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
- Create a class with at least one mutable field.
- Write one method that mutates the object.
- Write another method that reassigns the parameter to a new object.
- Print values before and after each method call.
- Show clearly which changes are visible in the caller.
Keep learning
Related questions
Add External JAR Files to an IntelliJ IDEA Java Project
Learn how to add external JAR dependencies to an IntelliJ IDEA Java project using module libraries, and when to use Maven or Gradle instead.
Avoiding Java Code in JSP with JSP 2: EL and JSTL Explained
Learn how to avoid Java scriptlets in JSP 2 using Expression Language and JSTL, with examples, best practices, and common mistakes.
Call a Method After a Delay in Android Java
Learn how to run Java code after a delay in Android using Handler.postDelayed, manage the main thread, and cancel callbacks safely.