静态代码块在类首次被主动使用进入初始化阶段时执行一次,且绑定于具体类加载器;类加载不触发该块,仅初始化阶段才运行,不同加载器会各自执行一次。

静态代码块不是类一加载就执行,而是在类完成加载、且首次被主动使用进入初始化阶段时才运行一次——但这个“一次”是绑定在具体类加载器上的。
静态代码块执行的前提是类初始化,不是类加载
类加载(Loading)只是把字节码读入方法区,不触发静态代码块;只有当 JVM 判定该类“首次被主动使用”,才会启动初始化(Initialization)阶段,此时静态代码块才执行。常见主动使用包括:
- 用 new 创建该类实例
- 调用该类的静态方法
- 读写该类非 final 的静态字段(如 public static int PORT = 8080;)
- 通过 Class.forName("X") 反射加载(注意:ClassLoader.loadClass() 不触发)
- 初始化子类时,父类尚未初始化 → 先执行父类静态代码块
而仅声明引用(ConfigLoader ref;)或访问编译期常量(public static final String NAME = "A";),不会触发初始化,静态代码块也不会运行。
不同类加载器会各自执行一次静态代码块
类的唯一性由「全限定名 + 类加载器」共同决定。同一份字节码被 AppClassLoader 和自定义 URLClassLoader 分别加载,会产生两个互不可见的 Class 对象:
- 每个 Class 对象拥有独立的静态变量存储区
- 每个 Class 对象的静态代码块在各自初始化时各执行一次
- A.class == B.class 返回 false,哪怕字节码完全相同
这是热部署、OSGi 模块隔离、Web 应用多租户等机制的底层基础。若看到静态代码块“重复执行”,大概率是多个类加载器共存导致,可在块内打印加载器哈希值验证:
System.out.println("Loaded by: " + MyClass.class.getClassLoader());
静态代码块执行顺序受源码位置和继承链约束
同一个类中,静态变量赋值与静态代码块严格按源码从上到下交替执行,不是先赋值再执行块:
- static int a = f1(); 在 static { b = "ok"; } 之前 → f1() 执行时 b 还未赋值,值为默认 null 或 0
- 多个 static{} 块也按书写顺序依次运行
- 继承关系下,顺序固定为:父类静态变量 → 父类静态块 → 子类静态变量 → 子类静态块
子类初始化必然触发父类初始化,但父类初始化不会自动触发子类初始化。
异常会让类永久处于初始化失败状态
静态代码块中抛出未捕获异常(包括 NullPointerException 或显式 throw),JVM 会封装为 ExceptionInInitializerError 并终止初始化。此后所有对该类的主动使用,都会直接抛出同一异常,无法重试。
- 不适合在静态块中做不可靠操作(如网络请求、文件读取未加容错)
- IO 资源建议用 try-with-resources 显式处理
- 避免跨类静态依赖形成循环初始化(如 A.static 块调 B.field,B.static 块又依赖 A.field)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











