在进一步讨论不可变性之前,让我们先看一下该类及其功能,然后再得出任何结论。String
这是工作原理:String
String str = "knowledge";
像往常一样,这将创建一个包含的字符串,并为其分配一个引用。够简单吗?让我们执行更多功能:"knowledge"
str
String s = str; // assigns a new reference to the same string "knowledge"
让我们看看下面的语句是如何工作的:
str = str.concat(" base");
这会将字符串追加到 。但是等等,这怎么可能,因为对象是不可变的?令你惊讶的是,是的。" base"
str
String
执行上述语句时,VM 采用 的值,即 和附加 ,给我们值 。现在,由于 s 是不可变的,VM 无法将此值分配给 ,因此它会创建一个新对象,为其提供值 ,并为其提供引用 。String str
"knowledge"
" base"
"knowledge base"
String
str
String
"knowledge base"
str
这里需要注意的重要一点是,虽然对象是不可变的,但其参考变量不是。因此,这就是为什么在上面的示例中,引用引用新形成的对象。String
String
在上面的例子中,我们有两个对象:第一个是我们用值创建的,指向,第二个对象指向。但是,从技术上讲,我们有三个对象,第三个是语句中的文字。String
"knowledge"
s
"knowledge base"
str
String
"base"
concat
有关字符串和内存使用情况的重要事实
如果我们没有另一个引用呢?我们会失去它。但是,它仍然存在,但由于没有引用,将被视为丢失。再看一个例子s
"knowledge"
String
String s1 = "java";
s1.concat(" rules");
System.out.println("s1 refers to "+s1); // Yes, s1 still refers to "java"
发生了什么事情:
- 第一行非常简单:创建一个新的并引用它。
String
"java"
s1
- 接下来,VM 将创建另一个新 ,但没有任何内容引用它。因此,第二个立即丢失。我们无法触及它。
String
"java rules"
String
引用变量仍引用原始 。s1
String
"java"
几乎每个方法,应用于一个对象以修改它,都会创建新对象。那么,这些物体去哪儿了呢?好吧,这些都存在于内存中,任何编程语言的关键目标之一都是有效利用内存。String
String
String
随着应用程序的增长,String
文本占用大面积内存是很常见的,这甚至会导致冗余。因此,为了使Java更有效率,JVM预留了一个特殊的内存区域,称为“字符串常量池”。
当编译器看到文本时,它会在池中查找 。如果找到匹配项,则对新文本的引用将定向到现有文本,并且不会创建新对象。现有的只是有一个额外的参考。以下是使对象不可变的要点:String
String
String
String
String
String
在常量池中,一个对象可能具有一个或多个引用。如果多个引用指向同一个 String
,甚至不知道它,那么如果其中一个引用修改了该 String
值,那将是不好的。这就是 String
对象是不可变的。String
String
好吧,现在你可以说,如果有人覆盖了String
类的功能怎么办?这就是 String
类被标记为 final
的原因,这样就没有人可以重写其方法的行为。