Twisol's reply is very good, but I'll add this:
> that reference itself is a value-- it's a thing passed to the function and copied from somewhere in memory to a register.
I don't see your point here. Java always passes by value. This isn't an open question. Computer architecture, assembly code, and compiler specifics, are irrelevant.
> it's the implementation details behind the syntactic sugar, compiler optimizations, and runtime support that allow the developer to reason abstractly with references.
It isn't. References types, and parameter-passing, work however the language defines them to work. The language's implementation isn't relevant.
> It's the interpretation of this value that makes it a reference to some other tangible item.
In pass-by-reference, the parameter refers to the passed-in variable, and you aren't given a choice about it. If you assign to a parameter which was passed by reference, the change will be visible to the caller. This is different from passing a pointer by value (as in C) and from passing an object-reference by value (as in Java).
(It's confusing that we use 'refer to' in two different senses. In Java, a reference-type variable 'refers to' an instance, but overwriting the variable does not impact the referred-to entity. In pass-by-reference, a parameter 'refers to' a variable, and overwriting does impact the referred-to entity. Perhaps we should instead say that in Java, a reference-type variable 'points to' an instance.)
> Your Java example with String overlooks the fact that String is immutable
It does not. The immutability of the String instance is irrelevant. Swap it out for another class type, or for an array type, and everything I wrote still applies.
> Use any other object type, allow doMotreStuff() to operate on the argument's properties, and you get the same effect as if passing by reference.
In Java, everything is passed by value, including Java's reference types. Sometimes, pass-by-value and pass-by-reference would behave the same. We could demonstrate this with a C# example. So what?
I'm not talking about accessing the members of the referenced object, as that isn't instructive here.
Using pass-by-reference would permit the reference-type variable itself to be modified. Again, it is irrelevant whether the object instance is immutable. An example where we don't use an immutable object, but it still matters that Java uses pass-by-value:
private static void doStuff(int[] arr) {
arr = null; // Only overwrites local copy. Reference types are passed by
} // value. The assignment is invisible to the 'main' method.
public static void main(String[] args) {
int[] myArr_1 = {1,2,3};
int[] myArr_2 = myArr_1;
// The value of local variable 'myArr_2' cannot be changed by this line:
doStuff(myArr_2);
assert(myArr_1 == myArr_2); // Ok.
}
> the SO explanation isn't a good one precisely because you get this pass-by-reference behavior in Java
There is no pass-by-reference in Java. It is incorrect to say that Java sometimes passes by reference. Java has reference types, has no pointer types, and always passes by value. These are distinct concepts, with accepted terminology.
> that's not helpful when attempting to help the newb understand why his object mutated in a method when "pass by value" is what he expected.
Maybe so, then we should start by explaining Java, and only explain the terms pass-by-value and pass-by-reference later on.
What we shouldn't do is teach an incorrect definition of pass-by-reference. Saying that Java uses pass-by-reference is simply wrong, and should cost a student marks on their exam.