构造代码块是用{ }括起、位于类体中方法外的代码段,它在实例变量赋值后、构造方法体前执行,每次new都触发,用于统一前置初始化逻辑。

构造代码块是Java中对象初始化流程里承上启下的关键环节——它在实例变量显式赋值之后、构造方法之前执行,每次new都会触发,且父子类中各自独立运行。
构造代码块到底是什么
构造代码块就是用{ }括起来的一段代码,写在类体中、方法外。它不是方法,没有名字和参数,也不属于任何对象或类的“调用入口”,但JVM会把它自动合并进每个构造方法的开头(在super()或this()调用之后、构造体代码之前)。
它和实例变量初始化共同构成“实例初始化阶段”,作用是为所有构造方法提供统一的前置初始化逻辑,比如日志记录、资源预检、状态校验等。
它在初始化流程中的准确位置
当你执行new Child()时,JVM按严格顺序执行:
- 父类静态变量赋值 → 父类静态代码块(仅首次)
- 子类静态变量赋值 → 子类静态代码块(仅首次)
- 父类实例变量显式赋值 → 父类构造代码块
- 父类构造方法体
- 子类实例变量显式赋值 → 子类构造代码块
- 子类构造方法体
注意:构造代码块总在对应类的实例变量赋值之后执行;如果变量赋值调用了方法(如String name = initName();),该方法会在构造代码块之前运行——因为编译器已把变量初始化逻辑提前插入了。
为什么不用构造方法而用构造代码块
当多个构造方法需要执行相同初始化动作时,构造代码块可避免重复代码。例如:
class User {
private String id = generateId(); // 实例变量赋值
{ // 构造代码块
System.out.println("用户对象正在初始化...");
validateEnvironment();
}
public User() { /* 无参 */ }
public User(String name) { /* 带参 */ }
public User(int age) { /* 另一重载 */ }
}
三个构造方法都自动包含那段校验和打印逻辑,无需在每个构造体里手动复制。
而如果把这段逻辑写进每个构造方法,不仅冗余,还容易漏改;若写成私有初始化方法再调用,又多一层栈帧开销,且无法保证“一定被调用”(开发者可能忘记)。
实战中容易踩的坑
构造代码块看似简单,但结合继承和方法重写时容易引发意外:
-
不要在构造代码块里调用可被子类重写的方法:此时子类字段尚未初始化,可能导致
null或默认值误用 -
不能访问
this以外的未初始化上下文:比如在静态上下文中引用构造代码块里的变量(语法不允许) -
不能抛出受检异常:除非在块内捕获,否则编译失败(构造代码块隐式属于构造方法体,而构造方法声明不支持
throws Exception,除非明确声明)
一个典型风险示例:initName()在父类构造代码块中被调用,但子类重写了它并访问了子类自己的实例字段——该字段此时还是null或0。











