考虑下面的例子。

String str = new String();

str  = "Hello";
System.out.println(str);  //Prints Hello

str = "Help!";
System.out.println(str);  //Prints Help!

在Java中,String对象是不可变的。那么为什么对象str可以被赋值为"Help!"呢?这难道不是与Java中字符串的不变性相矛盾吗?有人能给我解释一下不变性的确切概念吗?

编辑:

好的。我现在明白了,但还有一个问题。下面的代码呢:

String str = "Mississippi"; 
System.out.println(str); // prints Mississippi 

str = str.replace("i", "!"); 
System.out.println(str); // prints M!ss!ss!pp! 

这是否意味着将再次创建两个对象(“Mississippi”和“M!ss!ss!pp!”),并且在replace()方法之后引用str指向不同的对象?


当前回答

关于你问题的替换部分,试试这个:

String str = "Mississippi"; 
System.out.println(str); //Prints Mississippi 

String other = str.replace("i", "!"); 
System.out.println(str); //still prints Mississippi 
System.out.println(other);  // prints M!ss!ss!pp!

其他回答

字符串类是不可变的,你不能改变不可变对象的值。 但在String的情况下,如果你改变了String的值,它会在字符串池中创建新的字符串,而不是旧的字符串引用。通过这种方式,字符串是不可变的。 举个例子,

String str = "Mississippi";  
System.out.println(str); // prints Mississippi 

它将创建一个字符串“Mississippi”,并将其添加到字符串池 所以现在str指向密西西比。

str = str.replace("i", "!");  
System.out.println(str); // prints M!ss!ss!pp! 

但经过上述操作, 另一个字符串将被创建"M!ss!ss!pp!" 它将被添加到String池。而且 现在str指向M!ss!ss!pp!而不是密西西比州。

通过这种方式,当你改变string对象的值时,它会创建一个新的对象并将其添加到string池中。

让我们再看一个例子

String s1 = "Hello"; 
String s2 = "World"; 
String s = s1 + s2;

上面的3行代码将向字符串池中添加3个字符串对象。 1)你好 2)世界 3) HelloWorld

str第一次引用的字符串对象没有被改变,你所做的只是让str引用一个新的字符串对象。

STR不是一个对象,而是一个对象的引用。“Hello”和“Help!”是两个不同的String对象。因此,str指向一个字符串。你可以改变它指向的东西,但不能改变它指向的东西。

以下面的代码为例:

String s1 = "Hello";
String s2 = s1;
// s1 and s2 now point at the same string - "Hello"

现在,我们对s1做什么都不会影响s2的值。它们引用同一个对象——字符串“Hello”——但该对象是不可变的,因此不能被修改。

如果我们这样做:

s1 = "Help!";
System.out.println(s2); // still prints "Hello"

这里我们看到了改变对象和改变引用之间的区别。S2仍然指向我们最初设置s1所指向的对象。将s1设置为“Help!”只会改变引用,而它最初引用的String对象保持不变。

如果字符串是可变的,我们可以这样做:

String s1 = "Hello";
String s2 = s1;
s1.setCharAt(1, 'a'); // Fictional method that sets character at a given pos in string
System.out.println(s2); // Prints "Hallo"

编辑以回应OP的编辑:

如果你看一下String.replace(char,char)的源代码(也可以在JDK安装目录的src.zip中找到——一个专业的提示是,当你想知道某些东西是如何工作的时候,可以查看那里),你可以看到它所做的如下所示:

如果当前字符串中出现了一次或多次oldChar,则复制当前字符串,其中所有出现的oldChar都被newChar替换。 如果oldChar在当前字符串中不存在,则返回当前字符串。

所以,是的,“密西西比”。replace('i', '!')创建一个新的String对象。同样,以下观点成立:

String s1 = "Mississippi";
String s2 = s1;
s1 = s1.replace('i', '!');
System.out.println(s1); // Prints "M!ss!ss!pp!"
System.out.println(s2); // Prints "Mississippi"
System.out.println(s1 == s2); // Prints "false" as s1 and s2 are two different objects

你现在的作业是看看如果你改变s1 = s1,上面的代码会做什么。替换(“我”、“!”);到s1 = s1。替换(“问”、“!”);:)


1实际上,改变字符串(和其他不可变对象)是可能的。它需要反思,而且非常非常危险,永远不应该使用,除非你真的有兴趣破坏程序。

Java中的String在Immutable和Final中只是意味着它不能被更改或修改:

案例1:

class TestClass{  
 public static void main(String args[]){  
   String str = "ABC";  
   str.concat("DEF");  
   System.out.println(str);  
 }  
} 

输出:美国广播公司(ABC) 原因:对象引用str没有改变,实际上是一个新对象 在池中创建了“DEF”,它根本没有引用 (也就是失去了)。

案例2:

class TestClass{  
 public static void main(String args[]){  
   String str="ABC";  
   str=str.concat("DEF");  
   System.out.println(str);  
 }  
}  

输出:六边形ABCDEF 原因:在这种情况下,str现在引用一个新对象“ABCDEF” 因此它打印ABCDEF,即前一个str对象“ABC”在池中丢失,没有引用。

字符串是不可变的,这意味着你不能改变对象本身,但是你可以改变对对象的引用。当你调用a = "ty"时,你实际上是将a的引用更改为一个由String文字"ty"创建的新对象。改变一个对象意味着使用它的方法来改变它的一个字段(或者字段是公共的而不是final的,这样它们就可以从外部更新而不需要通过方法访问它们),例如:

Foo x = new Foo("the field");
x.setField("a new field");
System.out.println(x.getField()); // prints "a new field"

而在一个不可变类(声明为final,以防止通过继承修改)(它的方法不能修改它的字段,而且字段总是私有的,建议是final),例如String,你不能改变当前的String,但你可以返回一个新的String,即:

String s = "some text";
s.substring(0,4);
System.out.println(s); // still printing "some text"
String a = s.substring(0,4);
System.out.println(a); // prints "some"