java inner/outer class questions about outer class private variables access
我有以下Java类:
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Outer
{
private Integer a ;
private Long b ;
class Inner
{
public void foo ()
{
System. out. println("a and b are" + a +"" + b );
}
}
} |
当我在外$inner和外$inner上运行javap时,我得到以下信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| C :\test >javap Outer
Compiled from "Outer.java"
class Outer extends java. lang. Object{
Outer ();
static java. lang. Integer access$000 (Outer );
static java. lang. Long access$100 (Outer );
}
C :\test >javap Outer$Inner
Compiled from "Outer.java"
class Outer$Inner extends java. lang. Object{
final Outer this$0 ;
Outer$Inner (Outer );
public void foo ();
} |
我有两个问题:
1)为什么Java编译器生成在外部类中访问外部变量的静态方法来访问它的私有变量?为什么内部类可以通过它的$0成员轻松调用实例方法?
2)为什么这是0美元的内舱决赛?如果不是最终结果会怎样?
谢谢和问候。
- IMHO这个机制(在Java 1.1中添加)是个错误。我试图通过声明使用默认(包)访问的a和b来避免这种混乱。您不会丢失任何东西,因为编译器的黑客程序使得fields包仍然可见。
非静态内部类具有对外部类实例的隐式引用。这是作为对外部类的final引用来实现的。如果不是final,技术上可以在实例化后修改。
外部类是隐式传递的,这就是为什么内部类上的任何构造函数都具有外部类的隐式参数的原因,这就是如何传入this$0。
编辑:对于access$000方法,关键的线索是它们是包访问,它们以Outer作为参数。所以当Inner中的代码调用时,比如说Inner.this.a实际上是在调用Inner.access$000(this$0)。因此,这些方法可以让外部阶级的private成员进入内部阶级。
- 为什么access$...()方法需要是static方法?你提供的答案是针对第二个问题,以及从未问过的问题。
- @醋:见更新。
- 嗨,克莱特斯,谢谢你的回答。我理解,给定了"access*"方法,这样内部类就可以访问外部类的私有成员。但为什么这些方法是静态的呢?为什么它们不能成为实例方法?因为内部类有这个$0,所以那里的代码可以很容易地调用这个$0.access$000,如果它是一个实例方法,而不是调用outer.access$000(这个$0)。
- 如果它们不是静态的,那么您将不得不担心每个可能的子类意外地覆盖它们。只有静态方法提供所需的特定于类的语义。
- 醋的反应更好地解释了…我应该向下滚动。
- 感谢克莱图斯的更新。
1)它们必须是static,以便在某些子类中不被覆盖。希望你能理解。
施里尼,从你的评论来看,似乎有必要解释这些事情以避免一些误解。首先,要知道static方法不能被重写。覆盖在对象中是排他的,它是为了促进多态性。而静态方法属于类。找到了一些很好的资源来支持我的论点,并且让您理解静态方法不能被重写。
- 可以在Java中重写静态方法吗?
- 为什么静态方法不能是抽象的
现在,对于您的第二个反驳,您说得对,它们具有包级别的访问权限,并且不能在包外部的子类中被重写。但我不知道为什么您忽略了子类/es存在于同一个包中的情况。这是一个有效的例子,国际海事组织。事实上,在一个真实的作品中命名一个像access$000()这样的方法是荒谬的。但不要低估意外超越的可能性。有些情况下,比如说SubOuter的子类Outer本身也有一个内部类。我自己也没试着去想那个案子,只是猜测而已。
2)即使你认为它不会被修改,技术上也有可能,正如克莱特斯已经指出的那样,使用final可以很容易地由编译器进行优化。
- 嗨,醋,谢谢你的回答。但是子类可以重写其父类的静态方法;另外,在这种特定情况下,这些方法是包访问,因此包外部的子类无论如何也不能重写它们。所以我不明白你的答案。
- @史利尼1000:不,你是在做傻事。见我的附录。
- 嗨,醋,谢谢你的解释。我已经编写了一个超类/子类,并在做出之前的答复之前对它进行了测试;但是我没有考虑过超类引用指向子类对象的情况。谢谢你指出这一点。如果我的回答冒犯了你,我会向你道歉;这不是我的意图。
- 很遗憾,问题1仍未得到回答。非虚拟成员函数(即Java语言中的最终方法)和静态或独立功能之间没有技术差异:编译器确切地知道要跳转到的最终代码。派生类无法更改这一点。我更倾向于猜测编译器编写人员刚刚决定OO符号在这些实现细节中是不相关的,并且使用独立的access$xxx函数看起来更自然,您越接近裸机编程。