考虑以下代码段:
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:不,多数民众赞成在完全分开。那就是帮助避免错误。不要忘记,每次调用一个方法时,都会得到不同的变量集。创建对象时,方法"已初始化",因为没有任何要初始化的对象。
无论派生类是否已初始化,都会发生方法覆盖。
这就是为什么您应该避免在初始化程序中调用虚拟方法的原因。