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
类型,因此