为什么子类默认调用super()?

时间:2012-12-19 22:00:32

标签: java oop constructor subclass

这个问题一直困扰着我一段时间,而且我没有找到一个好的答案(除了“就是这样”)。

让我给出一些背景代码,以显示我在说什么。

class Note {
    private final String name = "Note";

    public Note() {
        System.out.println(name);
    }
    // ...
}

class Todo extends Note {
    private final String name = "Todo";

    public Todo() {
        System.out.println(name);
    }
    // ...
}

// ...
Note note = new Todo(); // case 1
Todo todo = new Todo(); // case 2

那么案例1和案例2如何打印出来:

Note
Todo

这没有任何意义,因为Todo()(构造函数)不会调用super()(至少不会明显)。为什么子类必须调用父默认构造函数,为什么不只需要任何子类来实现构造函数?

我读了几个与此相关的questions,但没有人回答为什么

修改
我想我的例子有点差,但它实际上是Java 7认证问题的衍生物。从答案的集合我现在理解为什么。让我提供一个更好的例子:

public Note {
    private String description;

    public Note() {
        description = "I'm a Note";
    }

    public Note( String description ) {
        this.description = description;
    }

    // getters/setters/etc.
}

public Todo extends Note {
    // field vars..

    public Todo() {
        // empty constructor
    }

    // getters/setters/etc..
}

所以现在这更有意义,因为在创建Todo时,如果super()未插入封面后面,则Note的{​​{1}}方面将不会初始化。在这种情况下,拥有一个子类是没有意义的。谢谢大家!

8 个答案:

答案 0 :(得分:7)

子类构造函数必须调用某些构造函数。如果您还没有告诉它使用哪个,它将使用默认构造函数。

另一种选择是让超类中的变量完全未初始化,并且可以通过子类和/或其方法访问,这些非常坏。

决定静默调用默认的超类构造函数,而不是例如虽然没有编译,但仍有争议,但我怀疑它首先与“默认构造函数”的存在联系在一起。

仅供参考,您的问题也表明您可能对继承感到困惑。 Note.nameTodo.name字段完全独立:您不能覆盖子类中的字段,只能覆盖方法。

答案 1 :(得分:3)

这个问题可分为两部分。

  1. 为什么必须调用超类的任何构造函数?这很简单:构造函数负责设置对象的状态,并且由于超类中的状态对于子类代码(即private字段)不可见,所以这只能通过调用来处理超类的构造函数。
  2. 为什么不必明确致电super()?这只是一个相当随意的Java设计决策,使代码看起来更简单。它与default constructor的概念很好地联系在一起(即存在于未定义显式构造函数的类中的隐含的无参数构造函数),尽管在您的示例中可以看到,它也适用于你有一个在超类中明确定义的无参数构造函数。

答案 2 :(得分:1)

您不必显式调用超类构造函数。并且没有必要在子类构造函数中调用super()或super(arguments)。如果不指定,编译器将自动添加对无参数超类构造函数的调用。

如果超类没有无参数构造函数,那么必须在每个子类构造函数中显式调用super(arguments)。

答案 3 :(得分:1)

两个案例打印出Note和Todo的原因是你使用operator new始终创建类Todo的实例。如果你写过

Note note = new Note();
Note todo = new Todo(); 

然后结果将是:

Note
Note
Todo

该行为的原因是Polymorphism

回答为什么构造函数必须调用超级构造函数来创建自己很容易想象。因为它使用超类的元素,所以必须首先创建类,所以继承它的类可以对它进行操作。

答案 4 :(得分:1)

在您的示例中,父级的构造函数不对该对象执行任何操作。

通常,构造函数通过设置字段来对对象进行一些构造。这些字段对于该类的构造函数是已知的,并且可能无法在另一个类中设置或访问,并且您不希望在每个子类(已经在父类中)中复制所有代码,因此父字段设置正确。

因此,每个类初始化它所知道的字段并且不依赖于子类的行为来正常行为是有意义的。这允许您更改一个类而无需更改它的子类。例如添加或更改字段。

答案 5 :(得分:0)

事实上,确实如此。即使您不编写,也会自动添加 super()调用。这是Todo构造函数中的第一条指令。

要自己测试,只需使用参数创建一个新的Note构造函数并删除空的构造函数。看看现在发生了什么:)

为什么会这样?嗯,这很简单:因为这就是它的实现方式。

答案 6 :(得分:0)

documentation说明了一切:

  

如果构造函数没有显式调用超类构造函数,   Java编译器自动插入对无参数的调用   超类的构造函数。如果超级班没有   无参数构造函数,您将得到编译时错误。宾语   确实有这样的构造函数,所以如果Object是唯一的超类,   没问题。

答案 7 :(得分:0)

就是这样! :)

对我而言,这一切都有意义,因为在面向对象编程范例中编写代码的主要原因是重用现有代码。

有许多方法可以重用,其中一种方法是继承一个类(即:创建一个子类)。

当您继承一个类时,您还将继承该类的创建方式。

在我看来,如果这种行为不存在,Java将是一种更加混乱的语言