java - 相互引用的静态变量初始化?
问题描述
这与静态变量初始化顺序有些相关,但是这个问题并没有真正回答我的问题。
当我运行以下代码时,我预计会发生未定义的行为或无限递归的情况:
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
为什么会这样?
解决方案
答案来自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 的过程如下:
在 C 的初始化锁 LC 上同步。这包括等待当前线程可以获取 LC。
如果 C 的 Class 对象指示某个其他线程正在对 C 进行初始化,则释放 LC 并阻塞当前线程,直到通知正在进行的初始化已完成,此时重复此步骤。
如果 C 的 Class 对象指示当前线程正在对 C 进行初始化,那么这一定是对初始化的递归请求。释放LC并正常完成。
第 3 步是我们在这里采取的步骤。A类已经被当前线程初始化,所以我们处于递归初始化的情况。因此,我们只是放弃并立即正常完成,因此 A.test 保持为空,B.test 获得 A.test 的值为空。
推荐阅读
- .htaccess - 如何在我的 .htaccess 文件上设置 2 条重写规则
- reactjs - Apollo 客户端订阅数据是只读的
- php - 将单个数组转换为变量
- html - 如何在 CSS 中使图像不可选择?
- ios - 发送应用程序进行测试 - 企业帐户
- c++ - 为什么数组上的 std::binary_search 在 cmd 和 linux 终端中给出不同的结果?
- android - 我的 android 模拟器不适用于 vs 代码
- android-studio - 我正在制作音乐播放器,我想让应用程序读取存储空间以获取歌曲
- docker - Gitlab 管道失败,即使部署发生在 GCP 上
- asp.net-core - ASP.NET Core 5:OpenIDConnect 打破默认/根路由