【问题标题】:Initialization in polymorphism of variables变量多态的初始化
【发布时间】:2016-09-26 02:33:33
【问题描述】:

假设你有以下代码

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,调用构造函数,
这将控制权交给class B 中的print() 方法,最后打印4。

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

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

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

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

【问题讨论】:

  • 当你调用某个类的构造函数时,它的超类的构造函数会在它的构造函数之前被调用,我想你知道的。你知道超类的字段也是在构造函数调用之前初始化的吗?
  • 一个很好的例子,说明为什么从构造函数调用可覆盖的方法是一个坏主意 - 结果充其量是令人困惑的,更常见的是危险(可能泄漏对部分初始化对象的引用)。

标签: java variables constructor initialization polymorphism


【解决方案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::iB::i 都是默认值 0
  • super(),表示调用A的构造函数
    • A::i 设置为 4
    • print() 被调用。由于后期绑定,绑定到B::print()
    • B::print() 正在尝试打印出 B::i,它仍然是 0
  • B::i 设置为 2

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

因此你看到的结果

【讨论】:

    【解决方案2】:

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

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

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

    所以当你打电话时

    A a = new B();
    

    A中的构造函数会调用B中的print方法,会打印出B类中的i,即0。

    【讨论】:

      【解决方案3】:

      在你的B类中,“i”的声明隐藏了A中“i”的声明,并且子类中所有对“i”的引用都指的是B.i而不是A.i。

      所以你在 A.i 中看到的是 java 中任何 int 属性的默认值,它是零。

      Java 实例变量不能在子类中被覆盖。

      您想试试这个以获得更多说明。

      class B extends A {
          int i = 2;              //"this line"
      
          public static void main(String[] args){
              B b = new B();
              A a = b;
              System.out.println("a.i is " + a.i);
          System.out.println("b.i is " + b.i);
          }
      
          void print () {
              System.out.println(i);
          }
      }
      

      输出:

      a.i is 4
      b.i is 2
      

      【讨论】:

      • 感谢@Hulk 指出拼写错误。更新我的答案并更正
      猜你喜欢
      • 2011-08-22
      • 2010-12-22
      • 1970-01-01
      • 2010-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多