在以下代码中:
public class Main { Emp globalEmp; public void aMethod() { final int stackVar = 10; globalEmp = new Emp() { public void doSomeThing() { System.out.println("stackVar :" + stackVar); } }; } public static void main(String[] args) { Main m = new Main(); m.aMethod(); m.globalEmp.doSomeThing(); } } interface Emp{ public void doSomeThing(); }
据我所知,将执行以下操作:
> Main m = new Main(); :将创建一个新的Main类实例,并将globalEmp设置为null.
> m.aMethod(); :调用aMethod,包括将其实例变量stackVar复制到堆栈中,然后创建Emp类的新实例并将其分配给globalEmp实例.
>当方法aMethod到达结束时,局部变量stackVar将从堆栈中推出.
> m.globalEmp.doSomeThing(); :将在globalEmp变量指向的先前创建的对象上调用函数doSomeThing.并且因为这个函数doSomeThing正在访问一个本地变量stackVar,它假定不会从缓存中弹出,它应该抛出一些错误说明.
那么,Java运行时如何真正起作用呢?
编辑:
>由于运行时将创建最终局部变量的浅表副本(下面的每个答案),为什么它也不允许访问非最终变量?
>你能否提供一些链接(在规格或官方地方)?
解决方法
通常,匿名内部类将为其生成一个构造函数,以获取在匿名内部类的类中引用的局部变量的值.生成的代码转为:
globalEmp = new Emp() { ... };
成:
globalEmp = new Main$1(stackVar);
换句话说,它会创建值的副本.之后,原始值来自的变量无关紧要.
在您实际显示的情况下,它不需要因为10是常量 – 但这是捕获变量的一般方式.
编辑:回应你的编辑…
变量必须是最终的,这样就不会出现混淆 – 如果你可以改变它们,一些开发人员可能希望在匿名类中可以看到任何变化,就像在其他闭包实现中一样.
至于围绕这个的官方文档,我在Java语言规范中看到的最接近的是section 8.1.3,尽管它没有讨论各种决策背后的动机.