Java中运行静态/实例初始化块的顺序是什么?

假设一个项目包含几个类,每个类都有一个静态初始化块。 这些块以什么顺序运行? 我知道在一个类中,这些块按照它们出现在代码中的顺序运行。 我已经读过,在不同的类中是一样的,但是我写的一些示例代码不同意这个。 我用这个代码:

package pkg; public class LoadTest { public static void main(String[] args) { System.out.println("START"); new Child(); System.out.println("END"); } } class Parent extends Grandparent { // Instance init block { System.out.println("instance - parent"); } // Constructor public Parent() { System.out.println("constructor - parent"); } // Static init block static { System.out.println("static - parent"); } } class Grandparent { // Static init block static { System.out.println("static - grandparent"); } // Instance init block { System.out.println("instance - grandparent"); } // Constructor public Grandparent() { System.out.println("constructor - grandparent"); } } class Child extends Parent { // Constructor public Child() { System.out.println("constructor - child"); } // Static init block static { System.out.println("static - child"); } // Instance init block { System.out.println("instance - child"); } } 

并得到这个输出:

开始
静态 – 祖父母
静态 – 父母
静态 – 孩子
实例 – 祖父母
构造函数 – 祖父母
实例 – 父
构造函数 – 父类
实例 – 孩子
构造函数 – 孩子
结束

显而易见的答案是,父母的块在他们的孩子之前运行,但这可能只是一个巧合,如果两个class级不在同一个层次上,这并不会有帮助。

编辑:

我修改了我的示例代码,将其添加到LoadTest.java中:

 class IAmAClassThatIsNeverUsed { // Constructor public IAmAClassThatIsNeverUsed() { System.out.println("constructor - IAACTINU"); } // Instance init block { System.out.println("instance - IAACTINU"); } // Static init block static { System.out.println("static - IAACTINU"); } } 

正如类名所暗示的,我从来没有在任何地方引用新的类。 新scheme产生了与旧scheme相同的产出。

一个类的静态初始化器在第一次被访问时运行,既可以创build实例,也可以访问静态方法或字段。

所以,对于多个类,这完全依赖于运行这些类的代码来加载。

参见JLS第8版的 12.4和12.5节,他们详细介绍了所有这些(静态为12.4,实例variables为12.5)。

对于静态初始化(12.4节):

类或接口typesT将在第一次发生以下任一事件之前立即被初始化:

  • T是一个类,创build了一个T的实例。
  • T是一个类,由T声明的静态方法被调用。
  • 由T声明的静态字段被分配。
  • 使用由T声明的静态字段,该字段不是常量variables(§4.12.4)。
  • T是一个顶级类(§7.6),并且在T(§8.1.3)中进行词汇嵌套的assert语句(§14.10)被执行。

(和几个黄鼠狼字条款)

基思和克里斯的答案都很好,我只是为我的具体问题增加了一些细节。

静态初始化块按其类的初始化顺序运行。 那么,那是什么顺序呢? 根据JLS 12.4.1:

类或接口typesT将在第一次发生以下任一事件之前立即被初始化:

  • T是一个类,创build了一个T的实例。
  • T是一个类,由T声明的静态方法被调用。
  • 由T声明的静态字段被分配。
  • 使用由T声明的静态字段,该字段不是常量variables(§4.12.4)。
  • T是一个顶级的类,并且执行一个在T中词汇嵌套的assert语句(第14.10节)。

在类Class和包java.lang.reflect中调用某些reflection方法也会导致类或接口初始化。 在任何情况下,类或接口都不会被初始化。

为了说明,下面是对示例中发生的事件的演练:

  1. inputmain
  2. 打印“开始”
  3. 尝试创buildChild的第一个实例,这需要初始化Child
  4. 尝试初始化Child会导致Parent的初始化
  5. 试图初始化父亲导致祖父母的初始化
  6. 在祖父母的初始化开始时,祖父母的静态初始化块被运行
  7. 从技术上讲,Object作为祖父母的父母,在初始化链中得到最后的发言权,但没有任何贡献
  8. 在祖父母的静态初始化块结束之后,程序返回到父母的静态初始化块
  9. 在Parent的静态初始化块结束之后,程序返回到Child的静态初始化块
  10. 此时,Child被初始化,所以它的构造函数可以继续
  11. 由于IAmAClassThatIsNeverUsed永远不会被引用,所以它的代码都不会运行,包括静态初始化块
  12. 本演练的其余部分不涉及静态初始化器,只是为了完整性
  13. 子的构造函数隐式调用super()(即,父类的构造函数)
  14. 父类的构造函数隐式调用super()(即祖父母的构造函数)
  15. 祖父母的构造函数也是这样做的,没有任何效果(同样,Object没有任何贡献)
  16. 祖父母的构造函数调用super()后,立即祖父母的实例初始化块
  17. 祖父母的构造函数的构造函数的其余部分运行和构造函数终止
  18. 这个程序在调用super()(即祖父母的构造函数)后立即返回给父类的构造函数,
  19. 如上所述,Parent的实例初始化器完成它的事情,其构造函数完成
  20. 同样,程序返回并完成Child的构造函数
  21. 此时,该对象已经被实例化
  22. 打印“结束”
  23. 正常终止

一个类的初始化包括执行它的静态初始化程序和在类中声明的静态字段(类variables)的初始化程序。

接口的初始化包括执行接口中声明的字段(常量)的初始值设定项。

在一个类被初始化之前,它的直接超类必须被初始化,但是这个类实现的接口没有被初始化。 类似地,接口的超级接口在初始化之前不会被初始化。

因此,您可以在同一个类中有多个静态和实例初始值设定项

  • 静态初始化器按照它们声明的文本顺序调用(从12.4.2开始 )
  • 实例初始值设定项按声明的文本顺序调用(从12.5开始 )

每个被执行,就像它是一个单一的块。

http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html

请检查java文档。

那么清楚地提到,无论静态块如何可能存在,它们将按照它们出现的顺序作为一个块被执行

所以,

我的理解是java正在看你的代码

 static{ i=1; i=2; } 

static int i;

这就是为什么你得到输出2

希望这是有帮助的

有一种情况是静态块不会被调用。

 class Super { public static int i=10; } class Sub extends Super { static { system.out.println("Static block called"); } } class Test { public static void main (String [] args) { system.out.println(Sub.i); } } 

上面的代码输出10