In short, you are confusing reassigning a local variable with modifying a shared object. sb.append()
does not modify the variable sb
, it modifies the object that the variable references. But number = 3
modifies the variable number
by assigning it a new value. Objects are shared across method calls, but local variables are not. Therefore assigning a local variable a new value will not have any effect outside the method, but modifying an object will. A bit simplified: if you use a dot (.
), you are modifying an object, not a local variable.
You confusion is probably due to confusing terminology. "Reference" means two different things in different context.
1) When talking about Java objects and variables, a reference is a "handle" to an object. Strictly speaking, a variable cannot hold an object, it holds a reference to an object. References can be copied, so two variables can get a reference to the same object. E.g.
StringBuilder a = new StringBuilder();
StringBuilder b = a; // a and b now references the same object
a.writeLine("Hello"); // modify the shared object
b.toString() // -> "Hello"
When an objects is passed into a method as a parameter, it is also the reference that is copied, so the called method can modify the same object. This is also called "Call by sharing".
void main(){
String builder b = new StringBuilder();
otherMethod(b);
b.toString(); // --> "Hello" because the shared object is modified
}
void otherMethod(StringBuilder b){
b.write("Hello"); // modify the shared object
}
But the otherMethod can only modify the shared object, it cannot modify any local variables in the calling method. If the called function assigns a new value to its own parameters, it doesn't affect the calling function.
void main(){
String builder b = new StringBuilder();
b.writeLine("Hello");
otherMethod(b);
b.toString(); // --> "Hello" because the shared object is NOT modified
}
void otherMethod(StringBuilder b){
b = new StringBuilder(); // assign a new object to b
b.write("Goodbye"); // modify the new object
}
So you have to be clear on the distinction between modifying a shared object (through modifying fields or calling method on the object) and assigning a new value to a local variable or parameter.
2) When talking evaluation strategy (how parameters are passed into function) in general computer language theory, call-by-reference and call-by-value means something quite different.
Call-by-value, which Java supports, means that values from arguments are copied into parameters when calling methods. The value can be a reference, which is why the called function will get a reference to the shared object. Confusingly Java is said to "pass references by value", which is quite distinct from call-by-reference.
If a language supports call-by-reference, a function can change the value of a local variable in the calling function. Java does not support this, which is shown by the example above. Call-by-reference means the called function gets a reference to the local variable used as argument in the call. This notion is completely foreign to Java, where we simply do not have the concept of a reference to a local variable. We only have references to objects, never to local variables.
To show how call-by-reference works, we can look at C# which do support call-by-reference with the ref
modifier:
void main(){
String builder b = new StringBuilder();
b.writeLine("Hello");
otherMethod(ref b);
b.toString(); // --> "Goodbye" because otherMethod changed b to point to the new object
}
void otherMethod(ref StringBuilder b){
b = new StringBuilder(); // assign a new object to b
b.write("Goodbye"); // modify the new object
}
Here otherMethod() actually modifies the local variable b in main().
So what about primitives like integers, booleans etc.? These are immutable which means they cannot be modified like objects can. Since they cannot be modified, the question if they are shared or not is moot, since there is no observable difference whether they are shared or copied.
Note: I didn't invent the terminology, so don't blame the messenger. I much prefer the terms call-by-sharing or call-by-object which is more intuitive for Java, but the fact remains that the terms call-by-value and call-by-reference is established terminology, and the Java specification itself uses the term call-by-value.