
java中对象在构造器执行前已被jvm创建,因此抽象类的实例初始化块可调用子类实现的抽象方法——此时对象已存在但尚未完全初始化,属高危行为,需谨慎规避。
java中对象在构造器执行前已被jvm创建,因此抽象类的实例初始化块可调用子类实现的抽象方法——此时对象已存在但尚未完全初始化,属高危行为,需谨慎规避。
在Java对象生命周期中,一个常见却极易被误解的关键点是:对象的创建(allocation)与初始化(initialization)是两个分离的阶段。new Platypus() 所触发的并非“一步构造”,而是严格遵循JVM规范的三步过程:
- 对象分配(new 指令):JVM在堆上为 Platypus 分配内存空间,此时所有字段(包括继承自 Mammal 的字段)已被赋予默认值(如 null、0、false);
- 父类初始化链启动:按继承顺序,从最顶层父类开始执行——先执行 Mammal 的实例初始化块({}),再执行 Mammal() 构造器;
- 子类初始化继续:返回后执行 Platypus() 构造器体。
正是第2步中,Mammal 的实例块 System.out.println(chew()) 被执行——而此时 Platypus 的构造器尚未运行,其字段(如 private String name)仍为 null,但 JVM 已将该对象的运行时类型确定为 Platypus,因此 chew() 的动态分派(virtual dispatch)会命中 Platypus.chew() 实现。
✅ 正确理解示例输出顺序的根源:
"yummy!" ← Mammal 实例块中调用 chew(),实际执行 Platypus.chew() "Mammal constructor" ← Mammal() 构造器体 "Platypus constructor" ← Platypus() 构造器体
⚠️ 但这是危险的反模式:
立即学习“Java免费学习笔记(深入)”;
- 子类方法可能访问未初始化的字段(如 this.name.length() 将抛 NullPointerException);
- 违反“构造器应保证对象处于一致状态”的设计契约;
- 抽象类无法预知子类如何实现 chew(),更无法约束其依赖前提。
? 安全替代方案(推荐):
- ✅ 模板方法模式:将可变逻辑封装为 protected final 模板方法,在父类构造完成后由子类显式调用;
- ✅ 延迟初始化(Lazy Init):通过 Supplier<T> 或 Optional<T> 将抽象行为推迟至对象完全构建后;
- ✅ 工厂+构建器模式:将复杂初始化移出构造流程,交由静态工厂或 Builder 统一管控。
? 核心原则:永远不要在构造器或实例初始化块中调用任何可被子类重写的方法(包括抽象方法)。这是《Effective Java》第17条明确警示的“Avoid calling overridable methods in constructors”。
综上,技术上可行 ≠ 设计上合理。理解JVM对象创建机制是基础,而坚守初始化安全边界,才是写出健壮抽象类的关键。


















