Java is strictly pass-by-value for everything, including primitives and object references. There is no pass-by-reference mechanism in Java. To understand why, let’s look at the exact memory mechanics of how Java handles variable storage on the stack versus the heap.
In Java - Object references are passed by value
Java has pointers, and the value of the pointer is passed in. ^1
- There’s no way to actually pass an object itself as a parameter, as there is no pass-by-reference mechanism in Java.
- You can only pass a pointer to an object.
Primitives: Pass-by-Value of the Data
When you pass a primitive variable (like an int, boolean, or char) into a method
- As there’s no such thing in Java as a pointer/reference to a primitive.
- Java copies the exact numerical value stored in that variable and places that copy into the method’s local stack frame.
public void modifyValue(int x) {
x = 100; // Changes only the local copy on the stack
}
// Execution:
int num = 10;
modifyValue(num);
System.out.println(num); // Prints 10, because 'num' was untouched.- Technical mechanism:
- The memory slot for
numcontains the bits for10. - When
modifyValueis called, a separate memory slot on the stack is allocated for parameterx, and the bits for10are copied into it. - Modifying
xalters the bits inx’s slot, leavingnum’s slot unchanged.
- The memory slot for
Objects: Pass-by-Value of the Reference (Memory Address)
Note
Objects are not passed by reference A correct statement would be Object references are passed by value.
The reference values (often just references) are pointers to these objects, and a special null reference, which refers to no object. Official Ref
When you pass an object (like a String) into a method,
- Java does not pass the object itself.
- It copies the value of the reference—which is the memory address (pointer) pointing to where the object lives on the heap.
It sounds exactly like pass-by-reference because a memory address is being handed over, but technically Java is strictly pass-by-value.
The difference comes down to what is actually being copied and whether you can redirect the original variable. Let’s break down why Java’s behavior is officially called pass-by-value of a reference:
1. What is being copied?
- In pass-by-reference
- the function receives a direct alias to the original variable itself.
- If you change what that variable points to inside the function, the caller’s variable changes too.
- In Java
- when you pass an object (like a
Stringor an array) - Java copies the value of the reference (the memory address value) and hands that copy to the method.
- when you pass an object (like a
2. The Proof: You Cannot Reassign the Original Reference
If Java were truly pass-by-reference, you could reassign the object inside a method and have it affect the caller. Look at this example:
public class PassByVal1 {
public static void modifyReference(String str) {
// This reassigns our local copy of the reference to a new string
str = "New String";
}
public static void main(String[] args) {
String myStr = "Original";
modifyReference(myStr);
System.out.println(myStr); // Prints "Original", NOT "New String"!
}
}If it were pass-by-reference, myStr in main would now point to "New String". Because it’s pass-by-value, only the copy of the address was changed inside the method; the original myStr variable back in main still points to "Original".
Mutation and Redirection
The called method can mutate the internal state of the object if it is mutable, but it cannot redirect the caller’s variable to a different object.
Why Object Pass-by-Value Allows Mutation
This is the ultimate core of understanding Java’s execution model. It feels contradictory: If only a value is copied, how can the object change?
The answer lies in what the value actually is:
- The value stored in an object variable is the memory reference (memory address pointer).
- When you pass an object into a method, Java copies that address value onto the method’s stack frame.
- Because the method now holds an exact copy of the address, both the caller’s variable and the method’s parameter point to the exact same object on the heap.
The Crucial Distinction: Mutation vs. Reassignment
- Mutating the object :
sb.append("world")in below eg.- Because your parameter holds a copy of the address, the CPU follows that address straight to the heap and modifies the internal bytes of that object.
- Both variables see the change because they point to the same house. This is fully permitted in pass-by-value.
- Reassigning the variable :
- `str = “Other” in below eg
- This changes only the local variable’s stored address value on the stack.
- The caller’s variable still holds the original address value
Pass-by-value simply means the variable’s contents are copied. When the contents happen to be a memory address pointer, copying that pointer gives the method a working map to the exact same heap object.
public void modifyReference(StringBuilder sb) {
sb.append("World"); // Mutates the object on the heap
sb = new StringBuilder("New"); // Reassigns the local variable copy only
}
// Execution:
StringBuilder mySb = new StringBuilder("Hello");
modifyReference(mySb);
System.out.println(mySb.toString()); // Prints "HelloWorld", NOT "New"!- Technical mechanism:
mySbholds a memory address value (e.g.,0x1000), pointing to aStringBuilderinstance on the heap.- When
modifyReferenceis called, that address value (0x1000) is copied into the method’s local parametersb. - Inside the method, calling
sb.append("World")follows the address0x1000to the heap and modifies the internal byte array of that object. That is why the change is visible outside. - However, executing
sb = new StringBuilder("New")changes the local variablesbto hold a new address (e.g.,0x2000). The caller’s variablemySbstill holds0x1000. If Java were pass-by-reference,mySbwould now point to0x2000. Because it is pass-by-value of the reference,mySbis unaffected.
Summary of Differences
- Pass-by-Value (Primitives): Copies the actual data bits (e.g.,
10,'a'). The original variable cannot be changed by the method. - Pass-by-Value (Objects): Copies the memory address bits (e.g.,
0x5FA4). The method can mutate the internal state of the object if it is mutable, but it cannot redirect the caller’s variable to a different object. - Pass-by-Reference (Not in Java): Passes an alias to the variable itself, allowing both the data and the variable binding to be completely reassigned by the callee.
is there a way to check memory address in java and work an example with that to understand this concept better
Unlike languages like C or C++, Java does not allow developers to access or print actual physical or virtual memory addresses (pointers). The Java Virtual Machine (JVM) abstracts memory management entirely for security and garbage collection, meaning objects can also move around on the heap during garbage collection compaction.
However, Java provides a built-in method called System.identityHashCode(Object x) that returns the integer representation of the object’s default memory address hash code (computed by the JVM based on its memory address before any custom hashCode() override).
We can use System.identityHashCode() to write a technical demonstration proving that references are copied (pass-by-value of a reference) rather than passed by reference.
Code Demonstration: Tracking Reference Values
public class ReferenceDemo {
public static void main(String[] args) {
String myString = "Hello";
// Print the identity hash code (based on the memory address) before the call
System.out.println("Before method call:");
System.out.println("myString reference value: " + System.identityHashCode(myString));
testPassByValue(myString);
// Print after the call to prove original reference is unchanged
System.out.println("After method call:");
System.out.println("myString reference value: " + System.identityHashCode(myString));
}
public static void testPassByValue(String str) {
System.out.println("\nInside method (at start):");
System.out.println("str reference value: " + System.identityHashCode(str));
// Reassigning the local parameter 'str' to a completely new string object
str = "World";
System.out.println("\nInside method (after reassignment):");
System.out.println("str reference value: " + System.identityHashCode(str));
}
}Technical Breakdown of What Happens:
- Initial State (
main):myStringpoints to aStringobject ("Hello") sitting on the heap at a specific memory location.System.identityHashCode(myString)prints that location’s integer identifier (e.g.,460141958).
- Entering the Method (
testPassByValue):- When
testPassByValue(myString)is invoked, Java takes the value stored insidemyString(the address460141958) and copies it into the method’s local parameterstr. - Printing
System.identityHashCode(str)right at the start inside the method prints the exact same number (460141958). Both variables point to the exact same object on the heap. This is why people often mistake it for pass-by-reference.
- When
- Reassignment (
str = "World"):- Inside the method, we execute
str = "World". This allocates a newStringobject on the heap (let’s say its identity hash is1163157884). - The local variable
stris updated to hold this new address value.
- Inside the method, we execute
- Exiting and Returning to
main:- When the method finishes and we return to
main, printingSystem.identityHashCode(myString)shows thatmyStringstill holds the original value (460141958). - The reassignment of
strinside the method had zero effect onmyStringbecause only a copy of the address was passed and modified.
- When the method finishes and we return to
How this relates to your recursion method:
When your recursive method calls checkPalindrome(s, i + 1, j - 1), Java copies the reference value of s (its memory address) onto every new stack frame. Because every stack frame gets its own copy of that address, no matter how deep your recursion goes, the original string reference in main remains intact and untouched, and no string data is ever duplicated.