这里的关键是了解内部类如何访问外部类的成员。private
以及在和成员的情况下如何访问这些non-private
成员。(注意:我将在这里讨论非static
内部类,因为问题仅与此有关)。
内部类存储对封闭实例的引用:
内部类将对封闭实例的引用存储为字段。该字段被命名为this$0
。封闭实例总是绑定到内部类对象。当您从封闭类内部创建内部类的对象时,this$0
所有对象的引用值保持相同,但this
引用会有所不同。
您可以使用内部类中的语法访问this$0
字段。Outer.this
例如,考虑以下代码:
class Outer {
public Outer() { }
public void createInnerInstance() {
Inner obj1 = new Inner();
Inner obj2 = new Inner();
}
private class Inner {
public Inner() {
System.out.println(Outer.this);
System.out.println(this);
}
}
}
public static void main(String[] args) {
new Outer().createInnerInstance();
}
执行此代码时,您将获得如下输出:
Outer@135fbaa4
Outer$Inner@45ee12a7
Outer@135fbaa4
Outer$Inner@330bedb4
请注意第 1次和第 3次引用是相同的,而第 2次和第 4次是不同的。
外部类成员可以使用this$0
引用在内部类中访问:
当您从内部类访问字段或外部类的任何其他成员时,访问表达式会自动限定this$0
。您明确地将成员访问限定为this$0
使用OuterClass.this
引用。因此,请考虑value
外部类中的字段 was public
,然后在showValue()
内部类中的方法中:
public void showValue() {
System.out.println(TestInnerClass.this.value);
System.out.println(value);
}
前 2 个打印语句是等效的。它们将被编译为相同的字节码:
public void showValue();
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/
o/PrintStream;
3: aload_0
4: getfield #1 // Field this$0:LTestInnerClass;
7: getfield #4 // Field TestInnerClass.value:Ljava/lang/Stri
g;
10: invokevirtual #5 // Method java/io/PrintStream.printl
:(Ljava/lang/String;)V
13: getstatic #3 // Field java/lang/System.out:Ljava/
o/PrintStream;
16: aload_0
17: getfield #1 // Field this$0:LTestInnerClass;
20: getfield #4 // Field TestInnerClass.value:Ljava/lang/Stri
g;
23: invokevirtual #5 // Method java/io/PrintStream.printl
:(Ljava/lang/String;)V
26: return
您不能使用以下方式显式访问外部类成员this
:
如果您在内部类中显式尝试限定字段或方法访问表达式this
,您将收到编译器错误:
public void showValue() {
System.out.println(this.value); // this won't compile
}
上面的 print 语句不会编译,因为value
它不是内部类本身的字段。这是一个外部类字段。this
指的是内部类实例,而不是外部类。
当内部类扩展外部类时,故事会发生变化:
当你的内部类扩展外部类时,事情就开始变得奇怪了。因为在这种情况下,限定字段或方法访问this
对非私有成员有效。对于private
成员,这仍然无效,因为private
成员不是继承的。
在继承内部类的情况下,直接访问外部类成员用this
. 这意味着,它们将作为内部类成员访问。在明确限定访问时,Outer.this
将引用封闭实例的字段 - this$0
。
考虑value
字段声明为public
:
public void showValue() {
System.out.println(value); // inner class instance field
System.out.println(this.value); // inner class instance field
System.out.println(Outer.this.value); // enclosing instance field
}
前两个 print 语句将打印value
内部类实例的字段,而第三个 print 语句将打印value
封闭实例的字段。使困惑?
记得我说过,当你从外部类内部创建多个内部类实例时,它们将具有相同的this$0
引用。
考虑创建一个外部类实例:
new Outer("rohit").callShowValue();
然后在callShowValue()
方法中,创建内部类的实例:
new Inner("rj").showValue();
现在,该showValue()
方法的输出将是:
rj
rj
rohit
您会注意到,this.value
与 不同Outer.this.value
。
如果您制作value
字段怎么办private
:
现在,当您制作外部类字段private
时,当然您无法使用this.value;
. 因此,第二个打印语句将无法编译。
在这种情况下,直接访问字段将被限定this$0
。现在更改字段value
私有,并将showValue()
方法修改为:
public void showValue() {
System.out.println(value); // enclosing instance field
System.out.println(this.value); // compiler error
System.out.println(Outer.this.value); // enclosing instance field
}
这就是问题所在。第一个 print 语句value
使用this
或this$0
基于字段是public
或来限定private
。
谈到你的具体问题:
现在在您的代码中,由于value
字段和getValue()
方法都是private
,因此showValue()
方法:
public void showValue() {
System.out.println(getValue());
System.out.println(value);
}
等同于:
public void showValue() {
System.out.println(TestInnerClass.this.getValue());
System.out.println(TestInnerClass.this.value);
}
这是访问封闭实例的字段和方法。并且该字段仍然是Initial Value。这就是为什么输出是:
初始值
初始值