
静态代码块什么时候执行
静态代码块在类首次被加载到 JVM 时执行,且只执行一次。它不依赖对象创建,也不受继承链中子类调用影响——哪怕你只 Class.forName("com.example.MyClass"),它也会触发。
常见错误现象:在 IDE 中断点没命中、或误以为每次 new MyClass() 都会重跑;其实只要类已加载,后续所有实例化都跳过静态块。
- 执行时机严格绑定类加载阶段(
Linking后的Initialization),不是编译期也不是运行期任意时刻 - 多个静态块按源码顺序依次执行,不能跨文件控制顺序
- 若静态块抛出未捕获异常(如
NullPointerException),类加载失败,后续任何对该类的引用都会抛NoClassDefFoundError
静态代码块 vs 静态变量初始化
两者都在类初始化阶段运行,但顺序和用途不同。静态变量声明时的直接赋值(如 static int x = 10;)本质是编译器自动插入到第一个静态块开头的语句;而显式静态块更适合做多步逻辑、资源预热或条件判断。
使用场景:你想读取配置文件并校验格式,或者初始化一个全局 HashMap 并预填默认值——这些没法用单行赋值完成,就得靠静态块。
立即学习“Java免费学习笔记(深入)”;
-
static final String CONFIG_PATH = System.getProperty("config.dir");是变量初始化,无副作用 -
static { loadConfig(); validateSchema(); }是静态块,可含 I/O、异常处理、循环等 - 若静态变量初始化依赖尚未执行的静态块,会导致该变量为默认值(如
null或0),这是典型初始化顺序陷阱
为什么不能在静态块里用 this 或调用非静态方法
因为静态块执行时,还没有任何对象实例存在,this 根本无从指向;同理,非静态方法必须依附于实例,JVM 此时连 MyClass 的对象内存都没分配。
常见错误现象:IDE 不报错但编译失败,提示 non-static method xxx() cannot be referenced from a static context;或者误把工具方法写成实例方法,结果卡在静态初始化失败。
- 所有在静态块中调用的方法,必须是
static的 - 若需访问外部资源(如数据库连接池),应确保其初始化本身也是静态安全的,避免隐式依赖实例状态
- 不要试图在静态块中启动线程并等待其完成——类加载是单线程同步过程,阻塞会导致整个类加载挂起
类加载器视角下的静态块可见性
静态块属于类定义的一部分,由加载它的 ClassLoader 负责触发。同一个类被两个不同类加载器(如 Tomcat 的 WebAppClassLoader 和 SharedClassLoader)分别加载,会得到两个独立的类对象,各自执行一次静态块。
这在 OSGi、Spring Boot DevTools、或热部署场景下极易踩坑:你以为的“全局单例”其实有多个副本,静态块也重复跑了多次。
- 检查是否真需要跨类加载器共享状态——大多数情况应该改用服务注册或 Spring 的
@PostConstruct - 避免在静态块中写日志或打印语句来“确认执行”,容易因类加载器隔离导致日志分散不可见
- JDK 9+ 模块系统下,若模块未导出包,即使类能加载,静态块也可能因反射限制而无法访问某些类
真正麻烦的不是写静态块,而是它一旦出错,问题会藏在类加载底层,堆栈里看不到业务代码,只有 ExceptionInInitializerError 套着一层又一层的 Cause。盯住那个最内层的原始异常,它通常就在你静态块第一行附近。










