JAVA初始化块
正如一些消息来源所说,Java实例初始化块在创建实例时或在构造函数之前执行。 但想象一下这种情况:
public class Foo { { System.out.println("Foo init"); } public Foo() { { System.out.println("Foo constr"); } } } public class Main extends Foo { { System.out.println("Main init"); } public Main() { { System.out.println("Main constr"); } } public static void main(String[] args) { new Main(); } }
输出是(如预测的那样):
Foo init Foo constr Main init Main constr
所以我的问题是 – 实例初始化块的正确定义是什么,因为它显然不是在构造函数之前执行的,因为输出应该是
Main init Foo init Foo constr Main constr
因为Main()构造函数在调用super()之前被调用,而Main初始化块应该是第一个。
不会。初始化块直接复制到构造函数中。 显然,那里也有一个隐含的超级。 你的榜样就变成了
public class Foo { public Foo() { {System.out.println("Foo init");} // initializer. {System.out.println("Foo constr");} } } public class Main extends Foo { public Main() { super(); // super constructor. {System.out.println("Main init");} // initializer. {System.out.println("Main constr");} } public static void main(String[] args) { new Main(); } }
这解释了你观察到的行为
Foo init Foo constr Main init Main constr
new Main();
上述状态将首先转到Main()。 但是在Main()中执行任何操作之前,会调用super()。 所以,调用Foo()。 现在,在Foo()中,检查实例初始化块。 所以,你明白了。 “Foo init”。 接下来执行Foo()中的语句。 所以,你得到 – “Foo constr”。 接下来,控件返回到Main(),Main()现在检查Main类是否存在初始化块。 因此,打印“Main Init”..然后打印Main()的其他语句。 定义是正确的..理解……好……取决于你怎么看…
代码由编译器转换为如下所示:
public class Main extends Foo { void _init() {System.out.println("Main init");} public Main() { super(); _init(); {System.out.println("Main constr");} } }
主要规则是:
- 在当前类的任何初始化代码之前调用super()
- 初始化程序块在构造函数的主体之前调用
在JLS中解释了一个特定的初始化过程 ,但让我提炼出最关键的部分:
- 在初始化类方面发生的事情是依赖于实现的JVM。
- 如果尚未初始化超类,则在其子类(步骤7)之前初始化超类。
- 评估所有静态初始值设定项和字段,就好像它们是文本顺序中的单个块一样。 最后一部分很重要; 它意味着首先初始化的是首先看到的。
这就是你看到你所做的行为的原因 – 因为Main
是Foo
的子类,它还没有被初始化,因此它的静态块在那时没有被评估过。
因此, Main
的构造函数直到Foo
的构造函数之后才会被执行,因为在子类中有一个对super()
的隐式调用 。
最后一个陈述不正确。 Main() 在 Foo() 之后执行,因为类构造函数在其超级构造函数完成后执行。 有关更多详细信息,请参阅规范的这一部分,该部分处理隐式和显式超级构造函数调用。
了解此代码如何在内部工作:
class Foo { {System.out.println("Foo init");} public Foo() { {System.out.println("Foo constr");} } } class Main extends Foo { {System.out.println("Main init");} public Main() { {System.out.println("Main constr");} } public static void main(String[] args) { new Main(); } }
第1步:JVM调用Main
类的main()
方法
第2步:构造函数Main()
有内部super()
,如果你没有使用this()
,它由JVM给出,因此super将调用超类构造函数,即超类的Foo()
。
第3步:现在在调用超类的Foo之前,JVM将检查是否有任何IIB
即实例初始化块,因此,在打印“Foo constr”之前将打印“Foo init”
现在输出到现在是:
Foo init Foo constr
步骤4:我们的控制返回到当前构造函数,并在执行之前再次执行当前的construstor JVM将调用IIB
即实例初始化块和将打印“Main init”。 然后最后“主要建设”
所以最后是:
Foo init Foo constr Main init Main constr
实际上,任何构造函数的第一次调用始终是Super()
或this()
。 你正在使用new Main();
创建新对象new Main();
当然这将调用构造函数,总是调用Constructor来初始化对象。