字符串是不可变的。但是字符串的引用是可变的。这就是它们之间的区别。
因此:
String str = "abc"
str
是一个变量,指的是一个不可变的字符串"abc"。如果我执行以下操作:
str = "def";
str
仍然是一个变量,指向另一个不可变的字符串("def")。无论我如何改变str
所指向的位置,str
都是一个变量。 我不能更改任何字符串的实际内容(因为Java的字符串是设计为不可变的)。每当您执行似乎修改字符串的操作(比如 toUpperCase
),实际上是创建一个包含旧字符串内容副本、按照描述方式修改后的新字符串。
将字符串设置为不可变的目的在于,如果我有一个对字符串的引用,那么这个字符串永远不会发生更改。当传递字符串时,这是一种非常有用的保证。如果我们没有这个保证,我们就必须一直复制字符串,以保护自己免受他人修改。例如,考虑一个name
属性的标准设置函数:
void setName(String n) {
this.name = n;
}
如果字符串不是不可变的,我们就必须这样做:
void setName(String n) {
this.name = new String(n);
}
关于字符串的不可变性,Java的设计者非常聪明地决定让字符串成为不可变对象,以确保我们的字符串副本不会以我们意想不到的方式发生更改。这将导致大量的字符串数据在内存中重复,其中大部分都是不必要的。因此,如果您需要进行任何更改,则会创建一个新字符串而不是就地修改旧字符串。(对于那些确实需要的情况,您可以使用char[]来获得就地修改行为。)
关于您提出的关于str = "abc";
和str = new String("abc")
之间差异的问题,其区别是后者(使用构造函数)保证返回一个新的引用,而前者则没有。例如:
String a = "abc";
String b = "abc";
String c = new String("abc");
a == b
(检查引用是否匹配,而不是字符串)将返回true,因为字面字符串已被内部化。保证a == c
为false,因为我们使用了c
的构造函数。a.equals(b)
和a.equals(c)
都将返回true,因为a
、b
和c
都引用到相等的字符串。