Java 从构造函数调用可重写方法的问题
如果可能的话,请把这句话讲清楚 在这里,作者说: 不要从构造函数调用可重写的方法。在创建 子类对象,这可能导致调用重写的方法 在子类对象完全初始化之前。回想一下,当你 构造一个子类对象,其构造函数首先调用 直接超类的构造函数如果超类构造函数 调用可重写方法,即该方法的子类版本 将由超类构造函数在子类之前调用 构造函数的主体有机会执行 我不明白如何在超类的构造函数中调用子类版本的可重写方法Java 从构造函数调用可重写方法的问题,java,constructor,overriding,Java,Constructor,Overriding,如果可能的话,请把这句话讲清楚 在这里,作者说: 不要从构造函数调用可重写的方法。在创建 子类对象,这可能导致调用重写的方法 在子类对象完全初始化之前。回想一下,当你 构造一个子类对象,其构造函数首先调用 直接超类的构造函数如果超类构造函数 调用可重写方法,即该方法的子类版本 将由超类构造函数在子类之前调用 构造函数的主体有机会执行 我不明白如何在超类的构造函数中调用子类版本的可重写方法 TnX演示将调用子方法的示例: class Foo { static class Parent {
TnX演示将调用子方法的示例:
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)
线程“main”java.lang.AssertionError中的异常:已调用
在Foo$Child.someMethod(Foo.java:16)
在Foo$Parent(Foo.java:9)
在Foo$Child(Foo.java:14)
在Foo.main(Foo.java:21)
演示将调用子方法的示例:
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)
线程“main”java.lang.AssertionError中的异常:已调用
在Foo$Child.someMethod(Foo.java:16)
在Foo$Parent(Foo.java:9)
在Foo$Child(Foo.java:14)
在Foo.main(Foo.java:21)
> p>为了说明这是一个坏主意,一些(简单的)代码,考虑这两个类:
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
时首先调用超级构造函数
多亏了多态性的工作原理,只要在
NamedGreeter
上调用printHello()
方法(即使该调用来自Greeter
类),就会调用NamedGreeter
中的实现。从父类的构造函数中调用该方法意味着,即使子类扩展了该方法,也不会初始化子类定义的任何字段,因为在子构造函数中不可能执行任何操作(如初始化字段)在调用父构造函数之前,< p>为了说明这是一个坏主意(一些简单化的代码),请考虑这两个类:
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
时首先调用超级构造函数
多亏了多态性的工作原理,只要在
NamedGreeter
上调用printHello()
方法(即使该调用来自Greeter
类),就会调用NamedGreeter
中的实现。从父类的构造函数中调用该方法意味着,即使子类扩展了该方法,也不会初始化子类定义的任何字段,因为在子构造函数中不可能执行任何操作(如初始化字段)在调用父构造函数之前。必须首先区分实例化和初始化。实例化是创建类型实例的过程(为其分配空间并获取对该空间的引用)。初始化是将实例的状态设置为其初始值的过程
采用以下类型层次结构:
class Foo {
public Foo() {}
}
class Bar extends Foo {
public Bar() {super();}
}
新实例创建表达式
new Bar();
导致实例化和初始化。实例化首先发生。Java创建了一个具体类型Bar
的实例
然后需要进行初始化。在继承层次结构中,初始化遵循相同的层次结构,但从上到下
Object
|
Foo
|
Bar
对象
的构造函数首先运行以初始化定义为对象
一部分的状态,然后运行Foo
的构造函数来初始化定义为Foo
一部分的状态,最后运行Bar
的构造函数来初始化Bar
中定义的状态您的实例仍然是Bar
类型,因此多态性仍然适用。如果您调用一个实例方法,并且该方法在层次结构中较低的位置被重写,则将调用该实现
这就是那句话所指的。这很危险。请在此处阅读更多信息:
您必须首先区分实例化和初始化。实例化是创建类型实例的过程(为其分配空间并获取对该空间的引用)。初始化是将实例的状态设置为其初始值的过程 采用以下类型层次结构:
class Foo {
public Foo() {}
}
class Bar extends Foo {
public Bar() {super();}
}
新实例创建表达式
new Bar();
导致实例化和初始化。实例化首先发生。Java创建了一个具体类型Bar
的实例
然后需要进行初始化。在继承层次结构中,初始化遵循相同的层次结构,但从上到下
Object
|
Foo
|
Bar
对象
的构造函数首先运行以初始化定义为对象
一部分的状态,然后运行Foo
的构造函数来初始化定义为Foo
一部分的状态,最后运行Bar
的构造函数来初始化Bar
中定义的状态您的实例仍然是Bar
类型,因此