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

时间:2010-01-05 17:01:50

标签: java static-initializer

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

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");
    }
}

得到了这个输出:

  

START
  静态 - 祖父母   静态 - 父母   静电 - 儿童
  实例 - 祖父母   建设者 - 祖父母
  实例 - 父母   构造函数 - 父母
  实例 - 孩子
  建设者 - 孩子
  END

显而易见的答案是,父母的障碍在他们的孩子面前运行,但这可能只是巧合而且如果两个班级不在同一层级中则无济于事。

编辑:

我通过将此示例代码附加到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");
    }
}

正如类名所暗示的那样,我从未在任何地方引用过新类。新程序产生的输出与旧程序相同。

8 个答案:

答案 0 :(得分:90)

参见JLS version 8的第12.4节和第12.5节,它们详细介绍了所有这些(静态12.5和实例变量12.5)。

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

类或接口类型T将在第一次出现以下任何一个之前立即初始化:

  • T是一个类,创建了一个T实例。
  • T是一个类,调用T声明的静态方法。
  • 分配由T声明的静态字段。
  • 使用由T声明的静态字段,该字段不是常量变量(§4.12.4)。
  • T是顶级类(第7.6节),并且执行在词典内嵌套在T(第8.1.3节)内的断言语句(第14.10节)。

(以及几个狡猾的词条)

答案 1 :(得分:60)

首次访问类时,会运行类的静态初始化程序,以创建实例或访问静态方法或字段。

因此,对于多个类,这完全取决于运行导致这些类加载的代码。

答案 2 :(得分:31)

Keith和Chris的答案都很棒,我只是为我的具体问题添加更多细节。

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

  

类或接口类型T将在第一次出现以下任何一个之前立即初始化:

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

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

为了说明,这里是对示例中发生的事情的演练:

  1. 输入主要
  2. 打印“开始”
  3. 尝试创建Child的第一个实例,这需要初始化Child
  4. 尝试初始化子项会导致初始化
  5. 尝试初始化父级会导致祖父母的初始化
  6. 在祖父母初始化开始时,运行祖父母的静态初始化块
  7. 从技术上讲,Object凭借祖父母的父母在初始化链中获得最后的发言权,但它没有任何贡献
  8. 祖父母的静态初始化块结束后,程序回退到Parent的静态初始化块
  9. 在Parent的静态初始化块结束后,程序回退到Child的静态初始化块
  10. 此时,Child已初始化,因此其构造函数可以继续
  11. 由于IAmAClassThatIsNeverUsed从未被引用,因此其代码均未运行,包括静态初始化程序块
  12. 本演练的其余部分与静态初始化程序无关,仅为完整性而包含
  13. Child的构造函数隐式调用super()(即Parent的构造函数)
  14. Parent的构造函数隐式调用super()(即祖父母的构造函数)
  15. 祖父母的构造函数做同样的事情,没有任何效果(再次,对象没有任何贡献)
  16. 在祖父母的构造函数调用super()之后,祖父母的实例初始化程序块
  17. 祖父母的构造函数构造函数的其余部分运行,构造函数终止
  18. 程序在调用super()(即祖父母的构造函数)后立即回退到Parent的构造函数
  19. 如上所述,Parent的实例初始化程序执行其操作并且其构造函数完成
  20. 同样,程序返回并完成Child的构造函数
  21. 此时,对象已实例化
  22. 打印“END”
  23. 正常终止

答案 3 :(得分:1)

类的初始化包括执行静态初始化器和类中声明的静态字段(类变量)的初始化器。

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

在初始化类之前,必须初始化其直接超类,但不会初始化类实现的接口。同样,在初始化接口之前,不会初始化接口的超接口。

答案 4 :(得分:0)

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

  • 静态初始值设定项按声明的文本顺序(来自12.4.2
  • 进行调用
  • 实例初始值设定项按声明的文本顺序(来自12.5
  • 进行调用

每个都像一个块一样被执行。

答案 5 :(得分:0)

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

请检查java文档。

然后清楚地提到无论静态块如何存在,它们将按照它们出现的顺序作为单个块执行

所以,

我的理解是java正在将您的代码视为

static{
i=1;
i=2;
}

static int i;

这就是你得到输出2

的原因

希望这有用

答案 6 :(得分:0)

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

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

答案 7 :(得分:0)

class A {
  public A() { 
    // 2
  }
}

class B extends A{
  static char x = 'x'; // 0
  char y = 'y'; // 3
  public B() { 
    // 4
  }

  public static void main(String[] args) {
    new B(); // 1
  }
}

注释中的数字表示评估顺序,越小,越早。

如示例所示,

  1. 静态变量
  2. 主要
  3. 超类的构造函数
  4. 实例变量
  5. 构造函数