As people have astutely pointed out, java is always pass by value.
It just so happens that the "value" of an object is it's memory address where it is stored on the heap. So here's what happens:
Object o = new Object();
The right hand side allocates space on the heap, we'll say at location 1000
The left hand side allocates a variable on the stack, we'll say at location 100, and due to the assignment contains the "value" of the object, which is 1000, it's location on the heap.
Your memory now looks like this:
Stack
---------------------------
====== caller stack frame ====
100(o) ==> 1000
==============================
Heap
------------------------------
1000 ==> object data 1
Now you make a method call:
void foo(Object x) { .... }
foo(o);
A new stack frame is created (obviously on the stack) for the method call.
A new variable is created on the stack, say at location 200, and given the "value" of the passed object, which is 1000, it's location on the heap.
So your memory looks like this before the method actually runs it's code
Stack
---------------------------
====== caller stack frame ====
100(o) ==> 1000
==============================
====== foo stack frame =======
200(x) ==> 1000
==============================
Heap
------------------------------
1000 ==> object data 1
So if you now do this in your method:
void foo(Object x) {
x = new Object();
}
You would be allocating a new object on the heap, let's say at location 2000, and assigning the method parameter variable to it's location so your memory now looks like this:
Stack
---------------------------
====== caller stack frame ====
100(o) ==> 1000
==============================
====== foo stack frame =======
200(x) ==> 2000
==============================
Heap
------------------------------
1000 ==> object data 1
2000 ==> object data 2
And when the method exits, the stack frame is dropped, and you're back to this:
Stack
---------------------------
====== caller stack frame ====
100(o) ==> 1000
==============================
Heap
------------------------------
1000 ==> object data 1
2000 ==> object data 2
Notice that "o" still points to the same object data. The second allocated object (at location 2000) would now also be elligible for garbage collection.