考虑下面的例子。

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指向不同的对象?


当前回答

不可变性意味着实例化对象的值不能改变,你永远不能把“Hello”变成“Help!”

变量str是一个对象的引用,当你给str赋一个新值时,你没有改变它引用的对象的值,你引用的是一个不同的对象。

其他回答

我建议你读一读《Cup Size》——一个关于变量和值传递的故事(续《Cup Size》)。这对阅读上面的文章有很大帮助。

你读过吗?是的。好。

String str = new String();

这将创建一个名为“str”的新“远程控制”,并将其设置为值new String()(或“”)。

例如,在内存中创建:

str --- > ""

str  = "Hello";

这将更改远程控制“str”,但不会修改原始字符串“”。

例如,在内存中创建:

str -+   ""
     +-> "Hello"

str = "Help!";

这将更改远程控件“str”,但不会修改原始字符串“”或远程控件当前指向的对象。

例如,在内存中创建:

str -+   ""
     |   "Hello"
     +-> "Help!"

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

如果HELLO是你的字符串,那么你不能把HELLO改成HILLO。这个性质叫做不可变性。

你可以有多个指针字符串变量指向HELLO字符串。

但是如果HELLO是char Array,那么你可以将HELLO改为HILLO。例如,

char[] charArr = 'HELLO';
char[1] = 'I'; //you can do this

编程语言具有不可变的数据变量,因此可以作为键、值对中的键使用。

我可以说,不可变性是你不能改变字符串本身。假设你有字符串x,它的值是“abc”。现在您不能更改字符串,也就是说,您不能更改“abc”中的任何字符/s。

如果你必须改变字符串中的任何字符,你可以使用一个字符数组并改变它或使用StringBuilder。

String x = "abc";
x = "pot";
x = x + "hj";
x = x.substring(3);
System.out.println(x);

char x1[] = x.toCharArray();
x1[0] = 's';
String y = new String(x1);
System.out.println(y);

输出:

hj
sj

这里的不可变性意味着实例可以指向其他引用,但字符串的原始内容不会在原始引用处被修改。 让我用你举的第一个例子来解释。 第一个str指向“Hello”,这是可以的。 第二次它指向“救命!”。 这里str开始指向“Help!”,“Hello”字符串的引用丢失了,我们无法恢复。

事实上,当str试图修改现有内容时,将生成另一个新字符串,str将开始指向该引用。 所以我们看到,在原始引用处的字符串没有被修改,但在它的引用处是安全的,对象的实例开始指向不同的引用,所以不变性是保留的。