[英]Java Inheritance: Difference while inheriting super class members and variables
我正在经历Java继承,并尝试了以下代码
class A {
public int x = 1;
public void print1() {
System.out.println("Print from A");
}
}
class B extends A {
public int x = 2;
public void print1() {
System.out.println("Print from B");
}
}
class C extends B {
public int x = 3;
public void print1() {
System.out.println("Print from C");
}
public static void main(String aa[]) {
C c = new C();
((A) c).print1();
System.out.println(c.x);
System.out.println(((A) c).x);
}
}
输出是
从C打印
3
1个
现在,我的问题是我可以从class C instance c
访问class A member x
,但是为什么我不能访问class A method print1()
。 我知道,如果我愿意,我可以叫new A().print1();
。 但是,我想直接从C类的实例访问它。 因此,通过覆盖方法,顶部的父类方法是否在底部的子类中丢失了? 但是,父类成员将保留(尽管隐藏)。 为什么这样? 有没有一种方法可以在不创建A
实例的情况下从C
类中的A
类调用方法?
编辑
当你说((A)c).print1();
,JVM知道实际需要调用的实例是print1()
,其类型为C
因此将执行类C
的print1()
版本。
但是当您键入[ ((A)c).x
,您是在指状态,而不是行为,也是在使用类型A
引用。 因为编译器知道什么是类型引用(在本例中为A
),所以在编译时确定将使用x
哪个版本。 这就是为什么您看到A
的状态。
Java doc中的以下语句可能对您也很有趣:
在类中,即使其类型不同,与超类中的字段具有相同名称的字段也会隐藏超类的字段。 在子类中,无法通过其简单名称引用超类中的字段。 相反,必须通过super访问该字段,这将在下一部分中介绍。 一般来说,我们不建议隐藏字段,因为这会使代码难以阅读。
回答您的问题:
问题1:
我的问题是我能够从C类实例c访问A类成员x
关于变量x
,因为它被声明为public
,所以不能对其应用任何业务验证。 而且,将其声明为public
的开发人员知道,封装的概念现在不能应用(显然不建议这样做)。 因此,允许使用子实例访问父状态不会有任何危害。 因此,通过使用[ ((A)c).x
,您可以从类A
访问x
。
问题2:
但是,父类成员将保留(尽管隐藏)。 为什么这样?
这是因为Java不允许您随意使用super.super.super..
来访问继承层次结构中任何父级的行为。 其背后的原因是保留封装。 您只能使用super
关键字访问直接父类的行为,但不能超过此行为。
为了进一步解释,假设您具有B
类的实现:
class B extends A {
public int x = 2;
public void print1() {
if(x >= 2) {
System.out.println("Print from B");
}
}
}
现在,如果允许使用super.super
,则可以轻松地在类B
print1()
方法中绕过验证,并可以从类C
的实例调用A
的print1()方法。
问题3:
但是,父类成员将保留(尽管隐藏)。 为什么这样?
如前所述,保留封装。
问题4:
有没有一种方法可以在不创建A实例的情况下从C类中的A类调用方法?
因此,无法使用C
的实例从A
访问print1()
。
当您覆盖已经在base(parent)类中实现的函数时,在子类中调用该函数时,子类中的类将自动调用,因此在您情况下,当您调用C对象的print1()
时:
C c = new C();
c.print1();
您将调用覆盖功能。 但是,如果在程序的任何部分中您觉得在任何情况下都需要调用顶级父类( super
无法实现),则应重新考虑您的设计策略; 也许您不应该覆盖print1()
,甚至不应该将print1()
首先放在基类中
在面向对象的编程中,只有方法才具有被覆盖的能力。 您不能覆盖数据成员。 这解释了为什么获得输出。
A
print1()
方法在C
被覆盖,并且c
是C
的对象。 因此,它将调用C
的print1()
方法。 另一方面,变量x
不被覆盖。
要调用超类方法,您可以执行以下操作:
super.print1();
在这里,所有类都使用了同名实例变量来混淆编译器,但是您应该知道,只有成员函数才能被覆盖,而成员变量不能被覆盖。
因此,所有三个x
将分别存储在内存中。 但是public void print1()
将被其子类覆盖。 所以,
((A)c).print1();
这段代码将调用C类中定义的重写方法。无论在哪个类中强制转换C
类的Object都无关紧要。 但是在第二种情况下((A)c).x
将打印A
类的值。
((A)c).x
:-这将引用变量x
属于Class A
而不是Class C
因为变量从未在继承中被覆盖。 ((A)c).print1(); 实际上,它类似于c.print1();。 。 不管您使用Class A进行类型转换,它仍将调用Class C的print1()函数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.