变量多态性中的初始化

假设您有以下代码

class A {
    int i = 4;

    A() { 
        print();
    }

    void print () {
        System.out.println("A");
    }
}

class B extends A {
    int i = 2;              //"this line"

    public static void main(String[] args){
        A a = new B();
        a.print();
    }

    void print () {
        System.out.println(i);
    }
}

这将打印 0 2

现在,如果您删除标有“此行”的行,代码将打印 4 4

  • 我明白,如果没有int i=2;线

A a = new B();将调用类 A,将 i 初始化为 4,调用构造函数,
将控制权交给 中的方法,最后打印 4。print()class B

a.print()将调用类 B 中的方法,因为这些方法将在运行时绑定,运行时还将使用在类 A, 4 中定义的值。print()

(当然,如果我的推理有任何错误,请告诉我)

  • 但是,我不明白的是是否有int i = 2。

为什么如果你插入代码,第一部分(创建对象)会突然打印0而不是4?为什么它没有将变量初始化为 i=4,而是分配默认值?


答案 1

它是Java中几种行为的组合。

  1. 方法重写
  2. 实例变量阴影
  3. 构造函数的顺序

我将简单地介绍一下您的代码中发生的事情,看看您是否理解。

您的代码在概念上如下所示(跳过 main()):

class A {
    int i = 0; // default value

    A() { 
        A::i = 4;  // originally in initialization statement
        print();
    }

    void print () {
        System.out.println("A");
    }
}

class B extends A {
    int i = 0;              // Remember this shadows A::i

    public B() {
        super();
        B::i = 2;
    }

    void print () {
        System.out.println(i);
    }
}

所以,当你在原来的 中调用 时,它正在构造一个 ,对于它,发生这种情况:main()A a = new B();B

  • A::i并且全部为默认值B::i0
  • super(),这意味着 A 的构造函数称为
    • A::i设置为 4
    • print()被调用。由于后期绑定,它必然会B::print()
    • B::print()正在尝试打印出来,这仍然是0B::i
  • B::i设置为 2

然后,当您调用您的 时,它将绑定到正在打印出来的(此时为2)。a.print()main()B::print()B::i

因此,您看到的结果


答案 2

新对象中的所有实例变量(包括在超类中声明的变量)都初始化为其默认值 - JLS 12.5

因此,您的变量将初始化为 0。B 中的构造函数将如下所示:B::i

B() {
    super();
    i = 2;
}

所以当你打电话

A a = new B();

A 中的构造函数将调用 B 中的方法,该方法将打印 B 中的类 B,即 0。printi