静态代码块在类首次加载时执行一次,构造块在每次创建对象时执行;前者适用于一次性初始化(如数据库连接池),后者用于对象级隔离初始化(如分配唯一ID),测试中需警惕静态资源干扰和异常导致类加载失败。

静态代码块在类首次被加载时执行一次,构造块在每次创建对象时执行——这个差异在测试环境下尤为明显,直接影响初始化逻辑的复用性、资源独占性和断言可靠性。
静态代码块:只在类首次引用时触发
哪怕你运行一百次测试方法,只要该类没被重新加载(JVM 默认不卸载类),静态代码块就只执行一次。常见触发场景包括:
• 第一次 new 该类实例
• 第一次访问其静态字段或调用静态方法
• 使用 Class.forName("X") 显式加载
• JUnit 运行器首次加载测试类(如 @BeforeClass 所在类)
这意味着:数据库连接池初始化、Mockito 的全局 stub 配置、日志框架启动等一次性动作,放在这里不会重复执行;但若测试间需隔离状态(比如清空缓存),它无法自动重置。
构造块:每次 new 都会执行,且严格早于构造方法
每调用一次 new Xxx(),构造块就完整跑一遍,顺序固定为:
父类静态块 → 子类静态块 → 父类构造块 → 子类构造块 → 父类构造方法 → 子类构造方法
适合做对象级初始化:比如给每个测试实例分配唯一 ID、重置内部计数器、注入 mock 依赖、记录创建时间戳。它能访问 this 和实例变量,但注意——此时构造方法体还没执行,某些字段可能仍为默认值(如 int=0、Object=null)。
测试中容易踩的坑
- 静态块里抛出未捕获异常(如配置文件缺失),会导致整个类加载失败,后续所有测试用例报 NoClassDefFoundError,而非预期的 RuntimeException
- 多个测试方法共用一个类的静态资源(如 static List<String> logEntries),彼此干扰;而构造块天然隔离,每次 new 都是干净实例
- 使用 @RunWith(MockitoJUnitRunner.class) 或 @ExtendWith(MockitoExtension.class) 时,静态块在 Runner 初始化前就已执行,可能早于 mock 注入时机
- JUnit 5 的 @TestInstance(PER_CLASS) 模式下,测试类实例复用,构造块只执行一次——此时行为更接近静态块,需特别留意
怎么验证执行次数?简单加日志就行
在测试类中写:
立即学习“Java免费学习笔记(深入)”;
static { System.out.println("【静态块】执行"); }{ System.out.println("【构造块】执行"); }
然后分别运行单个测试、连续运行多个测试、或用 @BeforeAll/@BeforeEach 调用 new ——输出顺序和频次一目了然。记住:静态块打一次印,构造块打几次印,就说明它执行了几次。


















