静态代码块不避免循环依赖,而是易触发它;应仅执行安全无副作用操作,避免跨类静态引用、延迟高风险逻辑、用holder模式或@lazy实现懒加载,并显式分步初始化加校验。

静态代码块本身不直接“避免”循环依赖,而是容易成为循环依赖的触发点。关键在于控制类加载时序、推迟高风险操作、切断隐式引用链——不是删掉 static 块,而是让它只做安全、确定、无副作用的事。
别在 static 块里调用未就绪的类或方法
如果类 A 的 static 块中直接访问类 B 的静态字段或方法,而类 B 又在自己的 static 块里反向依赖类 A,JVM 就会在类加载阶段卡住,最终抛出 ExceptionInInitializerError,后续所有对该类的访问都会变成 NoClassDefFoundError。
- 禁止在 static 块中调用另一方尚未完成初始化的枚举常量、静态 final 字段或静态工厂方法
- 避免通过
Class.forName()或 SpringApplicationContext.getBean()触发新类加载 - 检查调用链:当前 static 块 → 调用的方法 → 该方法是否引用了其他正在初始化的类
把不可靠逻辑移出 static 块,改用懒加载
配置读取、网络请求、数据库连接等易失败操作,根本不该出现在类加载阶段。它们应该延迟到第一次真正需要时才执行。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 用静态内部类 Holder 模式:定义私有 static class Holder { static final Object INSTANCE = init(); },外层类不触发其加载
- 封装成静态方法,由业务方显式调用,并自行处理异常(如重试、降级)
- Spring 环境下可用
@Lazy注解让 Bean 延迟实例化,避开构造期循环
用字符串或 ID 替代硬引用,延迟解析
枚举之间、配置类之间最常见的循环,源于构造器或 static 块里直接写 OtherEnum.VALUE。此时对方类可能还没走到常量赋值那一步,字段仍是 null。
- 把枚举引用改成字符串标识,比如
Status("PENDING", "APPROVE_ACTION") - 在业务方法中按需调用
EnumB.valueOf("APPROVE_ACTION"),并捕获IllegalArgumentException - 或统一维护一个启动后加载的映射表(如
Map<string enumb></string>),static 块只负责注册 key,不查 value
显式分步初始化,加日志和断言验证
别指望字段声明顺序能保证初始化顺序——那是 JVM 实现细节,不可靠。把初始化逻辑写进 static 块,并逐行控制。
- 先加载基础配置(如 properties 文件),再构建核心对象,最后校验状态
- 每步之后加
log.info("Loaded X")或Objects.requireNonNull(y, "Y must be ready") - 确保父类 static 块已执行完毕,再在子类 static 块中使用其静态值(JVM 保障父子类加载顺序)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










