首页 > 解决方案 > 相互引用的静态变量初始化?

问题描述

这与静态变量初始化顺序有些相关,但是这个问题并没有真正回答我的问题。

当我运行以下代码时,我预计会发生未定义的行为或无限递归的情况:

class A {
    static File test = B.test;
    public static void main(String[] args) {
         System.out.println(A.test);
         System.out.println(B.test);
    }
}
class B {
     static File test = A.test;
}

但是,相反,我看到:

null
null

为什么会这样?

标签: java

解决方案


答案来自JLS 第 12.4 节。我们从 12.4.1 开始:

12.4.1. 初始化发生时

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

  • T 是一个类,并创建了一个 T 的实例。

  • 调用由 T 声明的静态方法。

  • 分配了一个由 T 声明的静态字段。

  • 使用了由 T 声明的静态字段,并且该字段不是常量变量(第 4.12.4 节)。

A.main被调用时,第三个子句适用,因此A.test必须初始化类 A(以及扩展名)。的初始化A.test触发了上面的第四个子句,这意味着现在必须初始化 B。B 现在必须访问 A 的字段,但 A 尚未初始化。

我们现在需要深入研究第 12.4.2 节:

12.4.2. 详细的初始化过程……对于每个类或接口C,都有一个唯一的初始化锁LC。从 C 到 LC 的映射由 Java 虚拟机实现决定。初始化 C 的过程如下:

  1. 在 C 的初始化锁 LC 上同步。这包括等待当前线程可以获取 LC。

  2. 如果 C 的 Class 对象指示某个其他线程正在对 C 进行初始化,则释放 LC 并阻塞当前线程,直到通知正在进行的初始化已完成,此时重复此步骤。

  3. 如果 C 的 Class 对象指示当前线程正在对 C 进行初始化,那么这一定是对初始化的递归请求。释放LC并正常完成。

第 3 步是我们在这里采取的步骤。A类已经被当前线程初始化,所以我们处于递归初始化的情况。因此,我们只是放弃并立即正常完成,因此 A.test 保持为空,B.test 获得 A.test 的值为空。


推荐阅读