方法本地内部类与内部类

下面的代码产生输出middle 。 任何人都可以详细解释这是怎么发生的?

是否因为class A的“内部”版本的声明是在go()方法中创buildclass A的实例之后发生的?

 class A { void m() { System.out.println("outer"); } } public class MethodLocalVSInner { public static void main(String[] args) { new MethodLocalVSInner().go(); } void go() { new A().m(); class A { void m() { System.out.println("inner"); } } } class A { void m() { System.out.println("middle"); } } } 

我猜你希望本地类方法被调用。 这并没有发生,因为你在本地类的范围之外使用new A() 。 因此,它访问下一个更接近的范围内的候选人,这将是内部类。 来自JLS§6.3 :

立即封闭的本地类声明的范围(第14.2节)是立即封闭块的其余部分,包括它自己的类声明。

因此,方法第一行中的new A()不会访问出现在其后面的本地类。 如果您在此之前移动类声明,您将获得预期的输出。

另请参阅JLS第14.3节 ,其中包含相似的示例。

由于您拥有代码的顺序,您将得到输出“中间”。 由于方法范围的class A 调用new A() 之后发生,因此您将得到输出“middle”。 如果您按照以下顺序切换顺序,您将得到输出“内部”:

 void go() { class A { void m() { System.out.println("inner"); } } new A().m(); } 

输出:

inner

实例化class A的优先顺序从高到低依次为:

  1. 方法

请看官方Java语言规范讨论内部类的更多信息。

inner不打印的原因是( 6.3 ):

立即封闭的本地类声明的作用域是立即封闭的块的其余部分,包括它自己的类声明。

(在方法中声明的类称为本地类。)

所以A不能引用本地类,因为expression式new A()发生在它的声明之前。 换句话说,本地类与局部variables的范围相似。

middle打印而不是outer的原因是内部类A 影响顶层类A ( 6.4.1 ):

名为n的types的声明d隐藏d的作用域中名为n的任何其他types的声明。

这意味着在MethodLocalVSInner体内的任何地方,不合格的A必须引用内部类。

如果您熟悉成员variables的阴影,例如:

 class Example { int x; void setX(int x) { // ┌ 'x' refers to the local method parameter this.x = x; } } 

基本上,类声明正在进行。

情况1:

 void go() { new A().m(); class A { void m() { System.out.println("inner"); } } } 

在这种情况下,如果你在本地类的范围之外运行你的方法。 那为什么它会打印middle

案例2:

 void go() { class A { void m() { System.out.println("inner"); } } new A().m(); } 

在这种情况下,它将打印inner becase类现在在范围内。

在方法中:

  void go() { new A().m(); class A { void m() { System.out.println("inner"); } } } 

当方法开始执行时,第一行将执行new A().m();

并且由于内部类已经在范围内,因此该类的对象将被创build,并且m方法将被调用inner class而不是local method class因为它仍然不在范围内。 这就是为什么你作为输出middle

但如果你改变你的方法为:

  void go() { class A { void m() { System.out.println("inner"); } } new A().m(); } 

你的本地方法类现在将在范围内,并将有更高的偏好,所以你会得到输出inner

您正在使用MethodLocalVSInner的实例调用go方法

在go方法里面,你在这里创build一个A()的实例,因为你没有明确地导入外部的A class而直接的内部类是在方法调用语句之后,JVM正在挑选inner class A ,它在MethodLocalVSInner并执行里面的go方法