Java字符串的工作原理是什么?

12

我试图理解Java字符串是如何不可变的。我知道这应该是一个简单的概念,但在阅读了几个在线网页后,我仍然不太明白。

我不明白Java字符串是如何“不可变”的。我目前有以下代码:

public static void main(String[] args) {

  String name = "Jacob Perkins";

  System.out.println( name );

  name = name + "!";

  System.out.println( name );

}

我的输出结果如下:

Jacob Perkins
Jacob Perkins!

如果字符串应该是不可变的,为什么会发生这种情况?我为什么能够重新分配一个值给字符串?


“String”变量并不是不可变的(因为它们会改变!)你已经创建了一个新的字符串,并将其引用分配给变量“name”。 - dlev
我还是不明白,为什么在Java字符串中会看到“不可变”的术语? - user2301187
两个变量可以指向同一个字符串对象。如果字符串是可变的,那么一个变量对字符串所做的更改可以通过另一个变量的引用看到。由于它们不是这样,每个变量都知道它指向的字符串在“不看”的情况下不能“改变”。这样做是因为可变字符串会很可怕并且会增加很多复杂性,例如,每当您想要一个不会更改的字符串副本时,您必须记住制作字符串的副本,否则稍后可能会发生奇怪的事情,甚至可能是不安全的事情! - Patashu
但是看起来 name = name + "!" 改变了原始的 name 变量的值。我仍然不理解。 - user2301187
@user2301187,我已经发布了一个解释它的答案。 - Patashu
这意味着字符串本身是不可更改的。当您使用“+”运算符时,实际上会创建一个新的字符串,并将“+”后面的部分添加到其中,因此您从未修改当前的字符串(因此是“不可变的”),而是创建了一个新的字符串。 - Martin Tuskevicius
7个回答

35

让一张图片为您解释:

字符串示例

在左侧,你有这个变量,实际上它是一个引用

  1. String name = "Jacob Perkins;" 创建了字符串 "Jacob Perkins",并且 name 指向该字符串。
  2. name = name + "!"; 创建了一个新的字符串 "Jakob Perkins!",引用现在指向新的字符串。但旧的字符串保持不变,因为字符串是不可变的。

5
一旦创建了字符串,它本身就不能被更改。你的代码示例所做的是用name中先前的内容和一个感叹号构造一个新的字符串来替换name中原来的字符串。(name的原始内容不再由任何变量引用,将最终被垃圾收集器清除。)
如果你检查已编译的代码(或通过调试器逐步运行),你会发现代码name + "!"被编译成StringBuilder对象的创建和对该对象进行的一些操作。
也就是说,字符串是不可变的,但变量name并不是。它的值会改变,指向不同的字符串。字符串本身永远不会改变。

1
也许这会帮助你理解。
假设你有一个类 Point (java.awt.Point)。 假设你有一个实例 p:
Point p = new Point(0,0);

然后,您可以创建一个新变量y,引用与p相同的对象,如下所示:

Point y = p;

如果您更改p的值,也会更改y的值。因为Point类是可变的。
p.setLocation(1,1);

它使得y参照位置1,1。使用String类不会发生这种情况。
String a = "123";
String b = a;

如果你执行 a = a + "4"; a 的新值将会是 "1234",但 b 仍然是 "123";
它们引用的对象并没有改变,只是 a 现在指向另一个对象。

1
字符串对象是不可变的,只有变量引用会改变。
在你的例子中,“Jacob Perkins”对象仍然存在,一个新对象“Jacob Perkins!”被创建。
名称变量指向新对象。

0

在Java中,有引用和值。

当你说

String foo = "John Smith";

foo是一个变量,保存了一个引用。该引用指向包含“John Smith”的对象或值。

变量保存的引用是可变的。我可以这样做:

foo = "Jack Smith";

我已经改变了引用。但是如果我写出这样的代码:

String foo = "John Smith";
String bar = foo; //bar's reference is a copy of foo's reference - the same value
foo = "Jack Smith";
System.out.println(bar); //prints John Smith

我们可以看到,尽管我们改变了foo的值,bar的值并没有改变。为什么呢?简单来说,当我们写下foo = "Jack Smith";时,我们使foo指向了一个新的字符串。我们没有通过foo修改字符串,因为在Java中没有任何方法可以改变一个字符串,也不可能改变它的值。相反,一个新的字符串被返回,并且变量指向了这个新的、不可变的字符串。这样,只有通过该变量才能修改一个字符串变量'指向'的对象,这是Java语言的一个重要特性,也是它所保证的。


0
实际发生的是创建了3个字符串对象。 "Jacob Perkins","!"和"Jacob Perkins!"。您并没有真正修改"Jacob Perkins"实例。您只是将名称变量的引用从"Jacob Perkins"实例更改为"Jacob Perkins!"。

我不明白您所说的“更改名称变量的引用”的意思。 - user2301187
在第一个println()中,name变量指向“Jacob Perkins”字符串对象。由于字符串是不可变的,该对象并没有真正改变。相反,创建了两个新的字符串对象,“!”和“Jacob Perkins!”。然后,将name变量指向了“Jacob Perkins!”字符串对象。实际上,您创建了3个字符串对象,其中没有一个被静音。 - renz

0
String name = "Jacob Perkins";

String name2 = name + "!";
name.substring(5); // or wather syntax is

不要更改变量名称


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接