让我们把它分成几个部分
String s1 = "hello";
该语句创建包含hello的字符串并占用内存中的空间,即在常量字符串池中,并将其分配给引用对象s1
String s2 = s1;
此语句将相同的字符串hello分配给新的引用s2
__________
| |
s1 ---->| hello |<----- s2
|__________|
两个引用都指向同一个字符串,因此输出相同的值,如下所示。
out.println(s1); // o/p: hello
out.println(s2); // o/p: hello
尽管String是不可变的,但赋值是可能的,因此s1现在将引用新值堆栈。
s1 = "stack";
__________
| |
s1 ---->| stack |
|__________|
但是指向hello的s2对象会保持原样。
__________
| |
s2 ---->| hello |
|__________|
out.println(s1); // o/p: stack
out.println(s2); // o/p: hello
由于 String 是不可变的,Java 虚拟机不允许我们通过它的方法修改字符串 s1。它将在池中创建所有新的 String 对象,如下所示。
s1.concat(" overflow");
___________________
| |
s1.concat ----> | stack overflow |
|___________________|
out.println(s1); // o/p: stack
out.println(s2); // o/p: hello
out.println(s1.concat); // o/p: stack overflow
请注意,如果 String 是可变的,那么输出将是
out.println(s1); // o/p: stack overflow
现在你可能会惊讶为什么 String 有像concat()这样的方法来修改。以下片段将消除您的困惑。
s1 = s1.concat(" overflow");
在这里,我们将字符串的修改值分配回s1引用。
___________________
| |
s1 ---->| stack overflow |
|___________________|
out.println(s1); // o/p: stack overflow
out.println(s2); // o/p: hello
这就是为什么 Java 决定String 是最终类的原因否则任何人都可以修改和更改字符串的值。希望这会有所帮助。