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

并得到这个输出:

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

从这个问题的明显答案是,父母的块在他们的孩子之前运行,但这可能只是一个巧合,并且如果两个班不在同一个层次结构中则无济于事。

编辑:

我通过将其附加到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");
    }
}

正如班级名称所暗示的,我从来没有在任何地方引用新班级。 新方案产生的结果与旧方案相同。


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

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


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

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

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

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


    基思和克里斯的答案都很好,我只是为我的具体问题添加更多细节。

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

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

  • T是一个类,创建了一个T的实例。
  • T是一个类,由T声明的静态方法被调用。
  • 由T声明的静态字段被分配。
  • 使用由T声明的静态字段,该字段不是常量变量(§4.12.4)。
  • T是一个顶级类,并且执行在词汇上嵌套在T中的断言语句(第14.10节)。
  • 在类Class和包java.lang.reflect中调用某些反射方法也会导致类或接口初始化。 在任何其他情况下,类或接口都不会被初始化。

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

  • 输入main
  • 打印“开始”
  • 尝试创建Child的第一个实例,该实例需要初始化Child
  • 试图初始化Child会导致Parent的初始化
  • 尝试初始化Parent会导致祖父母的初始化
  • 在祖父母的初始化开始时,祖父母的静态初始化块被运行
  • 从技术上讲,Object作为祖父母的父母,在初始化链中得到最后的发言权,但它没有任何贡献
  • 在祖父母的静态初始化程序块结束后,程序返回到父母的静态初始化程序块
  • 在Parent的静态初始化块结束后,程序返回到Child的静态初始化块
  • 此时,Child被初始化,所以它的构造函数可以继续
  • 由于IAmAClassThatIsNeverUsed永远不会被引用,因此它的代码都不会运行,包括静态初始化块
  • 本演练的其余部分不涉及静态初始值设定项,仅包含为了完整性
  • 子的构造函数隐式调用super()(即,父类的构造函数)
  • 父类的构造函数隐式调用super()(即祖父母的构造函数)
  • 祖父母的构造函数也是这样做的,它没有任何效果(同样,Object没有任何贡献)
  • 在祖父母的构造函数调用super()之后立即传入祖父母的实例初始化块
  • 祖父母的构造函数的构造函数的其余部分运行并且构造函数终止
  • 该程序在调用super()(即祖父母的构造函数)后立即返回给父类的构造函数,
  • 如上所述,Parent的实例初始化器完成它的任务,并且构造器完成
  • 同样,程序返回并完成Child的构造函数
  • 此时,该对象已被实例化
  • 打印“结束”
  • 正常终止
  • 链接地址: http://www.djcxy.com/p/78805.html

    上一篇: In what order do static/instance initializer blocks in Java run?

    下一篇: Static variable in Java