按值传递(StringBuilder vs String)



我不明白为什么System.out.println(name)输出Sam而不受方法的concat函数的影响,而System.out.println(names)输出Sam4作为方法的append方法的结果。为什么StringBuilder受到影响而不是String?通常,对对象的引用调用方法会影响调用者,所以我不明白为什么String结果保持不变。提前感谢

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
}

因为当你调用speak(name);时,内部会说话

name = name.concat("4");

创建一个新对象,因为String是不可变的。当你改变原来的字符串时,它创建了一个新对象,我同意你是在返回它,但你没有捕捉到它。

你所做的就是:

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

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

当然,现在我认为没有必要解释为什么它与StringBuilder工作,除非你不知道StringBuilder是可变的。

查看String的Javadoc,可以看到

[…字符串对象是 [...].

这意味着concat(String)不会改变String本身,而是构建一个新的String

另一方面,

StringBuilder s是可变的。通过调用append(CharSequence),对象本身发生了变化。

由于String是不可变的,因此String#concat不会修改原来的String实例,它只返回一个新的String,而原来的不修改,而StringBuilder是可变的,变化反映在作为参数传递的StringBuilder实例中。

好的,speak方法在做什么?

首先,

name.concat("4");

创建一个新的对象,它等于name,与"4"连接。

那么,这行

name = name.concat(4);

重新定义本地 (speak方法)变量name

然后用

返回对新值的引用
return name;

因此,在方法中传递的原始变量没有被修改,但是方法返回修改后的值。

test方法中,您实际上修改变量而不修改引用(StringBuilder类是可变的,因此如果该类型可以修改,则为变量)。

然后我们可以看到另一个问题:为什么StringBuilder.append返回值,在那里它看起来是多余的。这个问题的答案在于"构建器"模式的描述,它是实现修改方法的常用方式。参见wikipedia上的Builder模式。

String在java中不可变。只要在name上调用concat方法。一个新的字符串被创建,当你在System.out.println(name)中使用旧的引用时,如果你想使用修改的字符串,你应该显式地返回引用。而StringBuilder是可变的,它总是返回相同的引用。

当您调用speak(name)时,它会计算新值,但会丢弃它。

如果用

代替
name = speak(name);

结果将是你所期望的。

对于StringBuilder,您传递的对象是可变的:所以

names.append(names);

改变当前对象的状态(它还返回对同一对象的引用,这只是为了方便您编写names.append(...).append(...)等代码)。因此,在StringBuilder的情况下,当您调用该方法时引用的对象实际上已经更改,因此您可以看到更改。

在你的方法speak中,concat方法返回一个新的String,它被调用的原始对象是不变的(字符串是不可变的)。记录:

如果参数字符串的长度是0,则返回这个String对象。否则,返回一个String对象,该对象表示一个字符序列,该字符序列是由该String对象表示的字符序列与参数字符串表示的字符序列的连接。

调用name.concat("4")相当于调用name + "4"

在你的test方法中,append方法修改StringBuilder的内容。记录:

StringBuilder的主要操作是appendinsert方法,它们是重载的,可以接受任何类型的数据。它们都有效地将给定的数据转换为字符串,然后将该字符串的字符追加或插入到字符串构造器中。append方法总是在构造器的末尾添加这些字符;insert方法在指定的点添加字符。

在你的主方法中,namenames仍然是与方法调用之前相同的对象,但是name的内容没有变化,因为字符串是不可变的,而names的内容已经改变了。

如果您使用了两个方法的返回值,那么您将得到您期望的结果。

首先,String是Java中的不可变类不可变类就是不能修改其实例的类。实例中的所有信息在创建实例时初始化,不能修改。

第二,在java中参数是按值发送的,而不是按引用发送的。

在你的方法'test'中,你不需要names = names.append("4"),而names.append("4")就足够了。

如果你检查java文档中的String object,你会看到那里的大多数方法,包括concat,都会生成一个新的String。

所以在输出Sam4也为字符串,你将需要在主方法有这个name = speak(name)

String

String是不可变的(一旦创建就不能更改)对象。的作为String类型创建的对象存储在常量字符串池中。Java中的每个不可变对象都是线程安全的,这意味着String也是也线程安全。字符串不能被两个线程使用同时进行。字符串一旦被赋值就不能更改。

String demo = " hello ";//以上对象存储在常量中不能修改字符串池及其值

演示="再见";//在常量池和中创建新的"Bye"字符串由demo变量引用//"hello"字符串仍然存在于字符串常量池中,其值不会被覆盖,但我们丢失对"hello"字符串的引用

StringBuilder

StringBuilder与StringBuffer相同,即它存储对象,它也可以被修改。主要区别StringBuffer和StringBuilder之间的区别在于StringBuilder是也不是线程安全的。StringBuilder很快,因为它不是线程安全的.

查看更多详细信息

结论: 您不需要再次将值重新分配给StringBuilder,因为它已经是一个引用试验方法应为

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

但是说话应该是

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

最新更新