You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java接口与类循环依赖引发的异常行为探究

问题分析与解答

核心前提:JVM类/接口初始化规则

在解释具体场景前,先明确JVM初始化的关键规则:

  • 类初始化触发时机:首次主动使用类(访问静态字段、调用静态方法、实例化类等),初始化类时会先初始化父类,但默认不会初始化实现的接口。
  • 接口初始化触发时机:首次访问接口自身声明的静态字段,或调用接口的default方法。
  • 循环初始化处理:当类/接口处于「正在初始化」状态时,同一线程再次触发其初始化会直接跳过,此时访问静态字段返回的是准备阶段的默认值(如null)。

1. 为何第一个代码片段会输出null?

执行流程拆解:

  1. Main的main方法第一条语句访问Class.INSTANCE,触发Class的初始化。
  2. 由于Interface包含default方法,JVM在初始化Class时会提前触发Interface的初始化(Java 8引入default方法后的特殊处理:实现类初始化时,需确保接口的default方法元数据可用,因此会提前初始化接口)。
  3. 初始化Interface时,执行静态字段赋值Interface INSTANCE = Class.INSTANCE;。此时Class处于「正在初始化」状态,还未完成INSTANCE的赋值,JVM允许访问但返回默认值null,因此Interface.INSTANCE被赋值为null,Interface初始化完成。
  4. 回到Class的初始化流程,完成public static final Class INSTANCE = new Class();的赋值,Class初始化完成。
  5. 输出Class.INSTANCE得到有效实例,随后访问Interface.INSTANCE时,因Interface已完成初始化,其INSTANCE字段保持null,最终输出null。

2. 为何第二、第三个代码片段不会输出null?

场景二:交换println语句顺序

执行流程:

  1. main方法第一条语句访问Interface.INSTANCE,触发Interface的初始化。
  2. Interface初始化时执行Interface INSTANCE = Class.INSTANCE;,访问Class.INSTANCE触发Class的初始化。
  3. Class初始化时,因Interface处于「正在初始化」状态,JVM直接继续Class的初始化,完成INSTANCE = new Class();的赋值。
  4. Class初始化完成后,Interface.INSTANCE被赋值为该有效实例,Interface初始化完成。
  5. 先后输出Interface.INSTANCE和Class.INSTANCE,均得到有效实例,无null。

场景三:移除接口的default方法

执行流程:

  1. main方法第一条语句访问Class.INSTANCE,触发Class的初始化。此时Interface无default方法,JVM遵循「类初始化不触发实现接口初始化」的规则,不会初始化Interface。
  2. Class完成初始化,INSTANCE赋值为有效实例,输出正确结果。
  3. 第二条语句访问Interface.INSTANCE,触发Interface的初始化。此时Class已完全初始化,Class.INSTANCE是有效实例,因此Interface.INSTANCE被赋值为该实例,输出正确结果,无null。

内容的提问来源于stack exchange,提问作者ParSal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 09:54:53