4

所以,这就是我的理解:Java 不支持闭包,因此它会将变量从包含范围复制到嵌套范围中,以便以后可以使用。因为这是一个副本,所以无法同步原始和副本,并且变量被强制为最终变量,因此开发人员无法更改它并期望它被更新。这种理解部分来自这些答案

这将我们带到了这段代码的工作中:

public class SimpleClosure {

    public static void main(String[] args) {
        new SimpleClosure().doStuff();
    }

    public void doStuff() {
        final int number = 3;

        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Integer.toString(number));
            }
        }.start();
    }
}

很好。现在,问题是,最终修饰符只会阻止我更改变量指向的对象,但我可以毫无问题地更改对象。如果进行了“复制”,则不应反映对对象内容的更改。因此,问题是,为什么下面的代码有效?

import java.util.HashMap;
import java.util.Map;

public class StretchingClosure {

    public static void main(String[] args) {
        new StretchingClosure().doStuff();
    }

    public void doStuff() {
        final Map<String, String> map = new HashMap<String, String>();
        map.put("animal", "cat");

        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(map.get("animal")); // Outputs dog See (1)
            }
        }.start();
        map.put("animal", "dog");
    }
}

不用说,我遗漏了一些东西,或者我过度简化了编译器处理这些情况的方式。有人可以请教我吗?

(1):正如@trashgod 所指出的,在大多数平台上大部分时间输出都是正确的,但由于缺乏同步,不能保证。这对于示例来说已经足够了,但通常是不好的做法。

4

4 回答 4

4

不要将变量与对象混淆:确实复制了局部变量的引用,但它仍然引用同一个对象,在您的情况下是映射。有一个广为人知的习语可以解决这个final限制,涉及数组:

final int[] x = {1};
... use in an anonymous instance...
System.out.println(x[0]);
于 2013-01-25T13:08:54.447 回答
1

Java 在那里执行与常规方法参数相同的操作:

方法参数通过引用值传递,因此虽然您无法更改对象本身,但如果它是可变的并提供了改变其内部状态的方法,您可以更改该状态。例如,您不能更改字符串,但可以更改集合中的项目。

引用按值传递 = 引用被复制。对象本身不是。

于 2013-01-25T13:06:49.803 回答
1

该评论// Outputs dog具有误导性,因为它在大多数平台上的大部分时间都是正确的。Map一秒钟是在初始线程上更新的充足时间,但除非对共享数据的访问正确同步,否则无法保证匿名线程中更新值的可见性。有关. _ _java.util.concurrent

于 2013-01-25T13:24:26.807 回答
0

匿名类不获取变量的副本,而是获取对象引用的副本,这就是为什么在 1s 之后您会获得在匿名类之外更改的“正确”值。

于 2013-01-25T13:06:45.583 回答