在下面显示的代码片段中,内部类继承外部类本身。
package test;
class TestInnerClass {
private String value;
public TestInnerClass(String value) {
this.value = value;
}
private String getValue() {
return value;
}
public void callShowValue() {
new InnerClass("Another value").showValue();
}
private final class InnerClass extends TestInnerClass {
public InnerClass(String value) {
super(value);
}
public void showValue() {
System.out.println(getValue());
System.out.println(value);
}
}
}
public final class Test {
public static void main(String[] args) {
new TestInnerClass("Initial value").callShowValue();
}
}
main()
方法(最后一个代码段)中的唯一语句将值Initial value
分配给value
类的私有字段TestInnerClass
,然后调用{{ 1}}方法。
callShowValue()
方法导致另一个字符串 - callShowValue()
被设置为Another value
类的私有字段value
,然后再调用TestInnerClass
方法showValue()
延长InnerClass
。
因此,TestInnerClass
方法中的以下两个语句
showValue()
应该显示,
另一个值
另一个值
但他们显示,
初始值
初始值
为什么会这样?
答案 0 :(得分:8)
方法getValue()
和字段value
都是private
。因此,任何其他类都无法访问它们,包括子类,即。他们不是遗传的。
在InnerClass#showValue()
public void showValue() {
System.out.println(getValue());
System.out.println(value);
}
因为这些是私有的,getValue()
和value
指的是外部类的成员,因为你在同一个类中,所以可以访问它们,即。 内部类可以访问外部类私有成员。上述调用相当于
public void showValue() {
System.out.println(TestInnerClass.this.getValue());
System.out.println(TestInnerClass.this.value);
}
因为您已将value
设为
new TestInnerClass("Initial value")
您看到"Initial value"
被打印两次。 无法访问子类中的private
成员。
关键是:不要让子类成为内部类。
答案 1 :(得分:8)
这里的关键是理解内部类如何访问外部类的成员。如果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 st 和3 rd 引用是如何相同的,而2 nd 和4 th 是不同的
可以使用this$0
引用在内部类中访问外部类成员:
当您从内部类访问字段或外部类的任何其他成员时,访问表达式将自动限定为this$0
。您使用this$0
引用明确将成员访问限定为OuterClass.this
。因此,请考虑外部类中的字段value
为public
,然后在内部类的showValue()
方法中:
public void showValue() {
System.out.println(TestInnerClass.this.value);
System.out.println(value);
}
前两个打印语句是等效的。它们将被编译为相同的字节代码:
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;
访问它。因此,第二个print语句将无法编译。
此次直接访问该字段将获得this$0
的限制。现在更改字段value
private,并将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
}
这就是问题所在。根据字段是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);
}
正在访问字段和封闭实例的方法。该字段仍然是初始值。这就是为什么输出是:
初始值
初始值
答案 2 :(得分:2)
在上述情况下, TestInnerClass和InnerClass 之间存在2种不同的关系。
但故事中有一点点扭曲...... 有两个对象!问题是我们将“Another Value”放入不同的对象!并打印旧的值 ..
第一个对象:
public static void main(String[] args) {
new TestInnerClass("Initial value").callShowValue();
}
Test Class中的上述方法使用“初始值”创建一个TestInnerClass实例
第二个对象:
public void callShowValue() {
new InnerClass("Another value").showValue();
}
由于 InnerClass 正在扩展 TestInnerClass ,因此使用“Another Value”创建另一个新的TestInnerClass实例。 但是我们从旧对象打印的值不是第二个。
答案 3 :(得分:1)
其他答案已经解释了为什么你得到了你看到的结果(你有两个TestInnerClass
实例并正在访问第一个实例),但实际上有一种方法可以访问{{1的私有成员在其子类中 - 关键字TestInnerClass
。
如果您将super
方法替换为:
showValue
您将获得预期的输出。
我还建议如果你决定这样做,你可以使public void showValue() {
System.out.println(super.getValue());
System.out.println(super.value);
}
成为一个静态内部类,因为它不再需要引用它的外部类的实例。