如果有可能请为我说明这句话
这里,作者说:
不要从构造函数中调用可覆盖的方法。创建时 子类对象,这可能导致被调用的方法被调用 在子类对象完全初始化之前。回想一下,当你 构造一个子类对象,它的构造函数首先调用其中一个 直接超类的构造函数。 如果是超类构造函数 调用一个可覆盖的方法,该方法的子类的版本 将由超类构造函数调用 - 在子类之前 构造函数的主体有机会执行。
我无法理解如何在超类的构造函数中调用子类的可覆盖方法
TNX
答案 0 :(得分:3)
您必须首先区分实例化和初始化。实例化是创建类型实例的过程(为其分配空间并获取对该空间的引用)。初始化是将实例的状态设置为其初始值的过程。
采用以下类型层次结构:
class Foo {
public Foo() {}
}
class Bar extends Foo {
public Bar() {super();}
}
新实例创建表达式
new Bar();
导致实例化和初始化。实例化首先发生。 Java创建具体类型Bar
的实例。
然后需要进行初始化。在继承层次结构中,初始化遵循相同的层次结构,但从上到下。
Object
|
Foo
|
Bar
Object
的构造函数首先运行以初始化定义为Object
的一部分的状态,然后运行Foo
的构造函数来初始化定义为部分的状态Foo
最后运行Bar
的构造函数来初始化Bar
中定义的状态。 您的实例仍然是Bar
类型。所以多态仍然适用。如果您调用实例方法并且该方法在层次结构中较低的某处被覆盖,则将调用该实现。
这就是引用所指的内容。这很危险。在这里阅读更多内容:
答案 1 :(得分:2)
为了说明使用某些(简单)代码这是一个坏主意的原因,请考虑以下两个类:
class Greeter {
protected Greeter() {
printHello();
}
protected void printHello() {
System.out.println("Hello");
}
}
看起来很简单,只要你实例化它就打印Hello
。现在让我们扩展它:
class NamedGreeter extends Greeter {
private String name;
public NamedGreeter(String name) {
this.name = name;
}
@Override
protected void printHello() {
System.out.println("Hello " + name);
}
}
目的显然是NamedGreeter
在实例化时按名称问候你,但事实上它总是打印Hello null
,因为在实例化NamedGreeter
时首先调用超级构造函数
感谢多态性如何工作,只要在printHello()
上调用NamedGreeter
方法(即使该调用来自Greeter
类),NamedGreeter
中的实现将被调用。从父类的构造函数中调用该方法意味着即使子类扩展它,也不会初始化子项定义的字段,因为它不可能在子构造函数中执行任何操作(如初始化)字段)在调用父构造函数之前。
答案 2 :(得分:1)
演示将调用子方法的示例:
class Foo {
static class Parent {
Parent() {
someMethod();
}
void someMethod() {}
}
static class Child extends Parent {
@Override void someMethod() {
throw new AssertionError("Invoked");
}
}
public static void main(String[] args) {
new Child(); // Throws Exception.
}
}
输出:
Exception in thread "main" java.lang.AssertionError: Invoked
at Foo$Child.someMethod(Foo.java:16)
at Foo$Parent.<init>(Foo.java:9)
at Foo$Child.<init>(Foo.java:14)
at Foo.main(Foo.java:21)