字符串 - 它们如何工作?

时间:2011-08-17 06:52:49

标签: java string

String对象如何在Java中工作?术语“不可变”如何完全适用于字符串对象?为什么我们不通过某种方法看到修改后的字符串,虽然我们操作原始的字符串对象值?

5 个答案:

答案 0 :(得分:12)

String有一个私人最终char[]。创建新的String对象时,也会创建并填充该数组。它无法在以后[从外部]访问或修改[实际上它可以用反射完成,但我们将把它放在一边]。

它是“不可变的”,因为一旦创建了一个字符串,它的值就不能改变,“cow”字符串将始终具有值“cow”。

我们没有看到修改后的字符串,因为它是不可变的,不管你用它做什么,都不会改变同一个对象[除了用反射修改它]。所以“cow”+“horse”会创建一个 new String对象,而不是修改最后一个对象。

如果你定义:

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

你打电话:

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

调用所在的str未被修改[因为它是对同一对象的原始引用!]当您更改arg时,您只需将其更改为引用另一个String对象,并且不要更改实际的原始对象。因此,str将是同一个未更改的对象,仍然包含"cow"

如果您仍想更改String对象,可以使用反射来完成。但是,它是未经修改的,可能会产生一些严重的副作用:

    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);
}

答案 1 :(得分:4)

来自tutorial

  

String类是不可变的,因此一旦创建,就无法更改String对象。 String类有许多方法,其中一些将在下面讨论,它们似乎可以修改字符串。由于字符串是不可变的,因此这些方法真正做的是创建并返回包含操作结果的新字符串。

答案 2 :(得分: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是可变的。

答案 3 :(得分:0)

每个对象都有状态。 String对象的状态是组成String的字符数组,例如,字符串“foo”包含数组['f','o','o']。因为String是 immutable ,所以永远不会以任何方式,形状或形式 更改

每个想要更改String的类中的每个方法都必须返回一个 new String,表示旧String的更改状态。也就是说,如果你试图反转“foo”,你将得到一个带有内部状态['o','o','f']的 new String对象。

答案 4 :(得分:0)

我认为this链接可以帮助您了解Java String的工作原理

现在考虑以下代码 -

String s = "ABC"; s.toLowerCase(); toLowerCase()方法不会更改s包含的数据“ABC”。而是实例化一个新的String对象,并在构造过程中给出数据“abc”。 toLowerCase()方法返回对此String对象的引用。要使String包含数据“abc”,需要采用不同的方法。

再次考虑以下内容 - s = s.toLowerCase();

现在,String引用了一个包含“abc”的新String对象。类String的声明的语法中没有任何内容将其强制为不可变的;相反,String类的方法都不会影响String对象包含的数据,从而使其不可变。

我真的不明白你的第三个问题。可能会提供一大堆代码,并告诉您的问题是一个更好的选择。希望这会有所帮助。

您还可以查看此blogpost以获得更多理解

[代码示例来自维基。你也可以在那里查看更多信息]