Assume newHead
is at memory location A
.
Since you assign newHead
to p
, p
now points to memory location A
as well.
Same logic for runner
, pointing to A
.
Now you set some property of the newHead
, which "changes" the property on all three newHead
, p
and runner
since all point to the same object in the same memory location A
.
If you now write runner = runner.next
you assign a new value to runner
.
Now runner
points to memory location B
.
But that simply does not matter for p
and newHead
since they only pointed to the same location as runner
but had no other relation than that. They only pointed to the same location / the same object. If you change the object all three know of that change. But if you change where one of the three points to, that does not matter for the other two.
Java non-primitive variables are simply pointers / references to memory addresses where the actual object lies. Without you as the developer having to deal with the problems that might arise from pointers (or having access to the features they provide).