字符串 – 它们如何工作?

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

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

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

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

如果你定义:

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

你打电话给:

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

调用的str不会被修改[因为它是对同一个对象的原始引用!]当你更改了arg ,你只需将它改为引用另一个String对象,并且不改变实际的原始对象。 所以str ,将是同一个对象,没有改变,仍然包含"cow"

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

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

从教程 :

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

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是可变的。

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

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

我认为这个链接将帮助您了解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对象包含的数据,从而使其不可变。

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

您还可以查看此博客文章以获得更多理解

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