构造函数中的多态方法(Java)

A类在构造函数中调用公共方法f() 。 B类用自己的实现覆盖方法f()

假设您实例化对象B ..对象B方法f()将在对象A的构造函数中调用,尽管对象B未完全初始化。

谁能解释这种行为?

编辑:是的,它不推荐练习..但我不明白为什么 Java没有调用基类Af()实现而不是“伸出”到派生类Bf()实现。

码:

 class A { A() { System.out.println("A: constructor"); f(); } public void f() { System.out.println("A: f()"); } } class B extends A { int x = 10; B() { System.out.println("B: constructor"); } @Override public void f() { System.out.println("B: f()"); this.x++; System.out.println("B: x = " + x); } } public class PolyMethodConst { public static void main(String[] args) { new B(); } } 

输出:

 A: constructor B: f() B: x = 1 B: constructor 

你是对的,这是它的工作方式。 但是不建议这样做,因为从你的class级inheritance的人可能会无意中破坏它。

每当您创建子类的实例时,首先调用超类构造函数(隐式super() )。 所以它打印

 a: constructor 

接下来调用f() ,因为子类重写了超类方法,所以调用子类f() 。 所以你会看到

 B: f() 

现在,子类尚未初始化(仍然是super()正在执行)所以x default是值0因为这是int类型的默认值。 因为你增加它( this.x++; )它变成1

 B: x = 1 

现在,超类构造函数已完成,并在子类构造函数中恢复,因此

 B: constructor 

实例变量现在设置为您指定的值(对应于与类型对应的默认值( 0表示数字, 0表示booleannull表示引用))

注意:如果现在在新创建的对象上打印x的值,它将为10

由于这是一种不好的做法,静态代码分析工具(PMD,FIndBugs等)会在您尝试执行此操作时向您发出警告。

我只是提供一个链接,因为我对这个问题的了解不多。 请参阅此处获取有关构造函数调用顺序的教程。

页面末尾与您描述的情况相关的最突出的引用如下:

  1. 调用基类构造函数。 递归地重复该步骤,使得首先构造层次结构的根,然后是下一个派生类等,直到到达最派生的类。
  2. 成员初始化程序按声明顺序调用。 调用派生类构造函数的主体。

因此,正如您在示例中所示,初始化了基类,然后对每个以下类进行了实例化,最后初始化了成员变量。

但正如比尔所提到的,这不是一个好习惯。 按照比尔说的那样。 他的代表人数比我多。

编辑 :有关更完整的答案,请参阅Jon Skeet的回答 。 此答案中的链接已断开, 只能找到JLS的pdf副本AFAIK 。 以下是.pdf格式的JLS副本。 相关章节是第8.8.7.1节。 有一个解释说明构造函数调用顺序在该答案中。

new B() ,A的构造函数被隐式调用或通过super()调用。 虽然它是在A类中定义的,但实际上当前的类是B.

尝试将以下调试信息添加到A的构造函数和函数中。

 System.out.println(this.getClass()); 

在你的情况下,类A中的函数f()已被类B重写,因此A()中的函数将调用B()的实现。 但是,如果f()是私有方法并且不能被B覆盖,则将以更高的优先级调用Af()。

但正如其他人评论的那样,这不是一个好习惯。