Pass-by-value (StringBuilder vs String) [duplicate

2020-08-11 17:32发布

问题:

I do not understand why System.out.println(name) outputs Sam without being affected by the method's concat function, while System.out.println(names) outputs Sam4 as a result of the method's append method. Why is StringBuilder affected and not String? Normally, calling methods on a reference to an object affects the caller, so I do not understand why the String result remains unchanged. Thanks in advance

public static String speak(String name) {
    name = name.concat("4");
    return name;
}

public static StringBuilder test(StringBuilder names) {
    names = names.append("4");
    return names; 
}

public static void main(String[] args) {
    String name = "Sam";
    speak(name);
    System.out.println(name); //Sam
    StringBuilder names = new StringBuilder("Sam");
    test(names);
    System.out.println(names); //Sam4
}

回答1:

Because when you call speak(name);, inside speak when you do

name = name.concat("4");

it creates a new object because Strings are immutable. When you change the original string it creates a new object,I agree that you are returning it but you are not catching it.

So essentially what you are doing is :

name(new) = name(original) + '4'; // but you should notice that both the names are different objects.

try

String name = "Sam";
name = speak(name);

Of course now I think there is no need to explain why it's working with StringBuilder unless if you don't know that StringBuilder is mutable.



回答2:

Looking at the Javadoc for String, one will read that

[...] String objects are immutable [...].

This means concat(String) does not change the String itself, but constructs a new String.

StringBuilders, on the other hand, are mutable. By calling append(CharSequence), the object itself is mutated.



回答3:

Because String is immutable and hence String#concat does not modify the original String instance, it only returns a new String while the original is left unmodified, while StringBuilder is mutable and the change is reflected in the StringBuilder instance passed as parameter.



回答4:

Okay, what is speak method doing?

First of all,

name.concat("4");

creates new object, which is equal to name, concatenated with "4".

So, the line

name = name.concat(4);

redefines local (for speak method) variable name.

Then you return the reference to this new value with

return name;

So, the original variable, passed within method is not modified, but the method returns modified value.

In the test method you actually modify variable without modifying the reference (the StringBuilder class is mutable, so variable if this type can be modified).

Then we can see another question arising: why StringBuilder.append returns value, where it can seem redundant. The answer to this question lies in the description of "builder" pattern, for which it is the usual way of implementing modification methods. See wikipedia on Builder pattern.



回答5:

String is immutable in java. As soon as you invoke concat method on name. A new string is created and while you are playing with the old reference in System.out.println(name).If you want to use the modified string you should explicitly return the reference. While StringBuilder is mutable and it returns the same reference always.



回答6:

When you invoke speak(name) it computes the new value, but discards it.

If you replace it with

name = speak(name);

the result will be the one you expect.

With the StringBuilder, the object you pass is mutable: so

names.append(names);

changes the state of the current object (it also returns a reference to the same object, which is just a convenience to allow you to write code like names.append(...).append(...) etc.). So in the case of the StringBuilder, the object you are referencing when you call the method has actually changed, hence you see the changes.



回答7:

In your method speak, the concat method returns a new String, the original object it was called on is unchanged (strings are immutable). As documented:

If the length of the argument string is 0, then this String object is returned. Otherwise, a String object is returned that represents a character sequence that is the concatenation of the character sequence represented by this String object and the character sequence represented by the argument string.

Calling name.concat("4") is the equivalent of name + "4".

In your test method the append method modifies the content of the StringBuilder. As documented:

The principal operations on a StringBuilder are the append and insert methods, which are overloaded so as to accept data of any type. Each effectively converts a given datum to a string and then appends or inserts the characters of that string to the string builder. The append method always adds these characters at the end of the builder; the insert method adds the characters at a specified point.

In your main method both name and names are still the same object as before the method call, but the content of name is unchanged as strings are immutable, while the content of names has been changed.

If instead you had used the return values of both methods, then you would have the result you were expecting.



回答8:

First of all, String is an immutable class in Java. An immutable class is simply a class whose instances cannot be modified. All information in an instance is initialized when the instance is created and the information can not be modified.

Second, in java parameters are sent by values and not by reference.

In your method 'test' you don't need names = names.append("4"), instead names.append("4") will be enough .

If you check java docs for String object, you will see that most of the methods there, including concat, will generate a new String.

So to have on output Sam4 also for the String, you will need in main method to have this name = speak(name).



回答9:

String

String is immutable ( once created can not be changed )object . The object created as a String is stored in the Constant String Pool . Every immutable object in Java is thread safe ,that implies String is also thread safe . String can not be used by two threads simultaneously. String once assigned can not be changed.

String demo = " hello " ; // The above object is stored in constant string pool and its value can not be modified.

demo="Bye" ; //new "Bye" string is created in constant pool and referenced by the demo variable // "hello" string still exists in string constant pool and its value is not overrided but we lost reference to the "hello"string

StringBuilder

StringBuilder is same as the StringBuffer , that is it stores the object in heap and it can also be modified . The main difference between the StringBuffer and StringBuilder is that StringBuilder is also not thread safe. StringBuilder is fast as it is not thread safe .

For more details check this

Conclusion: You don't need to re-assign the value again to StringBuilder as it is already a reference test method should be

public static void test(StringBuilder names) {
    names.append("4");
   }

but speak should be

 String name = "Sam";
   name =  speak(name);