Java 为什么我的子类构造函数没有被调用?

Java 为什么我的子类构造函数没有被调用?,java,inheritance,constructor,Java,Inheritance,Constructor,我有这样的继承结构: public abstract class Mom { int dummy; Mom() { dummy = 0; } Mom(int d) { this(); dummy = d; } } public class Kid extends Mom { String foo; Kid() { super(); foo = ""; }

我有这样的继承结构:

public abstract class Mom {
    int dummy;
    Mom() {
        dummy = 0;
    }
    Mom(int d) {
        this();
        dummy = d;
    }
}
public class Kid extends Mom {
    String foo;
    Kid() {
        super();
        foo = "";
    }
    Kid(int d) {
        super(d);
    }
}
// ...
Kid kiddo = new Kid(10);
// kiddo.foo == null !
我的
Kid
的无参数构造函数从未被调用!以下是我所期望的:

  • 新生儿(10)
    → <代码>儿童#儿童(智力)
  • super(d)
    → <代码>妈妈#妈妈(int)
  • this()
    Kid#Kid()
    //doh
  • super()
    → <代码>妈妈#妈妈()
  • 可以从
    Mom
    调用
    Kid
    的无参数构造函数吗?
    我想不是,我将添加一个抽象方法[1]
    init()
    ,它将调用
    Mom
    ,而
    Kid
    s必须重写。
    但我只是想知道确切的原因,如果可能的话,举例说明为什么要调用子类的构造函数是个坏主意(即使子类的构造函数确实调用了
    super()


    我会安排这些,这样你就不需要调用每个构造函数

    public abstract class Parent {
        final int dummy;
    
        Parent () {
            this(0);
        }
        Parent (int d) {
            dummy = d;
        }
    }
    
    public class Kid extends Parent {
        final String foo = "";
    
        Kid() {
        }
    
        Kid(int d) {
            super(d);
        }
    }
    
    使用
    final
    确保每个字段设置一次


    从构造函数调用任何可重写的方法被认为是不好的做法,因此让构造函数重写是一个坏主意


    this()
    调用同一类的构造函数,因为构造函数不遵循继承(静态方法也不遵循继承)


    构造函数会这样做,否则多次调用同一个构造函数会有危险,并且无法保证最终方法只设置一次。

    您应该为Kid类设置这些构造函数:

    Kid(int i) {
      super(i);
      whatever();
    }
    
    Kid () {
      this( DEFAULT_VALUE);
    }
    
    所以所有对父构造函数的调用都是通过子类的完全限定构造函数进行的。类的所有构造函数都有一个默认行为,它不会像当前代码那样被绕过。

    来自(我强调):

    • 替代构造函数调用以关键字
      this
      开始(可能以显式类型参数开头)他们已经习惯了 调用同一类的备用构造函数。

    • 超类构造函数调用以关键字
      super
      (可能以显式类型参数开头)或主 表情它们用于调用直接 超类。

    因此,this构造函数调用总是引用同一个类,而不是子类

    虽然可以在构造函数中调用虚拟方法,但这是不安全的,被认为是不好的做法,因为它可能导致这些方法使用部分初始化的对象实例

    对于您的问题,有几种可能的解决方案:

  • 在声明时初始化成员foo,即
    foo=”“。这也称为字段初始值设定项
  • 使用:
    {foo=”“;}
    。注意,如果类中需要,可以有多个实例初始值设定项
  • 咬紧牙关,在所有构造函数中重复初始化
  • 根据,在(1)和(2)中的初始化总是在调用构造函数之前执行,因此您可以进行定义良好的对象初始化,而无需求助于有问题的模式

    如果一个成员多次初始化,则最后一次初始化获胜:

    4) 执行实例初始值设定项和实例变量初始值设定项 对于此类,指定实例变量初始值设定项的值 到相应的实例变量,按从左到右的顺序 它们以文本形式出现在类的源代码中


    如果同一字段也在构造函数中初始化,那么构造函数将获胜。

    是的,
    Kid
    确实扩展了
    Mom
    我可能还需要在
    Mom
    Dad
    之间添加一个
    接口。我知道这是一个不好的做法,但是,我的
    init
    方法的目的是将成员字段设置为其默认值。我的类非常简单,我确信在子类方法尚未完全构造时调用它不会失败。不过,如果你有更好的设计想法,我就要了!非常感谢。更改了我答案中的示例,因此每个字段都设置了一次且仅设置了一次。哦,我明白了。谢谢你的详细回答。那么我将使用选项1!-但是如果我初始化
    foo=“init”
    然后使用实例初始化器创建一个对象,该实例初始化器将
    foo
    初始化为另一个值。在这种情况下,谁赢了?@Bicou我已经扩展了我的答案来回应这一评论。这是一种从左到右(或从上到下)的逻辑,因此最后一次初始化获胜。
    new Kid(10) --> Kid#Kid(int)
    super(d) --> Mom#Mom(int)
    this() --> Mom#Mom()
    
    Kid(int i) {
      super(i);
      whatever();
    }
    
    Kid () {
      this( DEFAULT_VALUE);
    }