重写方法的问题,如果有可能,请让这句话清楚我调用构造函数从
这里,笔者说:
不要从构造函数重写的方法。创建子类对象时,可能会导致在子类对象完全初始化之前被重写的方法称为 。回想一下,当你构造一个子类对象时,它的构造函数首先调用 直接超类的构造函数之一。 如果超类的构造函数 调用一个可重写的方法,该方法的子类的版本 将由超类构造函数调用 - 在子类 构造函数的主体有机会执行之前。
我不能明白,它如何能够调用重写方法的子类的版本在父类的构造函数
TNX
重写方法的问题,如果有可能,请让这句话清楚我调用构造函数从
这里,笔者说:
不要从构造函数重写的方法。创建子类对象时,可能会导致在子类对象完全初始化之前被重写的方法称为 。回想一下,当你构造一个子类对象时,它的构造函数首先调用 直接超类的构造函数之一。 如果超类的构造函数 调用一个可重写的方法,该方法的子类的版本 将由超类构造函数调用 - 在子类 构造函数的主体有机会执行之前。
我不能明白,它如何能够调用重写方法的子类的版本在父类的构造函数
TNX
您必须首先区分实例化和初始化。实例化是创建类型实例(为其分配空间并获取对该空间的引用)的过程。初始化是将实例的状态设置为其初始值的过程。
采取以下类型层次:
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
。所以多态性仍然适用。如果调用一个实例方法,并且该方法在层次结构中较低的地方被覆盖,那么该实现将被调用。
这就是引用的意思。这很危险。 Read more here:
一个例子,证明孩子的方法将被调用:
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)
为了说明这是一个坏主意了一些(简单的)代码的原因,考虑这两个类:
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
实施将被调用。在父类的构造函数中调用该方法意味着即使子类继承它,也不会初始化子类定义的任何字段,仅仅是因为在子类构造函数之前无法在子构造函数中执行任何操作(如初始化字段)父构造函数被调用。