考虑以下代码段:

class A{ /* assume static and non static block are here */ }
class B extends A{ /* assume static and non static block are here */ }

在主要方法中

new B();

因此初始化的顺序为:

类A的静态成员初始化

B类的静态成员初始化

类A的非静态成员初始化

然后在构造函数A中执行代码

B类的非静态成员初始化

然后在构造函数B中执行代码

现在看一下这段代码

class A{
A(){
this.m(); //line 1
}
void m(){
System.out.println("A.m()");
}
}
class B extends A{
void m(){
System.out.println("B.m()");
}
}

在主要方法中

new B();

当执行构造函数A的代码时,它只能在类A中看到方法m,因为尚未为类B初始化非静态成员(按照我提到的顺序)。

但是结果是" B.m()"。 (子类的方法已执行)

有人可以考虑我提到的顺序来解释这里发生的事情(方法重写)吗?

这应该带来一些清晰度,尽管并非完全相同。

Java类初始化顺序和重写方法的可能重复项

您永远不要,永远不要,永远不要在构造函数/析构函数中调用虚拟方法。 这绝对是一个糟糕的主意。 调用final实例方法可以,但是我通常认为这是个坏主意,请尽可能避免。 这是一个可怕的想法,它涵盖在Scott Meyers Effective C ++中,尽管您的问题是关于Java的,但他给出的主要理由在这里适用。 不要做

另外,您可能想浏览相关的JLS部分

When the code of constructor A is being executed, it can only see the method m in class A since non static members hasn't been initialized yet for class B (according to the order I mentioned).

您假设方法是已初始化的"非静态成员"的一部分。 并非如此-实际上,A构造函数完成后,B中的字段将被初始化。

用Java创建对象后,其类型即被设置且永不更改。 为所有字段分配了足够的空间-但是实际上是从继承层次结构的顶部开始对字段进行了初始化。

因此,是的,如果您从构造函数中调用重写的方法,则该方法将在未初始化要使用的某些字段的上下文中执行-因此,如果可能,应避免这样做。

乔恩,有一个简短的问题,想知道我是否朝着正确的方向迈进了?理解这一点的简单方法是,当我们有Class B的实例时,多数民众赞成在这里是活动对象。因此,新的B()将调用A()的超级构造函数,其中this.m()等效于新的B()。m(),因为该方法被覆盖了。

@JNL:好吧,它实际上不等同于new B().m(),因为它不会创建新对象...

乔恩(Jon),同意它不会创建新对象,我的意思是,如果我正确输入,我可以说this.m()等同于在main中调用的objectOfClassB.m(),因为主线程正在执行的程序具有新的B()对象。我希望它不会混淆?

@JNL:嗯,我是这样认为的-但我不确定它的积极帮助:)

只是想澄清一下。谢谢乔恩。欣赏它。

@Jon:所以非静态成员仅是非静态块和变量。方法不是非静态的。因此,在初始化过程中,仅会初始化非静态块和变量,而不会初始化方法。这是正确的吗?

@ chathura2020:不,方法等也是成员-静态和非静态方法都有。但是方法不需要作为对象初始化的一部分进行初始化。

好的,这就是原因,必须在使用局部变量之前对其进行显式初始化(??因为未初始化方法)

@ chathura2020:不,多数民众赞成在完全分开。那就是帮助避免错误。不要忘记,每次调用一个方法时,都会得到不同的变量集。创建对象时,方法"已初始化",因为没有任何要初始化的对象。

无论派生类是否已初始化,都会发生方法覆盖。

这就是为什么您应该避免在初始化程序中调用虚拟方法的原因。