方法中定义的内部类无法访问方法的局部变量,除非这些局部变量标记为final
。我已查看堆栈中的其他帖子-overflow和java代码牧场,但它们似乎都没有完全回答关于标记变量final如何允许内部类访问方法中的局部变量的问题。
class MyOuter {
private String x = "Outer";
void fly(final int speed) {
final int e = 1;
class FlyingEquation {
public void seeOuter()
{
System.out.println("Outer x is " + x);
}
public void display()
{
System.out.println(e);// line 1
System.out.println(speed);// line 2
}
}
FlyingEquation f=new FlyingEquation();
f.seeOuter();
f.display();
}
public static void main(String args[])
{
MyOuter mo=new MyOuter();
mo.fly(5);
}
}
我发现的解释:
局部变量存储在堆栈中,一旦方法调用完成,就会弹出堆栈,局部变量不可访问,而最终局部变量存储在内存的数据部分中,即使在方法调用结束后,也可能允许JVM
访问它们。 data section of memory
在哪里?我相信所有局部变量final = not 都存储在堆栈中。当从堆栈中删除该方法时,最终变量将被删除。最终变量中的值是否与堆中的对象一起存储?
它不支持非最终字段,因为它们可以通过方法或类进行更改,但这不受支持,因为实际上有两个不同的字段/变量。
答案 0 :(得分:13)
将局部变量标记为final
告诉编译器保证其值在分配后不会更改。这使得编译器可以安全地在内部类中创建合成字段,并在构造内部类实例时将复制局部变量的值放入此合成字段中。从内部类代码中读取局部变量实际上编译为合成字段的读取。
这个技巧不适用于非最终变量,因为它们可能会在实例化内部类后发生变化,并且合成字段会与原始变量不同步。
重要的是要意识到所有内部类是编译器的技巧 - 对于运行时JVM,所有类(顶级,嵌套静态和内部)都被视为相同。
答案 1 :(得分:12)
在内部类的实例化过程中,当方法和类都在作用域内时,内部类将复制作为常量的变量,这意味着该方法可以超出范围,因为内部类只使用变量的副本。 Check out this article
答案 2 :(得分:2)
是的,最终的局部变量保存在堆栈中,并在方法从堆栈中删除时被销毁。正如@Ian所建议的那样,当编译器看到变量的final关键字时,它会在堆上的内部类对象中创建一个值的副本。
答案 3 :(得分:0)
这背后的故事是java的创造者没有足够的时间来完全支持closure。
有几种方法可以使语言支持关闭。例如,在方案中,函数中的自由参数被绑定到与定义函数的词法范围相对应的环境。在强制不可变数据(如SML)的语言中,可以通过将闭包所需的局部变量复制到堆中来实现闭包。
因此,他们创造了一个内部阶级,让一个穷人的封闭版本模仿ML的风格。
答案 4 :(得分:0)
方法本地内部类不能使用外部方法的局部变量,直到该局部变量未声明为final。 我们需要将局部变量声明为final的主要原因是局部变量存在于堆栈中,直到方法在堆栈上,但可能存在内部类的对象仍然存在于堆上的情况。
答案 5 :(得分:0)
所以现在考虑一下。该方法的局部变量存在于堆栈上,并且只存在于方法的生命周期中。我们已经知道局部变量的范围仅限于声明变量的方法。当方法结束时,堆栈帧被吹走,变量是历史。但是即使在方法完成之后,在其中创建的内部类对象仍然可以在堆上存活,例如,如果对它的引用被传递到其他一些代码然后存储在实例变量中。因为只要方法本地内部类对象,局部变量不能保证存活,所以内部类对象不能使用它们。除非局部变量标记为 final 。
最终的局部变量是否存储在堆而不是堆栈中 ?
说明:现在经过SO的一些研究后发现所有本地变量(最终或不)都存储在堆栈中并且去了出 方法执行结束时的范围。
但是关于最终结果 变量JVM
将这些变为常量,因为它们不会在之后发生变化 发起了。当一个内部类尝试访问它们时编译器创建 该变量的副本(不是它自己的变量)进入堆 并在内部类中创建合成字段,所以即使在 方法执行结束,因为内部类是可访问的 有自己的副本。提交的合成字段实际上并不存在 存在于源代码中,但编译器在某些字段中创建这些字段 内部类使这些字段可访问。用简单的词隐藏起来 场。
所以最终变量也存储在堆栈中,但是内部类存储在堆中的变量。