2011-08-17 28 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更多的理解

[代码样本是从维基拍摄。你也可以在那里看看更多的信息]

相关问题