编程中级/高级Java开发者

在Java中,类初始化顺序是什么,它是如何工作的,以及对静态和非静态成员初始化顺序的误解可能导致什么后果?

用 Hintsage AI 助手通过面试

答案。

Java中的类初始化顺序(class initialization order)定义了字段、块和构造函数的初始化顺序。简而言之:

  1. 静态字段和静态初始化器在类加载到JVM时(每个类一次)按照其在类中的出现顺序执行。
  2. 非静态字段和初始化器在每次创建实例时,按照其出现顺序执行,在调用构造函数之前。
  3. 构造函数在初始化所有字段和块之后执行。

示例

class Parent { static { System.out.println("Parent static"); } { System.out.println("Parent init"); } Parent() { System.out.println("Parent constructor"); } } class Child extends Parent { static { System.out.println("Child static"); } { System.out.println("Child init"); } Child() { System.out.println("Child constructor"); } } // new Child() -> 顺序是什么?

输出将是:

  1. Parent static
  2. Child static
  3. Parent init
  4. Parent constructor
  5. Child init
  6. Child constructor

首先是父类的静态块,然后是子类的静态块,然后是非静态块和构造函数按继承顺序执行。

陷阱问题。

问题: 类中的静态初始化器将在何时执行:

class Ex {
    static { System.out.println("static"); }
}

— 在创建第一个实例时,还是在第一次访问任何静态方法/字段时?

答案: 静态初始化器在首次访问类时执行,包括对任何静态成员(方法/字段)的访问,而不仅仅是在创建实例时。

由于对该主题细节缺乏了解而导致的真实错误示例。


故事

在银行应用程序中,静态连接池在静态块中初始化,但由于在创建第一个实例之前访问静态方法,连接尚未创建。这导致在负载下运行时出现NullPointerException。


故事

日志服务依赖于在父类构造函数调用之后初始化的非静态字段。日志被记录到null记录器中,导致重要的错误消息丢失。


故事

在添加新字段并赋予初始值时,开发人员将其放在初始化块之后:块试图使用尚未初始化的字段,导致应用程序启动时出现错误,且原因难以追踪。