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)之前初始化超类。
  • 评估所有静态初始值设定项和字段,就好像它们是文本顺序中的单个块一样。 最后一部分很重要; 它意味着首先初始化的是首先看到的。

这就是你看到你所做的行为的原因 – 因为MainFoo的子类,它还没有被初始化,因此它的静态块在那时没有被评估过。

因此, 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来初始化对象。