静态代码块不主动处理依赖,而是由JVM按“首次主动使用”触发初始化,严格遵循类加载顺序:父类先于子类、声明顺序执行,编译期常量引用不触发初始化,异常会导致类永久不可用。

静态代码块本身不“处理”类依赖,而是严格遵循 JVM 规定的初始化顺序来响应依赖——关键不是它主动协调,而是它的执行时机和顺序被类加载机制锁定。只要理解触发条件、书写位置和继承关系,就能让依赖自然就位。
静态代码块的执行由“首次主动使用”触发,不是由声明位置决定
一个类的静态代码块不会因为另一个类引用了它就立刻运行。只有当该类被首次主动使用时,JVM 才进入其初始化阶段,执行所有静态成员(含静态块)。常见主动使用包括:
- 用 new 创建该类或其子类的实例
- 调用该类的 public static 方法
- 访问该类的 非编译期常量静态字段(如
public static int PORT = 8080;) - 通过
Class.forName("X")反射加载(注意:ClassLoader.loadClass()不触发) - 子类初始化时,若父类尚未初始化,会强制先完成父类初始化
特别注意:public static final String MSG = "hello"; 这类编译期常量,值在编译时就被内联,引用它不会触发所在类的初始化,静态块也就完全跳过。
同类中静态变量与静态块按源码顺序交替执行
它们不是“先全赋值、再全执行块”,而是谁写在前面,谁先执行。JVM 把它们合并进同一个 <clinit> 方法,逐行处理:
立即学习“Java免费学习笔记(深入)”;
-
static int a = getValue();→ 立即调用getValue() -
static { b = compute(); }→ 紧接着执行该块 - 若
getValue()中访问了后面才声明的static String c,此时c还未初始化,值为null(不是编译错误,但可能引发空指针) - 多个
static{}块也严格按书写顺序执行,无法靠后置覆盖前置逻辑
继承场景下,父类静态内容永远先于子类完成
这是 JVM 强制保障的顺序,不可绕过。例如执行 new Child() 时,完整流程是:
- Parent 类静态变量默认值 → Parent 静态变量赋值 + 静态块(从上到下)
- Child 类静态变量默认值 → Child 静态变量赋值 + 静态块(从上到下)
这个过程只发生一次。后续再 new Child() 或调用 Child.staticMethod(),不会再重复执行任何静态块。如果父类静态初始化失败(如抛出未捕获异常),子类初始化会直接失败,整个类进入“初始化失败”状态,之后所有访问都抛 ExceptionInInitializerError。
依赖外部类时,要确保对方已初始化完成
静态块里调用其他类的静态方法或字段,前提是那个类已经完成初始化。否则可能触发它的初始化,进而影响整体顺序。稳妥做法是:
- 把强依赖的类放在当前类的静态字段声明之前(例如
static Config CONF = Config.getInstance();写在静态块前) - 避免在静态块中调用可能被子类重写的方法(虚方法),因此时子类静态/实例部分尚未准备,易读到默认值
- 对不可靠操作(如网络、文件读取)加 try-catch 并提供 fallback,防止异常导致类永久不可用
- 必要时改用 Holder 模式(延迟初始化内部类),把初始化时机交给第一次实际访问,避开类加载期风险


















