
本文深入解析java继承场景下字段初始化顺序、构造器执行流程及方法重写陷阱,解释为何在父类构造器中调用可重写方法会导致子类字段显示为默认值(如0),并提供安全、规范的实践方案。
本文深入解析java继承场景下字段初始化顺序、构造器执行流程及方法重写陷阱,解释为何在父类构造器中调用可重写方法会导致子类字段显示为默认值(如0),并提供安全、规范的实践方案。
在Java继承体系中,看似简单的字段重定义(如子类声明与父类同名同类型的 int a = 100)极易引发隐蔽的初始化问题。您观察到的输出 Sub : 0 0 300 400 500(而非预期的 100 300 300 400 500)并非语法错误,而是由JVM对象初始化严格时序和构造器执行机制共同导致的关键行为。
核心原因:构造器执行顺序与字段初始化时机
Java对象创建遵循固定流程:
- 分配内存(所有字段设为默认值:
int → 0,Object → null); - 调用父类构造器(
Super()); -
父类字段初始化(
a = 10,b = 30,c = 30,d = 40,e = 50); - 父类构造器体执行 —— 此时
printValues()被调用; -
但此时子类字段尚未初始化!
Sub的a,b,c仍为默认值0(d和e是static,已在类加载时完成初始化,故显示400/500); - 返回后,才执行子类字段初始化(
a = 100,b = 300,c = 300)和子类构造器体。
关键点在于:父类构造器中调用的 printValues() 实际执行的是子类重写的版本(因多态),但此时子类实例字段还未赋值,读取的是内存分配后的初始零值。
验证与修复:安全的初始化实践
将 printValues() 移出构造器,改在对象完全构建后调用:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
class TwentyFive {
public static void main(String[] args) {
Sub sub = new Sub(); // 完整初始化完成
sub.printValues(); // 输出: Sub : 100 300 300 400 500
}
}同时,修正父类构造器中的冗余 + 符号(+a 应为 a),提升代码可读性。
重要注意事项与最佳实践
- ✅ 禁止在构造器中调用可重写方法:这是Java经典反模式。若必须初始化后操作,请使用
final方法或私有方法(无法被重写)。 - ⚠️ 避免字段遮蔽(Field Hiding):子类声明同名字段(如
int a)不会覆盖父类字段,而是独立存在。访问super.a与this.a指向不同内存位置,易引发逻辑混乱。生产代码中应通过protected字段 + 构造器参数或setter统一管理状态。 - ?
static和final static字段在类加载阶段即初始化,不受实例构造顺序影响,因此d/e显示正确值。 - ? 调试建议:使用IDE单步调试,观察字段值在“内存分配→父类初始化→父类构造器执行→子类初始化”各阶段的变化,直观理解JVM初始化协议。
遵循这些原则,不仅能解决当前问题,更能构建出健壮、可维护的面向对象设计。

















