Java中构造函数内部的多态性

时间:2014-03-04 16:56:27

标签: java

代码:

class A{

    A()  {
        test();
    }
    void test(){
        System.out.println("from A");
    }
}

class B extends A {

    void test() {

        System.out.println("from B");

    }

}

class C {

    public static void main(String args []){

        A a = new B();

        a.test();
    }
}

输出:

from B 
from B

为什么会这样打印?

6 个答案:

答案 0 :(得分:3)

这是极其糟糕的代码。发生的事情是void test()在子类B中被覆盖。

new B();创建了一个类B的实例。您引用将其强制转换为A这一事实与此无关。但即使尚未构造子类,Java运行时也会从父类A的构造函数中调用该子类中的方法。

极其谨慎地使用此(反)模式! (请注意,在C ++中,您会得到未定义的行为)。

答案 1 :(得分:2)

在运行时期间,从实际实例对象调用该方法。即B

A a = new B();
a.test();

在上面的代码中,您已实例化了对象B,而不是A。您刚刚分配了A类型的引用变量。在内部,它仅指代B的实例。在编译期间,它只是检查该方法是否实际存在于A引用中并允许它进行编译。在运行期间,实际上在真实对象上调用该方法,即引用B引用的A

答案 2 :(得分:2)

这是面向对象多态的最重要概念之一。 通过使用类B扩展A,您将创建一个更具体的实现,使用新方法(例如您的test()方法)覆盖其某些方法,并可能向其添加内容(成员和方法)。

无论何时覆盖一个类,都将调用子类的方法,而不管它们“正在行动”哪个类。

当你将一个对象转换为另一个类时(就像在你的情况下B到A),你只是说我想把它看作是A类的引用。这对于接受A类对象作为参数的方法很有用。

考虑这个例子:

Employee(超类),其方法为float computeSalary()

Technician extends Employee会覆盖方法float computeSalary()

Manager extends Employee会覆盖方法float computeSalary()

SalaryGenerator类有一个方法generateMonthlyPay(Employee e),它调用computeSalary()超类的Employee,但是会调用特定的子类方法,因为每个类都有一个不同的计算月薪的方式。

答案 3 :(得分:2)

在运行时调用多态方法时,Java使用特殊的数据结构来决定需要调用哪个类的方法。此结构在构造对象时设置,在任何用户提供的构造函数和初始化程序代码执行之前

当您创建A a = new B()时,表示“在调用test()时,您需要致电A.test()B.test()”的数据结构已准备好 < / em>输入A的构造函数。由于此结构是为B类准备的,因此它指向B.test(),即使调用代码位于A的构造函数中。这就是为什么你看到"from B"打印两次。

但请注意,虽然从技术上讲,您的代码会按照您的意愿执行,但从逻辑上讲,这是一个非常糟糕的决定。这段代码不好的原因与初始化序列有关:假设一个test()方法依赖于在构造函数中初始化的B的私有字段,如下所示:

class B extends A {
    private final String greeting;
    public B() {
        greeting = "Hello";
    }
    void test() {
        System.out.println(greeting + " from B");
    }
}

人们希望看到"Hello from B"被打印出来。但是,您只会在第二次通话中看到它:在第一次通话时,greeting仍为null

这就是为什么你应该避免从构造函数中调用方法覆盖:它破坏了方法的假设,即对象已经完全初始化,有时会产生相当不幸的后果。

答案 4 :(得分:1)

虽然引用类型是A,但它的对象类型是B,这意味着它指向B中的实现。因此,从B开始打印。

答案 5 :(得分:0)

使用时创建对象a, A =新B()

构造函数从基类调用到派生类..这里基类构造函数调用test(),但是由于过度运行的概念,它在派生类中调用test()。

所以你最初得到“来自B”。

a.test()再次调用over rided派生类test()。所以再次从B打印