当Base类构造函数在Java中调用重写方法时,Derived类对象的状态

Abh*_*bhi 12 java inheritance constructor

请参考下面的Java代码:

class Base{
     Base(){
         System.out.println("Base Constructor");
         method();
     }
     void method(){}    
}

class Derived extends Base{
    int var = 2;
    Derived(){
         System.out.println("Derived Constructor");  
    }

     @Override
     void method(){
        System.out.println("var = "+var);
     }
 }

class Test2{
    public static void main(String[] args) {
        Derived b = new Derived();
    }
}
Run Code Online (Sandbox Code Playgroud)

看到的输出是:

Base Constructor
var = 0
Derived Constructor
Run Code Online (Sandbox Code Playgroud)

我认为var = 0的发生是因为Derived对象是半初始化的; 类似于Jon Skeet在这里所说的

我的问题是:

如果尚未创建Derived类对象,为什么会调用重写的方法?

在什么时间点,var赋值为0?

是否存在需要此类行为的用例?

Jon*_*eet 12

  • Derived对象被创建-它只是在构造函数尚未运行.在创建它之后,对象的类型永远不会在Java中更改,这在所有构造函数运行之前发生.

  • var在构造函数运行之前,为创建对象的过程分配默认值0.基本上,类型引用被设置并且表示对象的其余内存被擦除为零(概念上,无论如何 - 它可能已经被擦除为零,作为垃圾收集的一部分)

  • 这种行为至少会导致一致性,但这可能是一种痛苦.就一致性而言,假设您有一个可变基类的只读子类.基类可能有一个isMutable()有效默认为true 的属性 - 但是子类将其重写为始终返回false.在子类构造函数运行之前,对象是可变的是奇怪的,但之后是不可变的.另一方面,在您运行该类的构造函数之前最终在类中运行代码的情况下,这绝对是奇怪的:(

一些指导原则:

  • 尽量不要在构造函数中做太多工作.避免这种情况的一种方法是在静态方法中工作,然后使静态方法的最后部分成为构造函数调用,它只是设置字段.当然,这意味着当你正在做工作时,你不会从多态中获益 - 但是在构造函数调用中这样做无论如何都是危险的.

  • 在构造函数期间尽量避免调用非final方法 - 这很可能会引起混淆.文档的任何方法调用你真的必须做出非常清楚,让任何人都重写他们知道,初始化完成之前,他们将被调用.

  • 如果你必须在施工期间调用一个方法,那么通常不适合在之后调用它.如果是这种情况,请记录并尝试在名称中指明它.

  • 尽量不要过度使用继承 - 当你从一个非Object类以外的超类派生的子类时,这只会成为一个问题.继承的设计很棘手.


sam*_*aur 5

如果尚未创建Derived类对象,为什么要调用重写的方法?

Derived类构造函数隐式调用Base类构造函数作为第一条语句。Base类构造函数调用method(),它调用类中的重写实现,Derived因为这是正在创建其对象的类。method()Derived课堂var上,此时该值被视为0。

var在什么时间点分配值为0?

varintDerived调用类的构造方法之前,已为类型指定默认值,即0 。它被分配值2 之后的隐性超类构造器调用完成和文中陈述Derived类的构造函数开始执行。

是否有任何需要这种行为的用例?

它通常是一个坏主意,使用非finalprivate方法在非的构造函数/初始化final类。原因在您的代码中显而易见。如果正在创建的对象是子类实例,则这些方法可能会产生意外的结果。