成员代码块在每次new对象时、调用构造器之前执行,顺序为父类成员代码块→父类构造器→子类成员代码块→子类构造器,该顺序由jvm规范明确保证,可通过system.out.println验证。

成员代码块在构造器之前执行的时机怎么确认
成员代码块(非静态代码块)确实会在每次 new 对象时、调用构造器 之前 执行。这不是“大概率”或“通常”,而是 JVM 规范明确的执行顺序:先执行父类成员代码块 → 父类构造器 → 子类成员代码块 → 子类构造器。
验证方式很简单,直接在代码块和构造器里加 System.out.println:
class A {
{ System.out.println("A 成员代码块"); }
public A() { System.out.println("A 构造器"); }
}
class B extends A {
{ System.out.println("B 成员代码块"); }
public B() { System.out.println("B 构造器"); }
}
执行 new B() 输出顺序是:A 成员代码块 → A 构造器 → B 成员代码块 → B 构造器。这个顺序稳定、可复现,不依赖编译器优化。
成员代码块 vs 构造器:该把什么逻辑放进去
成员代码块适合放「所有构造器共用、且必须在字段初始化之后、构造器主体之前运行」的逻辑。它不能接收参数,也不能抛检异常(除非用 try-catch 包裹),所以适用场景很窄。
立即学习“Java免费学习笔记(深入)”;
- 初始化 final 字段(尤其当值需多行计算,又不想写重复逻辑到每个构造器里)
- 执行轻量级预校验(如检查某个 static 配置是否已加载)
- 记录对象创建日志(注意:别在这里做耗时操作,比如 IO 或远程调用)
- 不能做需要参数的事——比如根据传入的
id初始化某个字段,这种只能进构造器
常见错误:误以为成员代码块能访问构造器参数或 this 引用
成员代码块里可以安全使用 this,也能访问实例字段,但不能访问构造器的形参——因为此时构造器还没开始执行,参数根本不存在于当前作用域。
下面这段代码会编译失败:
class C {
private String name;
{ name = prefix + "default"; } // ❌ prefix 未定义
public C(String prefix) { this.name = prefix + "ok"; }
}
真正容易踩的坑是:以为成员代码块里能拿到构造器刚算出的中间值,或者想用它做条件分支(比如根据某个配置开关决定初始化策略)——这类逻辑必须收拢到构造器里,靠 if/else 或工厂方法处理。
多个成员代码块的执行顺序和性能影响
一个类里可以有多个成员代码块,它们按**出现的先后顺序**依次执行,全部在任意构造器之前跑完。JVM 不会合并、重排或跳过它们。
性能上几乎无感,但要注意两点:
- 每个 new 都会完整执行一遍,不是只执行一次(那是 static 代码块的事)
- 如果块里做了反射调用、正则编译、或 new 大对象,会拖慢对象创建速度,尤其在高频 new 场景(如循环解析 JSON)下可能成为瓶颈
- 调试时,IDE 的断点可能不会停在成员代码块第一行(尤其用了 Lombok @Data 后),建议在块内首行加个显式语句(如
int _ = 0;)再打点
成员代码块不是语法糖,它是字节码里真实存在的 <init></init> 方法前缀逻辑;写多了既难读又难维护,真要封装初始化行为,优先考虑私有辅助方法 + 构造器调用。










