【问题标题】:Java: Calling superclass' constructor which calls overridden method which sets a field of subclassJava:调用超类的构造函数,该构造函数调用设置子类字段的覆盖方法
【发布时间】:2012-08-23 21:45:15
【问题描述】:

我有这个演示代码:

class Test2 extends Test {
    public int number = 0;

    @Override
    public void set(){
        number = 1;
        info();
    }

    @Override
    public void info(){
        System.out.println(number);
    }
}

public class Test {
    public Test(){
        set();
    }

    public void set(){
    }

    public void info(){
    }

    public static void main(String[] args){
        Test2 object = new Test2();
        object.info();
    }
}

代码给出了这个输出:

1
0

为什么?我期望这个输出:

1
1

在我看来,main函数调用Test2类的构造函数来创建一个对象。构造函数自动调用超类的构造函数。此构造函数调用被覆盖的方法 set()。因此调用了 Test2 类的方法 set()。此方法设置字段并调用写入数字的 info() 方法。然后主函数再次调用创建对象的info()方法。

数字字段正确设置为第一行输出为“1”。但是为什么第二行包含 0?似乎根本没有设置该字段。能解释一下吗?

我应该怎么做才能得到我期望的行为?提前致谢!

【问题讨论】:

  • 看起来字段初始化器在 Java 中的构造函数之后运行,因此在 set 方法将其设置为 1 后,初始化器将值重置为 0。您应该避免在构造函数中调用虚方法,因为你在一个部分构造的对象中调用行为。

标签: java overriding field super


【解决方案1】:
class Test2 {
    public int number = 0;
}

等价于

class Test2 {
    public int number;

    public Test2() {
        super();
        number = 0;
    }
}

因此,通过调用超级构造函数,字段number 被设置为1。从超级构造函数返回后,您将number 赋值为0。

只需删除分配,它的行为就会如您所愿。

【讨论】:

  • 好吧,我已经知道如何避免这个问题了——先实例化然后初始化。但这很有帮助。
【解决方案2】:

如果说 Animal 扩展了 Dog 而你调用 动物 a = new Dog()

那么步骤顺序如下

  1. Animal 的静态字段被初始化

  2. 动物的静态方块被执行

  3. Dog 的静态字段已初始化

  4. Dog的静态块被执行

  5. Animal 的非静态字段已初始化,它们将是 如果 Animal 改变了它们,则重新初始化>

  6. Animal Constructor 被执行

  7. Dog 的非静态字段已初始化

  8. 狗构造函数被执行

【讨论】:

    【解决方案3】:

    您的代码违反了 Java 中的黄金法则 - 永远不要在构造函数中调用可以被子类覆盖的方法 - 即此类方法应该是私有的。

    到 Test2 中的默认构造函数完成时,它已经覆盖了通过初始化程序 public int number = 0; 分配给它的初始值 1。

    【讨论】:

      猜你喜欢
      • 2013-12-05
      • 1970-01-01
      • 2014-05-22
      • 1970-01-01
      • 1970-01-01
      • 2018-02-28
      • 2021-01-17
      • 1970-01-01
      • 2015-12-18
      相关资源
      最近更新 更多