Java 字符串-它们是如何工作的?

Java 字符串-它们是如何工作的?,java,string,Java,String,String对象在Java中如何工作?术语“不可变”如何准确地应用于字符串对象?尽管我们对原始字符串对象值进行操作,但为什么我们在通过某些方法后看不到修改后的字符串?来自: String类是不可变的,因此一旦创建了String对象,就不能对其进行更改。String类有许多方法,下面将讨论其中一些方法,它们似乎可以修改字符串。由于字符串是不可变的,这些方法真正做的是创建并返回一个包含操作结果的新字符串 Java中的字符串是不可变的(状态一经创建就不能修改)。这为优化提供了机会。一个例子是字符串内部

String
对象在Java中如何工作?术语“不可变”如何准确地应用于字符串对象?尽管我们对原始字符串对象值进行操作,但为什么我们在通过某些方法后看不到修改后的字符串?

来自:

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


Java中的字符串是不可变的(状态一经创建就不能修改)。这为优化提供了机会。一个例子是字符串内部处理,其中字符串文本在字符串池中维护,只有当特定字符串文本在池中不存在时,才会创建新的字符串对象。如果字符串文本已经存在,则返回一个引用。这只能是因为字符串是不可变的,所以您不必担心持有引用的对象会改变它

看似修改字符串的方法实际上返回一个新实例。一个例子是字符串连接:

String s = "";
for( int i = 0; i < 5; i++ ){
    s = s + "hi";
}
String s=”“;
对于(int i=0;i<5;i++){
s=s+“高”;
}
内部实际发生的情况(编译器更改它):

String s=”“;
对于(int i=0;i<5;i++){
StringBuffer sb=新的StringBuffer();
某人追加;
某人加上“嗨”;
s=sb.toString();
}

您可以清楚地看到,新实例是通过toString方法创建的(请注意,直接使用StringBuffers可以提高效率)。与字符串不同,StringBuffers是可变的。

字符串有一个私有的final
char[]
。创建新字符串对象时,也会创建并填充数组。以后不能[从外部]访问或修改它[实际上可以通过反射完成,但我们将把它放在一边]

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

我们看不到修改过的字符串,因为它是不可变的,同一个对象永远不会被更改,无论您如何处理它[除了用反射修改它]。因此,“cow”+“horse”将创建一个新的字符串对象,而不是修改最后一个对象

如果您定义:

void foo(String arg) {
  arg= arg + " horse";
}
你打电话:

String str = "cow";
foo(str);
调用的
str
不会被修改[因为它是对同一对象的原始引用!]当您更改
arg
时,您只是将其更改为引用另一个字符串对象,而没有更改实际的原始对象。因此,
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);
}
每个对象都有状态。字符串对象的状态是组成字符串的字符数组,例如,字符串“foo”包含数组['f','o','o']。因为字符串是不可变的,所以此数组永远不能以任何方式、形状或形式更改

每个类中要更改字符串的每个方法都必须返回一个新字符串,该字符串表示旧字符串的已更改状态。也就是说,如果您尝试反转“foo”,您将得到一个内部状态为['o','o','f']的新字符串对象。

我认为该链接将帮助您了解Java字符串的实际工作原理

现在考虑下面的代码-< /P>

String s=“ABC”;
s、 toLowerCase()
方法toLowerCase()不会更改包含的数据“ABC”。相反,将实例化一个新的字符串对象,并在其构造过程中为其提供数据“abc”。toLowerCase()方法返回对此字符串对象的引用。要使字符串s包含数据“abc”,需要一种不同的方法

再次考虑下面的代码:<代码> S= S.ToWoReCase](<)代码>

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

我不太明白你的第三个问题。可能是提供一段代码,告诉你的问题是一个更好的选择。希望这有帮助

您也可以对此进行研究以获得更多的理解


[代码示例取自wiki。您也可以在那里查看更多信息]

这一研究不足的三部分问题如何获得投票的可能重复?“非常感谢用示例进行解释。”是的(干巴巴的),这也是经过充分研究的问题。当搜索C.O.W copy on write Java字符串时,使用cow作为字符串示例会让人困惑
sb.append(“hi”)
上面语句中的“hi”是否添加到字符串池中?
    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);
}