2015-07-13 211 views
6

我不明白为什么的System.out.println(名称)输出山姆而不受该法的concat函数,而的System.out.println(名)输出Sam4作为该方法的追加方法的结果。为什么StringBuilder会影响而不是String?通常情况下,对对象的引用调用方法会影响调用者,所以我不明白为什么字符串结果保持不变。在此先感谢传递由值(StringBuilder的字符串VS)

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 
} 
+0

很多答案在这里,基本上都说同样的事情。很难选择一个来upvote :) –

+2

@ArnaudDenoyelle幸运的是,你不需要只选择一个;) –

+0

@ArnaudDenoyelle作为马克Rotteveel说,海琳娜是谁必须作出艰难的选择;) – Karthik

回答

9

因为当你调用speak(name);,里面讲,当你做

name = name.concat("4"); 

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

所以基本上你在做什么是:

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

尝试

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

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

+0

“它创造了一个新的因为字符串是不可改变的。“语言中没有“可变性”的概念。它返回一个新的对象,因为这是该方法记录的功能。它不会修改它所调用的对象,因为这就是该方法所记录的。 – newacct

+1

@newacct对不起,先生,我相信我不像你那么了解,但我认为如果我说“它返回一个新的对象,因为它的方法被记录为这样做,OP会不高兴”。所以这就是为什么我想给出一些背后的推理。 – Karthik

+0

但是我们只说这个类是“不可变的”,只是因为它的所有方法都被记录为不能修改它。所以说循环推理说这些方法不会修改它,因为它是不可变的。 – newacct

3

当你调用speak(name)它计算新的值,但放弃它。

如果用

name = speak(name); 

取代它的​​结果将是你所期望的一个。

随着StringBuilder,传递的对象是可变的:如此

names.append(names); 

改变当前对象的状态(它也返回一个引用同一个对象,这仅仅是为了方便让你写代码,如names.append(...).append(...)等)。因此,在StringBuilder的情况下,调用方法时引用的对象实际上已更改,因此您会看到更改。

+0

你没有回答这个问题:为什么它和StringBuilder一起工作? – m0skit0

+0

是的,的确如此。查看更新。 –

0

字符串

字符串是不可变的(一旦创建不能改变)对象。创建为String的对象 存储在常量字符串池中。 Java中的每个不可变对象都是线程安全的,这意味着字符串是 也是线程安全的。字符串不能同时被两个线程 使用。曾经分配过的字符串不能更改。

String demo =“hello”; //上述对象存储在常量 字符串池中,其值不能修改。

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

的StringBuilder

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

有关详情,请this

结论: 你并不需要再次重新分配值StringBuilder,因为它已经是一个参考 测试方法应该是

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

但说的应该是

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

这不是它被问到的。 – m0skit0

+0

好吧,我更新了答案 –

3

String在java中是不可变的。只要您调用名称上的concat方法。创建一个新的字符串,并在System.out.println(name)中使用旧的引用。如果要使用修改的字符串,则应显式返回引用。 虽然StringBuilder是可变的,并且它总是返回相同的引用。

3

因为String是不可改变的,因此String#concat不修改原始String实例,它只返回一个新String而原来是左修改,而StringBuilder是可变的,变化体现在StringBuilder实例作为参数传递。

3

在你的方法speak,该concat方法返回一个新的String,它被称为原始对象是不变的(string是不可改变的)。如记录:

如果参数字符串的长度为0,则返回此String对象。否则,将返回一个String对象,该对象表示由此String对象表示的字符序列和由参数字符串表示的字符序列的并置字符序列。

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

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

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

在main方法都namenames仍然相同对象的方法调用之前,但name内容是不变的字符串是不可改变的,而names的内容已被更改。

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

4

好的,speak方法在做什么?

首先,

name.concat("4"); 

创建新的对象,其等于name,级联与"4"

因此,线

name = name.concat(4); 

重新定义本地(对于speak方法)可变name

然后你

return name; 

所以,原来的变量,方法内传递的不被修改返回参考这个新的价值,但该方法返回修改后的值。

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

然后我们可以看到另一个问题:为什么StringBuilder.append返回值,它看起来多余。这个问题的答案在于“建造者”模式的描述,为此它是实现修改方法的常用方式。请参阅wikipedia on Builder pattern

2

首先,String是Java中的不可变类。一个不变类只是一个其实例不能被修改的类。创建实例并且无法修改信息时,会初始化实例中的所有信息。

二,在java参数是通过值发送而不是通过引用。

在您的方法“测试”中,您不需要names = names.append("4"),而改为names.append("4")就足够了。

如果您检查String对象的java docs,您将看到那里的大多数方法(包括concat)都会生成一个新的String。

所以要输出Sam4也为String,你需要在main方法中有这个name = speak(name)