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

How a class inside a method access a local variable of that method?

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

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 class 的对象并在其上调用 outerMethod() 时,x 将存储在堆栈帧中,我还定义了一个 class 定义内部 class 并创建它的对象,然后我在其上调用 innerMethod() 。
因此内部 Class 的对象必须存储在堆中。如果是这样那么它怎么能访问 x??

Inner class 对象有一个 link 到您的 Outer class 对象。 Inner 所做的本质上是捕获或关闭您的局部变量 x,从而增加它的 "lifetime",因此您的 Inner class 可以访问 x .

Inner 只能访问 x 如果 x 是最终的(或者,从 Java 8 effectively final)。在幕后,编译器会注意到 Inner 对象使用了哪些外部变量,并将它们传递给 Inner 构造函数(这些将是合成构造函数参数 - 由编译器生成,因此您不会看他们)。然后它们将成为 Inner class 的最终(同样,合成 = 生成并向您隐藏)字段。当您在 Inner class 代码中引用 x 时,编译器会将其替换为对 Inner [=43] 中的合成 x 字段的引用=] 复制值的位置。

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

javac -d . -XD-printflat MyFile.java

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

class Outer {

    /*synthetic*/ static int access[=11=]0(Outer x0) {
        return x0.a;
    }

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

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

class OuterInner {
    /*synthetic*/ final Outer this[=11=];
    /*synthetic*/ final int val$x;

    OuterInner(final Outer this[=11=], /*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[=11=] = this[=11=];
        this.val$x = val$x;
        super();
    }
    private int b = 20;

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

您还可以注意到如何访问 Outer.a - 因为 Inner 被编译为 "plain old Java class" 它必须遵守可见性修饰符,因此 JVM 不允许直接访问到私人领域 Outer.a。然而,在编译过程中,编译器会注意到你想从 Inner 访问这个字段,并且由于它是一个内部 class,将生成一个访问器方法 Outer.access[=26=]0()。由于 Inner 具有对 Outer 对象的引用,它可以调用 Outer.access[=29=]0(referenceToOuter) 并获取 Outer.a.

的值

Inner class 确实会在同一个包中编译为单独的 class。你是对的,通常你不应该访问 Outer class.

的私有方法和字段

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