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

Pop*_*ops 91 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");
    }
}
Run Code Online (Sandbox Code Playgroud)

得到这个输出:

START
static - grandparent
static - parent
static - child
instance - grandparent
constructor - grandparent
instance - parent
constructor - parent
instance - child
constructor - child
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");
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Kei*_*all 92

参见JLS版本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节).

(和几个狡猾的词条款)

  • 最好的答案.很好! (2认同)

Chr*_*ung 60

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

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

  • 您是否已阅读[Java语言规范](http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4)?加载类时,静态初始化*不会*运行。 (2认同)

Pop*_*ops 32

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

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

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

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

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

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

  1. 输入main
  2. 打印"开始"
  3. 尝试创建Child的第一个实例,这需要初始化Child
  4. 尝试初始化Child会导致Parent的初始化
  5. 尝试初始化Parent会导致祖父母的初始化
  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. 打印"结束"
  23. 正常终止

  • 在12.4.1中我们还发现:`在一个类被初始化之前,它的直接超类必须被初始化,...` (2认同)