2015-07-13 214 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)