String
对象如何在Java中工作?术语“不可变”如何完全适用于字符串对象?为什么我们不通过某种方法看到修改后的字符串,虽然我们操作原始的字符串对象值?
答案 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以获得更多理解
[代码示例来自维基。你也可以在那里查看更多信息]