实例初始化块(不带static的{}块)是Java里一个存在感很低但真实起作用的语法——这篇真实记录父子类都有初始化块、字段初始化、构造器时,六个步骤真实按什么顺序执行。
Base和Derived各自定义了一个实例初始化块、一个赋值字段、一个构造器,用共享的静态日志列表记录每一步真实执行的时机。
InstanceInitializerDemo.java static class Base { { log.add("Base-实例初始化块"); } int baseField = logAndReturn("Base-字段初始化", 1); Base() { log.add("Base-构造器"); } } static class Derived extends Base { /* 同样结构 */ }
真实完整执行顺序: 1. Base-实例初始化块 2. Base-字段初始化 3. Base-构造器 4. Derived-实例初始化块 5. Derived-字段初始化 6. Derived-构造器
真实结果证明了两条真实规则的叠加效果:第一,父类的初始化(隐式super()调用)真实必须完整跑完才会开始执行子类的任何初始化逻辑——不存在"父类块、子类块交替执行"这种情况;第二,同一个类内部,实例初始化块和字段初始化真实是按源码书写顺序执行的,且真实都发生在这个类自己的构造器方法体真正执行之前。这套顺序背后其实对应着字节码层面的真实机制:实例初始化块和字段初始化表达式,真实都会被编译器搬运并拼接进构造器方法(<init>)的开头部分,紧跟在super()调用之后、开发者手写的构造器代码之前。
InstanceInitializerDemo.java static class MultiBlock extends Base { { log.add("第1个实例初始化块"); } int x = logAndReturn("字段x初始化", 10); { log.add("第2个实例初始化块"); } int y = logAndReturn("字段y初始化", 20); MultiBlock() { log.add("MultiBlock-构造器"); } }
真实多个初始化块按源码顺序执行: Base-实例初始化块 Base-字段初始化 Base-构造器 第1个实例初始化块 字段x初始化 第2个实例初始化块 字段y初始化 MultiBlock-构造器
每一步都是真实运行 new MultiBlock() 时,共享静态日志列表记录下的真实执行时机,不是按规则手工排的。