Java内部可见性难题

Ger*_*ard 10 java inner-classes

考虑以下情况:

public class A {
  public A() { b = new B(); }
  B b;
  private class B { }
}
Run Code Online (Sandbox Code Playgroud)

从Eclipse中的警告我引用:java complier通过合成访问器方法模拟构造函数AB().我想编译器现在继续为B创建一个额外的"水下"构造函数.

我觉得这很奇怪:为什么B级不能作为A中的ako场可见?并且:这是否意味着B类在运行时不再是私有的?并且:为什么表示B类的受保护关键字不同?

public class A {
  public A() { b = new B(); }
  B b;
  protected class B { }
}
Run Code Online (Sandbox Code Playgroud)

ska*_*man 25

内部类本质上是Java 1.1中引入的一个hack.JVM实际上没有内部类的任何概念,因此编译器必须提供它.编译器在类A的"外部"生成类B,但是在同一个包中,然后向其添加合成访问器/构造函数以允许A访问它.

当你给B一个受保护的构造函数时,A可以访问该构造函数,因为它在同一个包中,而不需要添加合成构造函数.

  • 我不会让它打扰你.特定的编译器警告对任何人都没有多大用处,合成方法一直用于内部类,并且没有重大影响. (2认同)
  • 但是成员对于包中的其他类仍然是"私有的"...... (2认同)

sie*_*egi 5

我知道这个问题现在已经快三年了,但我发现问题的一部分仍然没有回答:

并且:这是否意味着 B 类在运行时不再是私有的?

Carlos Heubergers 对 skaffmans 回答的评论表明, classB仍然private适用于包中的其他类。

他可能适合 Java 编程语言,即不可能B从其他类引用类。至少不是不使用反射(也可以从外部访问私有类成员),但这是另一个问题。

但是由于 JVM 没有任何内部类的概念(如 skaffman 所说),我问自己如何在字节码级别实现“仅一个类可访问”的可见性。答案:根本没有实现,对于 JVM 来说,内部类看起来像一个普通的包私有类。也就是说,如果您为自己编写字节码(或修改编译器生成的字节码),则可以B毫无问题地访问类。

您也可以从同一个包中的所有类访问所有合成访问器方法。因此,如果您在 classA的方法中为 class的私有字段赋值B,则会在类中生成一个具有默认(即包私有)可见性的合成访问器方法A(命名为类似access$000),为您设置值。这个方法应该只能从类中调用B(实际上它只能使用 Java 语言从那里调用)。但是从 JVM 的角度来看,这只是一个方法,可以被任何类调用。

所以,回答这个问题:

  • 从 Java 语言的角度来看,类B是并且保持私有的。
  • 从 JVM 的角度来看, class B(或更好: class A$B)不是私有的。