Java 是一种基于references 的语言。这只是 pointers 的另一种说法。
当你写作时:
String foo = "hello";
这只是语法糖:
String foo; // [1]
foo = "hello"; // [2]
第 1 行声明了一个名为“foo”的变量(一个指针)。
第 2 行做了两件事:它创建了一个全新的对象。 foo 然后更新以引用它。就像"hello" 是宝藏,foo 是藏宝图。 foo = "Hello"创造新的宝藏,把它埋在沙子里,然后在你的纸上画出宝藏的地图;您标记为foo 的那张纸。 “foo”不是宝,说这个字符串是“foo”字符串是不正确的。不是——它只是宝物,而宝物是没有名字的。不可能有通向它的地图(这意味着垃圾收集器最终会挖掘它并摆脱它),可能有一千个通向它的地图,以及介于两者之间的任何东西。在上面的 sn-p 中,有一张地图通向它。你的地图。你打电话给foo的那个。
但您可以与他人共享该地图。然后他们也能找到你的宝藏。
'locals 是不可变的'。是的。他们是。但是,您的本地是foo 变量,而foo 是不是宝藏。是地图。这是你的地图。没有人能惹它。 map 看起来会有所不同的唯一方法是,如果您在自己的代码中的某个地方编写 foo = 。将foo 传递给其他方法的次数永远不会改变该映射。
您阅读的文字所指的是,唯一不可变的是您的地图。地图所指的宝藏?谁知道。如果您将地图交给其他人,他们无法更改您的地图,但他们可以复制它。他们可以跟随它。他们可以挖掘那个宝藏,并将其粉碎成碎片。它们不会影响你的地图,但如果你跟着地图走,你会在那里找到什么?如果您将宝藏的位置与其他任何人分享,现在可能会完全不同。
现在,对于弦乐来说,这是一个有争议的问题:弦乐宝藏是无敌的。它们不能以任何方式被粉碎或修改。它们是不可变的对象,没有改变它的方法,也没有公共(非最终)字段。但并不是所有的物体都是这样的。一些宝物可以改变或粉碎。比如一个简单的列表:
List<String> x = new ArrayList<String>();
x.add("Hello");
foo(x);
System.out.println(x);
在上面的代码中你不知道它打印了什么,因为你不知道 foo 做了什么。你知道你的x不可能打印null。 null 指的是你有一张空白的藏宝图,没有人可以乱动你的藏宝图。但是foo可以把宝藏弄乱了:
public void foo(List<String> x) {
x = List.of();
}
这不会做任何事情。此方法获取您的藏宝图的副本。然后它会创造新的宝藏,把它埋在沙子里,用橡皮擦到它的x 藏宝图(曾经保存你的副本),然后在上面绘制一个全新的藏宝图,X 标记了它的位置新创造的宝藏。这对您的地图或地图所指向的宝藏绝对没有任何作用。您的代码将继续打印[Hello]。
但是:
public void foo(List<String> x) {
x.add("World");
}
这是完全不同的。这将获取您提供的藏宝图副本,跟随它并挖掘(. 是 java-ese 为:跟随地图并挖掘)。然后它打开箱子,把“世界”放在那里(从技术上讲,它把“世界”宝藏的地图放在那里,字符串也是对象,因此是基于引用的)。
如果您稍后按照地图进行挖掘,您就会看到这一点。您的代码将打印[Hello, World]。
这就是文字所要表达的。通过制作副本、使用不可变对象或意识到如果您已共享地图,您的地图指向的任何宝藏可能已被其他代码更改的概念来避免这种情况。