繁体   English   中英

Java继承:继承超类成员和变量时的区别

[英]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因此将执行类Cprint1()版本。

但是当您键入[ ((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被覆盖,并且cC的对象。 因此,它将调用Cprint1()方法。 另一方面,变量x不被覆盖。

要调用超类方法,您可以执行以下操作:

super.print1();

在这里,所有类都使用了同名实例变量来混淆编译器,但是您应该知道,只有成员函数才能被覆盖,而成员变量不能被覆盖。

因此,所有三个x将分别存储在内存中。 但是public void print1()将被其子类覆盖。 所以,

  1. ((A)c).print1(); 这段代码将调用C类中定义的重写方法。无论在哪个类中强制转换C类的Object都无关紧要。

但是在第二种情况下((A)c).x将打印A类的值。

  1. ((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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM