
静态代码块和实例代码块谁先执行
静态代码块在类加载时执行,且只执行一次;实例代码块在每次 new 对象时执行,在构造函数体之前。顺序很明确:静态代码块 → 实例代码块 → 构造函数体。
容易踩的坑:static 代码块里不能访问非 static 成员变量或方法,否则编译报错 non-static variable xxx cannot be referenced from a static context。
- 父类静态代码块先于子类静态代码块(按类加载顺序)
- 同一个类里多个静态代码块,按出现顺序执行
- 实例代码块不参与继承,子类 new 对象时只执行子类自己的实例代码块
构造代码块(即实例代码块)能替代构造函数吗
不能。实例代码块适合写所有构造函数共用的初始化逻辑,比如字段默认值、日志记录、资源预检等;但它不能接收参数,也不能重载,更不能控制对象创建流程。
常见错误现象:把本该在构造函数里根据参数做分支初始化的逻辑,硬塞进实例代码块,结果所有构造路径都无差别执行,导致状态错乱。
立即学习“Java免费学习笔记(深入)”;
- 实例代码块在每个构造函数的第一行隐式插入(编译器自动加的)
- 如果构造函数第一行是
this(...)或super(...),实例代码块仍会在调用目标构造函数前执行(注意:不是在 super 后!) - 性能上几乎无差异,但语义更清晰——它属于“对象诞生时必做的事”,而非“某个构造入口做的事”
初始化顺序涉及继承时的实际表现
Java 初始化顺序有固定链条:父类静态 → 子类静态 → 父类实例代码块 → 父类构造函数 → 子类实例代码块 → 子类构造函数。这个顺序决定了字段值可能被多次覆盖。
典型问题场景:父类字段在父类实例代码块中赋值为 10,子类实例代码块又赋为 20,而子类构造函数里再设为 30——最终值取决于谁最后写。
- 字段声明时的初始化(如
int x = 5;)本质就是被编译器挪进了实例代码块,位置在显式写的实例代码块之前 -
final字段必须在实例代码块或构造函数结束前完成赋值,否则编译失败 - 不要在实例代码块里调用可被子类重写的方法(
overridable method call in constructor),可能导致子类字段未初始化就被访问
什么时候该用静态代码块而不是 static 字段直接初始化
当初始化逻辑需要多行语句、异常处理、或依赖外部资源(如读配置、连数据库)时,必须用静态代码块。单纯赋值用 static int x = 5; 更简洁安全。
一个常被忽略的兼容性点:静态代码块在 Java 9+ 模块系统下,若类位于 unnamed module,行为不变;但若在 named module 且被其他模块反射访问,类加载时机可能受模块导出策略影响。
- 静态代码块里抛出未捕获异常(如
IOException),会导致ExceptionInInitializerError,之后任何对该类的引用都会立即失败 - 避免在静态代码块里做耗时操作(如网络请求),会阻塞整个类加载过程,影响启动速度
- 单元测试中,静态代码块只在第一次加载类时执行,多次
@Test方法间不会重跑——这点常被误认为“没生效”
初始化顺序看着线性,实际在继承 + 泛型擦除 + 动态代理混合时,很容易漏掉某一层的静态触发时机。最稳妥的做法是:把跨类依赖的初始化逻辑尽量推迟到首次使用时(比如用 Holder 模式),而不是堆在类加载阶段。










