内部类和局部变量

14

如果我在方法内定义的 局部变量 需要在其中定义的 内部类 中使用,为什么需要将其声明为 final

例如:

class MyOuter2 {

private String x = "Outer2";

void doStuff() {
    final String y = "Hello World";

    final class MyInner {

        String z = y;

        public void seeOuter() {
            System.out.println("Outer x is "+x);
            System.out.println("Local variable is "+y);
            MyInner mi = new MyInner();
            mi.seeOuter();
        }
    }
}
为什么字符串 y 需要是一个 final 常量?这对代码有什么影响吗?


看看这个讨论是否有帮助:http://techtracer.com/2008/04/14/mystery-of-accessibility-in-local-inner-classes/ - kosa
Java中关于局部final变量的问题 - Lion
6个回答

17

本地变量始终驻留在堆栈中,当方法结束时,所有本地变量都会消失。

但是,您的内部类对象可能仍然位于堆中,即使方法结束了(例如实例变量仍然持有引用),因此在这种情况下,它无法访问您的本地变量,因为它们已经消失,除非您将它们标记为final。


13

答案是两者处于不同的作用域。因此,内部类访问它之前该变量可能会改变。将其声明为final可以防止这种情况发生。


3
Java 8中发生了变化:http://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html#accessing-members-of-an-enclosing-class - ohcibi

9

这是因为函数内部的内部类实际上会复制局部变量,因为局部变量可能在函数调用后被销毁,而本地类的实例仍然存在。要使两个局部变量的副本始终具有相同的值(使它们看起来相同),您必须将变量声明为final。


有数十甚至上百个解释为什么编译器要制定这个规则。这是唯一一个既有意义又容易理解的解释。 - zubergu

3

我认为这是为了防止程序员犯错而设定的。这样,编译器会在你离开该方法后提醒程序员该变量将不再可访问。这在循环中至关重要。想象一下这段代码:

public void doStuff()
{
    InnerClass cls[] = new InnerClass[100];
    for (int i = 0; i < 100; ++i)
    {
        cls[i] = new InnerClass()
        {
            void doIt()
            {
                System.out.println(i);
            }
        };
    }
}

当调用doIt()方法时,会打印什么内容?i在循环中发生了变化。 为了避免混淆,你需要把变量复制到一个局部变量中或创建一个final变量,但这样做就无法进行循环。

1

你的内部类是final的,因此你不应该能够从内部修改任何final实体。


0

根据Java内存模型,使用final变量可以保证final变量始终被初始化。 如果我们尝试使用未初始化的局部变量,则会出现错误。 使用final可以确保内部类使用的局部变量已经被初始化。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接