静态代码块仅在类首次被主动使用且完成初始化阶段时执行一次,由触发初始化的类加载器决定;同一字节码被不同类加载器加载会视为独立类,各自执行一次静态代码块。

Java静态代码块只在类被首次主动使用时执行一次,与具体由哪个类加载器加载无关;关键在于“类的唯一性”由类全限定名 + 加载它的类加载器共同决定。同一个类被不同类加载器加载,会视为两个独立类,各自执行一次静态代码块。
静态代码块触发的真正条件
静态代码块执行的前提是类完成初始化阶段(Initialization),而初始化仅发生在以下五种主动使用场景之一:
- 创建该类的实例(new)
- 调用该类的静态方法
- 访问该类的静态字段(非编译期常量)
- 反射调用(如 Class.forName("X"))
- 初始化子类时,父类先被初始化(前提是父类尚未初始化)
注意:单纯引用静态常量(如 public static final int VAL = 1;)不会触发类初始化,因为编译期已内联,不经过类加载流程。
不同类加载器对静态代码块的影响
每个类加载器维护自己的命名空间。即使同一份 .class 字节码,被 AppClassLoader 和自定义 URLClassLoader 分别加载,也会生成两个互不感知的 Class 对象:
- 每个 Class 对象都有独立的静态变量存储区
- 每个 Class 对象的静态代码块各自执行一次(首次初始化时)
- A.class == B.class 判断为 false,哪怕字节码完全相同
这是实现热部署、模块隔离(如 OSGi)、Web 应用多租户等机制的基础。
双亲委派模型下的执行顺序
虽然类加载器分层(Bootstrap → Extension → App → 自定义),但静态代码块的执行时机不受委派过程直接影响——它只取决于哪个加载器最终定义了该类。例如:
- 若 java.lang.String 被 Bootstrap 加载,其静态块在 JVM 启动早期执行
- 若自定义类 com.example.MyUtil 由 AppClassLoader 加载,其静态块在第一次主动使用时执行
- 若你用新 URLClassLoader 重新加载 MyUtil,它的静态块会再次执行——和之前那个类无关
调试与验证建议
想确认静态代码块是否执行、由谁加载,可在块内加入明确标识:
- 打印当前类加载器:System.out.println(MyClass.class.getClassLoader());
- 打印类哈希:System.out.println(MyClass.class.hashCode());
- 配合 JVM 参数 -verbose:class 观察类加载日志
避免误判:不要仅凭“类名相同”就认为是同一个类;必须结合加载器身份判断。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











