方法中的 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
(没有访问修饰符)方法。
我知道所有局部变量都将存储在堆栈内存中,而对象和静态变量将存储在堆中。但是当我遇到以下代码时,我感到很困惑。
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
(没有访问修饰符)方法。