2011-08-17 70 views
1

String對象在Java中如何工作?術語「不可變」如何完全適用於字符串對象?爲什麼我們在通過某種方法後看不到修改後的字符串,儘管我們使用原始字符串對象值?字符串 - 它們是如何工作的?

+0

可能的重複[爲什麼不能在Java和.NET中可變的字符串?](http://stackoverflow.com/questions/93091/why-cant-strings-在Java和網絡中可變) – adatapost

+4

這個研究得不好的3部分問題是如何得到加票的? *「有例子的解釋是值得讚賞的。」*是(乾的),以及經過深入研究的問題。 –

+0

安德魯..!我很抱歉 ! –

回答

10

一個字符串有一個私有的最終char[]。當一個新的String對象被創建時,該數組也被創建和填充。它不能在以後從外部訪問或修改[實際上它可以通過反射來完成,但我們將把它放在一邊]。

它是「不可變的」,因爲一旦創建了一個字符串,它的值不能被改變,「牛」字符串將總是具有值「牛」。

我們看不到修改過的字符串,因爲它是不可變的,同一個對象永遠不會改變,不管你用它做什麼[除了用反射修改它]。所以「牛」+「馬」將創建一個新的字符串對象,並不修改最後一個對象。

如果你定義:

void foo(String arg) { 
    arg= arg + " horse"; 
} 

你撥打:

String str = "cow"; 
foo(str); 

,其中呼叫不被修改[因爲它是原始引用同一個對象!當你的str改變了arg,你只是改變它引用另一個String對象,並沒有改變實際的原始對象。所以str,將是相同的對象,這是沒有改變,仍然包含"cow"

如果你仍然想改變一個字符串對象,你可以做反射。但是,輕率,可以有一些嚴重的副作用影響:

String str = "cow"; 
    try { 
    Field value = str.getClass().getDeclaredField("value"); 
    Field count = str.getClass().getDeclaredField("count"); 
    Field hash = str.getClass().getDeclaredField("hash"); 
    Field offset = str.getClass().getDeclaredField("offset"); 
    value.setAccessible(true); 
    count.setAccessible(true); 
    hash.setAccessible(true); 
    offset.setAccessible(true); 
    char[] newVal = { 'c','o','w',' ','h','o','r','s','e' }; 
    value.set(str,newVal); 
    count.set(str,newVal.length); 
    hash.set(str,0); 
    offset.set(str,0); 
    } catch (NoSuchFieldException e) { 
    } catch (IllegalAccessException e) {} 
    System.out.println(str); 
} 
+0

該示例爲+1 – Charliemops

+0

使用cow作爲字符串示例時,在編寫Java字符串時搜索C.O.W副本時會感到困惑。 https://en.wikipedia.org/wiki/Immutable_object#Copy-on-write – masters3d

4

tutorial

String類是不可變的,使得它一旦建立一個字符串對象不能被改變。 String類有許多方法,其中一些將在下面討論,它們似乎修改了字符串。由於字符串是不可變的,這些方法真正做的是創建並返回一個包含操作結果的新字符串。

2

Java中的字符串是不可變的(狀態一旦創建就不能修改)。這提供了優化的機會。一個例子是字符串實習,其中字符串文字保持在字符串池中,並且只有在特定字符串文字不存在於池中時才創建新的String對象。如果字符串文字已經存在,則返回一個引用。這隻能由於字符串是不可變的,所以你不必擔心一些持有引用的對象會改變它。

似乎修改字符串的方法實際上會返回一個新實例。一個例子是字符串連接:

String s = ""; 
for(int i = 0; i < 5; i++){ 
    s = s + "hi"; 
} 

什麼實際內部發生(編譯更改它):

String s = ""; 
for(int i = 0; i < 5; i++){ 
    StringBuffer sb = new StringBuffer(); 
    sb.append(s); 
    sb.append("hi"); 
    s = sb.toString(); 
} 

你可以清楚地看到,新的實例由toString方法創建(注意,這可能是通過直接使用StringBuffers使效率更高)。與字符串不同,StringBuffers是可變的。

+0

'sb.append(「hi」)' 上述語句中的「hi」是否被添加到字符串池中? –

0

每個對象都有狀態。 String對象的狀態是構成String的字符數組,例如,String「foo」包含數組['f','o','o']。因爲一個字符串是不可變的,這個數組可以從來沒有以任何方式,形狀或形式進行更改。

每個想要更改字符串的類中的每個方法都必須返回新的表示舊字符串的已更改狀態的字符串。也就是說,如果您嘗試反轉「foo」,您將得到一個新的帶內部狀態['o','o','f']的字符串對象。

0

我認爲this鏈接將幫助您瞭解Java的String如何工作

現在考慮下面的代碼 -

String s = "ABC"; s.toLowerCase(); 方法toLowerCase()將不會更改數據「ABC」是S包含。相反,一個新的String對象被實例化,並在構造過程中給出數據「abc」。 toLowerCase()方法返回對該String對象的引用。爲了使字符串包含數據「abc」,需要採用不同的方法。

再次考慮以下 - s = s.toLowerCase();

現在字符串s的引用,其中包含 「ABC」 的新String對象。在類String聲明的語法中沒有任何東西強制它作爲不可變的;相反,沒有一個String類的方法會影響String對象包含的數據,因此使它不可變。

我真的不明白你的第三個問題。可能會提供一段代碼,並告訴你的問題是一個更好的選擇。希望這可以幫助。

您也可以看看這個blogpost更多的理解

[代碼樣本是從維基拍攝。你也可以在那裏看看更多的信息]

相關問題