Java中字段最终值由执行顺序决定:先默认值,再按源码顺序执行字段显式初始化和构造块,最后执行构造方法体;构造块位置影响是否覆盖字段初始值,多构造器时可避免重复代码,父子类中需警惕父类构造器调用子类重写方法时子类字段尚未初始化。

构造块和构造方法的执行顺序直接决定字段最终值——不是“谁写在后面谁生效”,而是“谁先执行谁先写,后执行的会覆盖前面的”。关键在于 Java 严格按源码声明顺序交织执行字段显式初始化和构造块,而构造方法体永远最后运行。
字段赋值实际发生顺序
每次 new 对象时,实例字段初始化流程如下:
- 所有字段先被赋予默认值(int→0,引用→null)
- 按类中从上到下的文本顺序,依次执行:字段显式赋值语句 和 构造代码块(二者谁在前谁先执行)
- 最后才进入构造方法体,执行其中的赋值语句
例如:
private String name = "A"; // 第1步执行
{ name = "B"; } // 第2步执行(构造块在字段后)
public Person() { name = "C"; } // 第3步执行 → 最终 name = "C"
构造块位置决定它能否真正改值
构造块只有写在字段声明之后,才能覆盖字段的显式初始值;如果写在前面,会被后续的字段赋值覆盖。
立即学习“Java免费学习笔记(深入)”;
- 块在前、字段在后:{ id = 100; } private int id = 5; → id = 5(字段赋值覆盖块)
- 字段在前、块在后:private int id = 5; { id = 100; } → id = 100(块生效)
这是因为编译器把字段显式初始化“内联”进实例初始化逻辑,但依然遵守源码顺序——构造块不是“全局拦截器”,它只是按位置插入的一段初始化代码。
多构造器场景下构造块的价值
当一个类有多个构造器,且都需要设置同一组默认值或执行共性操作时,构造块能天然避免遗漏:
- 不用在每个构造器第一行重复写 status = "INIT"; createTime = new Date();
- 哪怕某个构造器忘了调用 super() 或 this(),构造块仍会执行(只要 new 了对象)
- 适合轻量校验、日志标记、资源预占等副作用操作,且不依赖构造参数
但它不能替代构造器做参数驱动的差异化赋值——比如根据 name 是否为空来决定 nickname,这必须放在构造方法里。
父子类中顺序更需警惕
子类 new 过程中,初始化顺序是:父类字段/块 → 父类构造方法 → 子类字段/块 → 子类构造方法。特别注意:
- 父类构造方法中若调用被子类重写的方法,此时子类字段尚未执行自己的字段赋值或构造块,值仍是默认值(如 null 或 0)
- 子类构造块里的赋值,一定晚于父类构造方法体执行,无法影响父类方法中已使用的状态
这种延迟可能导致空指针或逻辑错乱,调试时建议在各环节加日志验证实际执行流。


















