方法中的类如何访问该方法的局部变量?

时间:2015-11-15 09:20:59

标签: java inner-classes

我很熟悉所有局部变量都将存储在堆栈内存中,对象和静态变量将存储在堆中。但是当我遇到以下令我困惑的代码时。

public class Outer {

   private int a =10;

   public void outerMethod() {      
      int x = 30;

       class Inner {
          private int b = 20;
          public void innerMethod() {
            System.out.println("The Value of a is: "+a);
            System.out.println("The Value of b is: "+b);
            System.out.println("The Value of x is: "+x);
          }
       };

       Inner inner = new Inner();
       inner.innerMethod();
    }   
}

上面的代码运行正常。但我的问题是 x 是outerMethod()的局部变量。当我们创建一个Outer类的对象,并在其上调用outerMethod()时, x 将存储在堆栈框架内,我还定义了Inner类的类定义并创建了它的对象,然后我在它上面调用innerMethod()。
因此内部类的对象必须存储在堆内。如果是这样,那么它怎么能访问 x ??

3 个答案:

答案 0 :(得分:3)

Inner只有x是最终版(或者来自Java 8 effectively final)才能访问x。在封面下,编译器会注意到Inner对象使用了哪些外部变量,并将它们传递给Inner构造函数(这些将是合成构造函数参数 - 由编译器生成,因此您不会看他们)。然后它们将成为Inner类的最终(再次,合成=生成并隐藏)。当您在x类代码中引用Inner时,编译器会将其替换为对复制值的x类中的合成Inner字段的引用。

您可以使用此有用命令查看更多详细信息:

javac -d . -XD-printflat MyFile.java

将生成与实际字节码对应的Java代码:

class Outer {

    /*synthetic*/ static int access$000(Outer x0) {
        return x0.a;
    }

    Outer() {
        super();
    }
    private int a = 10;

    public void outerMethod() {
        int x = 30;
        /*synthetic*/ {
        }
        ;
        Outer$1Inner inner = new Outer$1Inner(this, x);
        inner.innerMethod();
    }
}

class Outer$1Inner {
    /*synthetic*/ final Outer this$0;
    /*synthetic*/ final int val$x;

    Outer$1Inner(final Outer this$0, /*synthetic*/ final int val$x) {
        // Notice the synthetic references to the surrounding Outer object
        // and to the x local variable
        // Both become fields of Inner

        this.this$0 = this$0;
        this.val$x = val$x;
        super();
    }
    private int b = 20;

    public void innerMethod() {
        System.out.println("The Value of a is: " + Outer.access$000(this$0));
        System.out.println("The Value of b is: " + b);
        System.out.println("The Value of x is: " + val$x);
    }
}

您还可以注意到Outer.a是如何被访问的 - 因为Inner被编译为"普通的旧Java类"它必须尊重可见性修饰符,因此JVM不允许直接访问私有字段Outer.a。但是,在编译期间,编译器会注意到您要从Inner访问此字段,并且由于它是内部类,因此将生成访问器方法Outer.access$000()。由于Inner引用了Outer对象,因此可以调用Outer.access$000(referenceToOuter)并获取Outer.a的值。

答案 1 :(得分:1)

确实,Inner类将被编译到同一个包中的单独类中。你是对的,通常你不应该访问Outer类的私有方法和字段。

但是,编译器将在字节码级别创建一些合成的包私有方法来实现此功能。现在,您的Inner类可以访问这些自动生成的(隐藏自您)package-private(没有访问修饰符)方法。

答案 2 :(得分:0)

Inner类对象具有指向您的Outer类对象的链接。 Inner正在做的事实上是捕获或关闭你的本地变量x,从而增加它的生命周期"因此,您的Inner班级可以访问x