Base类构造函数在Java中调用覆盖方法时,派生类对象的状态

请参考下面的 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();
    }
}

看到的输出是:

Base Constructor
var = 0
Derived Constructor

我认为var = 0,因为Derived对象被初始化了一半;类似于Jon Skeet says here

我的问题是:

如果没有创建派生类对象,为什么会调用重写方法

在什么时候是var赋值0?

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

解决方法

>派生对象已创建 – 只是构造函数尚未运行.在创建即时之后,对象的类型在所有构造函数运行之前都不会发生变化.
>在构建器运行之前,var被分配为认值0作为创建对象的过程的一部分.基本上,类型引用被设置,表示对象的其余内存被擦除为零(在概念上,无论如何 – 它可能已经被擦除为零,作为垃圾回收的一部分)
这个行为至少可以导致一致性,但这可能是一个痛苦.在一致性方面,假设你有一个可变基类的只读子类.基类可能有一个isMutable()属性,它被有效地认为true – 但子类覆盖它总是返回false.在子类构造函数运行之前,对象可以是可变的,但之后不可变,这将是奇怪的.另一方面,在这个类的构造函数运行之前,最终在一个类中运行代码的情况绝对是奇怪的:(

几个准则:

>尽量不要在构造函数中做很多工作.避免这种情况的一种方法是以静态方法进行工作,然后使静态方法的最后一部分成为简单地设置字段的构造函数调用.当然,这意味着你在做这项工作时不会得到多态的好处 – 但是在构造函数调用中这样做是很危险的.>尝试非常努力以避免在构造函数期间调用非最终方法 – 这很可能导致混淆.记录您真正要做的非常清楚的任何方法调用,以便任何人覆盖它们都知道在初始化完成之前调用它们.>如果在施工过程中必须调用方法,那么以后调用它通常是不合适的.如果是这种情况,请记录它并尝试以名称表示.>尽量不要过度使用继承 – 这只会在您从Object之外的超类派生子类时成为一个问题:)设计继承是棘手的.

相关文章

最近看了一下学习资料,感觉进制转换其实还是挺有意思的,尤...
/*HashSet 基本操作 * --set:元素是无序的,存入和取出顺序不...
/*list 基本操作 * * List a=new List(); * 增 * a.add(inde...
/* * 内部类 * */ 1 class OutClass{ 2 //定义外部类的成员变...
集合的操作Iterator、Collection、Set和HashSet关系Iterator...
接口中常量的修饰关键字:public,static,final(常量)函数...