实例初始化块在多态下执行时机固定但存在陷阱:它总在对应类构造器前执行,此时若调用被子类重写的方法,会执行子类版本,而子类字段尚未初始化,可能导致null值等问题;应避免在其中调用可重写方法,优先使用final/private方法或移至构造器末尾。

构造块(即实例初始化块)在多态环境下执行顺序固定,但容易因方法重写导致意料之外的行为——关键不在于“能不能用”,而在于“何时执行、执行时调用的是哪个版本的方法”。
构造块的执行时机与继承链顺序
实例初始化块在每次创建对象时执行,且严格按以下顺序发生:
- 父类静态变量和静态初始化块(仅首次加载类时执行一次)
- 子类静态变量和静态初始化块
- 父类普通字段默认值赋值 → 父类实例初始化块 → 父类构造器主体
- 子类普通字段默认值赋值 → 子类实例初始化块 → 子类构造器主体
注意:实例初始化块总在对应类的构造器代码之前执行,但此时对象尚未完全构造完成,this已可见但类型尚未“稳定”。
多态下方法调用可能引发陷阱
若实例初始化块中调用了被子类重写的方法(尤其是非final、非private方法),实际执行的是子类的版本——即使此时子类字段还未初始化。
例如:
class Parent {
{ init(); } // 实例初始化块
void init() { System.out.println("Parent.init"); }
}
class Child extends Parent {
String name = "child";
void init() { System.out.println("Child.init: " + name); } // name 此时尚未赋值!
}
执行 new Child() 会输出 Child.init: null,而非 "child" ——因为 name = "child" 的赋值语句在实例初始化块之后才执行。
安全初始化的实践建议
- 避免在实例初始化块中调用可被重写的方法;如必须调用,应声明为 final 或 private
- 将依赖子类状态的逻辑移至构造器末尾或专用初始化方法(确保字段已赋值)
- 优先使用构造器参数传入必要初始值,减少对字段隐式依赖
- 考虑使用 builder 模式或静态工厂方法,把复杂初始化逻辑显式化、可控化
与构造器协同的典型模式
构造块适合做“每个实例都需执行的通用预处理”,比如日志记录、基础状态标记;但涉及字段计算、外部资源获取等操作,应放在构造器中,并明确区分父类/子类职责。
例如统一设置创建时间戳:
class Entity {
final long createdAt;
{ createdAt = System.currentTimeMillis(); } // 安全:final 字段,且无方法调用
}
这种写法简洁可靠,不会受多态影响。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











