我很熟悉所有局部变量都将存储在堆栈内存中,对象和静态变量将存储在堆中。但是当我遇到以下令我困惑的代码时。
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 ??
答案 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
。