【问题标题】:Inheritance, Polymorphism Questions继承、多态性问题
【发布时间】:2014-12-13 02:51:09
【问题描述】:

我目前正在学习我的第一个 Java 课程,并且我们最近讨论了多态性和继承的主题。为了更好地理解事情的工作原理,我尝试了一些事情,我想知道是否有人可以帮助我理解为什么下面的代码给了我输出以及当我们结合继承和覆盖方法时可能出现的问题.

public class AClass {
public void m1() {
    System.out.println("A.m1()");
    this.m2();
}
public void m2() {
    System.out.println("A.m2()");
}

public static void main(String [] args) {
AClass A = new AClass();
BClass B = new BClass();

A.m1();
A.m2();
B.m1();

public class BClass extends AClass {

@Override
public void m2() {

    // TODO Auto-generated method stub
    System.out.println("B.m2()");
}

}

我得到的输出是:

A.m1()
A.m2()
A.m2()
A.m1()
B.m2()

另外,如果我在 B 类的 m2() 方法中添加 super.m1() 调用,我会收到堆栈溢出错误。有人能告诉我为什么会这样吗?谢谢!

【问题讨论】:

  • m2 in BClass 调用m1 in AClass,后者调用m2,以此类推(无限递归)。
  • 感谢您的回复,如果您不介意可以一步一步进行,因为我无法理解为什么我们会有无限递归,为什么会继续跑?
  • 调试它并逐步执行代码。

标签: java inheritance polymorphism overriding


【解决方案1】:

另外,如果我在 B 类的 m2() 方法中添加 super.m1() 调用,我会得到 堆栈溢出错误。谁能告诉我为什么会这样?

函数调用在内存中相互堆叠。在您的代码中,

public class AClass {
public void m1() {
    System.out.println("A.m1()");
    this.m2();
}
public void m2() {
    System.out.println("A.m2()");
}

public static void main(String [] args) {
    AClass A = new AClass();
    BClass B = new BClass();

    B.m1();
}

public class BClass extends AClass {

@Override
public void m2() {

    // TODO Auto-generated method stub
    System.out.println("B.m2()");
    super.m1();
}

}

您的代码导致一个永无止境的序列

  1. main 在堆栈的底部。
  2. main 中的语句B.m1() 调用m1(),后者通过main 压入堆栈。
  3. m1() 的主体中的语句this.m2() 调用了被覆盖的m2(),它通过m1() 压入堆栈。
  4. 被覆盖的m2() 的主体中的语句super.m1() 调用m1(),后者通过m2() 压入堆栈。
  5. 返回步骤 3

函数调用一个接一个地被压入堆栈,没有一个被弹出。这会导致堆栈溢出。

【讨论】:

    【解决方案2】:

    无限递归可以这样解释:

    无限递归是由调用 B.m1() 引起的。 BClass 类扩展了 AClass,因此 BClass 继承了 AClass 的两个公共方法,即方法 m1 和 m2。所以方法调用 B.m1() 调用 BClass 的方法 m1,它继承自 classA。在 m1() 内部调用 this.m2() 实际上等价于 B.m2()。因此它从 BClass 调用被覆盖的方法 m2(),因为被覆盖的方法比原来的方法具有更高的优先级在超类中实现。但是当调用 BClass 中的 m2 时,它又通过 super.m1() 再次调用 AClass 的方法 m1() 并再次调用通过 this.m2() 调用 BClass 的重写方法。这会导致无限递归。

    【讨论】:

      【解决方案3】:

      你的情况很简单。当您覆盖父类的方法时,您替换其实现为子类中的@Override 方法。例如,当您调用B.m1() 时,java 会监视Bclass 中的m1 方法,如果没有找到,则使用与Aclass 具有相同签名的方法。然后它在Bclass 中监视m2() 方法,找到并调用它。

      多态更复杂。它允许在不了解这些确切类的情况下从子类调用相同的方法。我已经覆盖了来自related wikipedia 页面的一些代码:

      abstract class Animal {
          abstract String talk();
      }
      
      class Cat extends Animal {
          @Override
          String talk() {
              return "Meow!";
          }
      }
      
      class Dog extends Animal {
          @Override
          String talk() {
              return "Woof!";
          }
      }
      
      void main() {
          Animal[] animals = new Animal[size];
          //then goes creation
          for (Animal a : animals) {
              a.talk();
          }
      }
      

      【讨论】:

      • 谢谢!这是有道理的,但是我仍然不完全确定程序崩溃的原因,我不明白为什么我们会陷入各种无限循环(当我添加 super.m1() 方法时)
      • 当您将super.m1() 添加到B.m2() 方法的开头时,代码会这样:B.m1() -> A.m1() -> B.m1() -> A.m1() 一次又一次.
      • 更详细:当你调用 B.m1() 时,A.m1() 中的代码将被执行,因为虽然 B 类扩展了 A 类,但它不会覆盖 m1。问题是 A.m1() 调用 m2() (在打印“A.m1()”之后)它在 B 中有自己的实现,所以它是被调用的那个。但是如果你把 super.m1() 放在那里,A.m1() 会再次被调用,“A.m1()”会被再次打印,B.m2() 会被再次调用,以此类推。明白了吗?
      【解决方案4】:

      A.m1() -->打印“A.m1()”,因为您从主方法中调用此方法。

      A.m1() 方法也在其中调用 this.m2() 。这将进入方法 m2() 并打印 -->"A.m2()"

      **注意 this.m2() 和调用 m2() 是一样的。

      A.m2() -->打印“A.m2()”,因为您是从 main 调用此方法。

      现在是稍微棘手的部分。

      B.m1(),如你所见,BClass 没有 m1() 方法,但它继承自 AClass。通过从 AClass 继承,BClass(内部)拥有 AClass 的所有公共和受保护方法。这意味着 BClass 现在有一个 m1() 方法(与 AClass 相同)。因此,调用 B.m1() 将打印 AClass m1() --> "A.m1()"。

      现在请记住,A.m1() 也在其中调用 this.m2()。但是由于 BClass 也有一个 m2() 方法,Java 将使用该方法,这将最终打印 --> "B.m2()"。

      【讨论】:

        猜你喜欢
        • 2015-07-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-28
        相关资源
        最近更新 更多