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


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

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

谁能解释这种行为?

编辑:


3850
2018-02-03 02:02


起源

你想要解释什么?你跑了 B.f 在[大部分]之前 B 构造函数(C ++会运行 A.f 尽管它被覆盖了)。 - Tom Hawtin - tackline
与C ++的比较非常有趣。在一个“安全”的托管语言中,决定使这个操作不安全(好吧,程序不会崩溃,虽然NPE是可能的,但结果是意外的和不可预测的,因为它依赖于派生类)。 - vehsakul


答案:


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


8
2018-02-03 02:04



+1 - 这不是一个好习惯,因为对象不是很好的状态 - Jeff Storey
作为一般规则,从构造函数调用的任何方法都应该是final或private。 Swing对此尤其不利。继承Swing类并尝试覆盖setFont是一次冒险。 - Devon_C_Miller


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

a: constructor

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

B: f()

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

B: x = 1

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

B: constructor

实例变量现在设置为您指定的值(与对应于类型的默认值相对应(0 对于数字, false 对于 boolean 和 null 供参考))

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

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


4
2018-02-03 02:20





我只是提供一个链接,因为我对这个问题的了解不多。看到 这里 有关构造函数调用顺序的教程。

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

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

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

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

编辑:有关更完整的答案,请参阅 这个Jon Skeet回答。这个答案中的链接被破坏了 只有一份PDF格式的JLS副本才能找到AFAIK这里 是.pdf格式的JLS的副本。相关章节是第8.8.7.1节。有一个解释说明构造函数调用顺序在该答案中。


0
2018-02-03 02:12





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

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

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

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

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


0
2018-02-03 02:27