Java中父类构造器修改的字段值为何会被重置?
问题分析:匿名内部类字段赋值后被重置的原因与解决
问题现象
你定义了三个类:
SuperClass:实现Runnable的抽象类,构造器中调用未实现的run()方法ExtendingClass:继承SuperClass的抽象类,包含默认值为1的字段field,构造器调用父类构造器- 匿名内部类继承
ExtendingClass并实现run()方法,将field改为2,但最终输出field的值是1,而run()内部输出的是2
代码示例
MainClass.java
package mainPackage; public class MainClass { public MainClass() { ExtendingClass s=new ExtendingClass() { @Override public void run() { field=2; System.out.println(field); } }; System.out.println(s.field); } public static void main(String[] args) { new MainClass(); } }
SuperClass.java
package mainPackage; public abstract class SuperClass implements Runnable{ public SuperClass() { run(); } }
ExtendingClass.java
package mainPackage; public abstract class ExtendingClass extends SuperClass { int field=1; public ExtendingClass() { super(); } }
核心原因:Java对象初始化顺序
问题出在父类构造器调用子类重写方法时,子类实例变量尚未完成显式初始化,具体执行流程如下:
- 创建匿名内部类实例时,先触发父类
ExtendingClass的构造器 ExtendingClass构造器首先调用super(),进入SuperClass构造器SuperClass构造器调用run(),由于多态特性,实际执行的是匿名类的run()实现:将field赋值为2,此时控制台输出2SuperClass构造器执行完毕,回到ExtendingClass构造器,此时执行实例变量的显式初始化逻辑:int field=1;,这一步会把之前赋值的2覆盖为1- 最终
MainClass中输出s.field时,值已经被重置为1
解决方案
- 禁止在构造器中调用可重写的方法:这是Java开发的经典禁忌,因为子类对象在父类构造器执行时还未完成初始化,调用重写方法会导致不可预期的行为
- 改用初始化方法:将需要在对象创建后执行的逻辑放到一个单独的方法中,比如
init(),在对象实例化后手动调用,而不是在构造器中触发 - 调整字段初始化时机:如果字段必须在构造阶段赋值,可以将字段的初始化逻辑放到构造器内部,而不是显式声明时赋值,这样可以避免被父类构造器执行后的初始化步骤覆盖
修正后的示例代码
比如修改ExtendingClass,将字段初始化放到构造器中,同时修改SuperClass不调用run(),而是提供初始化方法:
SuperClass.java
package mainPackage; public abstract class SuperClass implements Runnable{ public SuperClass() { // 不再在构造器中调用run() } // 提供初始化方法,在对象创建后调用 public void initialize() { run(); } }
ExtendingClass.java
package mainPackage; public abstract class ExtendingClass extends SuperClass { int field; public ExtendingClass() { super(); // 构造器中初始化字段 field=1; } }
MainClass.java
package mainPackage; public class MainClass { public MainClass() { ExtendingClass s=new ExtendingClass() { @Override public void run() { field=2; System.out.println(field); } }; // 手动调用初始化方法 s.initialize(); System.out.println(s.field); } public static void main(String[] args) { new MainClass(); } }
此时输出会是两次2,符合预期。
内容的提问来源于stack exchange,提问作者NumericPrime
相关产品推荐
相关产品推荐

