3

除了 Java 继承是该语言的一个基本特性之外,我还有一些问题。
这是我的测试示例的来源:

class MyClass{

    public void say(String t){
        System.out.println("Hello MyClass "+t);
    }

    public void print(MyClass t){
        System.out.println("MyClass is printed");
    }

    public void anotherPrint(int i){
        System.out.println("MyClass is printed again");
    }
}

class MyClass2 extends MyClass{

    public void say(String t){
        System.out.println("Hello MyClass2 "+t);
    }

    public void print(MyClass2 t){
        System.out.println("MyClass2 is printed");
    }

    public void anotherPrint(double i){
        System.out.println("MyClass2 is printed again");
    }
}

public class HelloWorld{

    public static void main(String []args){
        MyClass klass = new MyClass2();

        klass.say("h"); //Question 1 (Prints: "Hello MyClass2 h")

        klass.print(new MyClass2()); //Question 2 (Prints: "MyClass is printed")
        klass.print(new MyClass()); //Question 3 (Prints: "MyClass is printed")

        klass.anotherPrint(1); //Question 4 (Prints: "MyClass is printed again")
        klass.anotherPrint(1.0); //Question 5 (Throws Exception!)
    }
}

我有以下问题:

1. klass 对象是MyClass. 为什么它执行MyClass2类中的方法?

2,3 。在问题 1 中,klass 调用了MyClass2该类的方法。在这里,我使用了一个适合每个覆盖和重载(同时)方法的参数。为什么类对象总是从MyClass类中调用方法?

4. 这是正常的。完全没有问题。

5. 抛出异常是对的。klass 对象没有这种带双参数的方法。但是,为什么不从MyClass2类中调用方法,就像在问题 1 中发生的那样?

4

4 回答 4

8

1. 类对象是MyClass 的实例。

不,它是类型的引用变量,MyClass但引用的对象MyClass2.

2. 为什么会执行 MyClass2 类的方法?

由于您正在调用say()of 的对象MyClass2,因此它执行say()of MyClass2。预期的行为。

这在 Java中称为运行时多态性。这提供了覆盖类层次结构树中已有功能的能力。在运行时,将调用哪个版本的方法取决于存储在该引用变量中的实际对象的类型,而不是引用变量的类型。

3.在问题1中,klass调用Class2类的方法。在这里,我使用了一个适合每个覆盖和重载(同时)方法的参数。为什么 klass 对象总是调用 MyClass 类的方法?

那不是被覆盖的方法。子类中具有相同签名(名称,加上其参数的编号和类型)和返回类型的实例方法作为超类中的实例方法覆盖超类的方法。覆盖方法也可以返回返回类型的子类型通过覆盖的方法。这称为协变返回类型。您的方法签名不同。因此,调用klass.print()where klassis a MyClassreference 将始终引用print()of MyClass

4. 抛出异常是对的。klass 对象没有这种带双参数的方法。但是,为什么不调用 MyClass2 类的方法,就像在问题 1 中发生的那样?

因为在编译时,编译器会根据引用类型验证您是否可以调用方法。这里引用类型是MyClass,由于MyClass没有定义anotherPrint(double),编译器抱怨。这是编译时检查。在问题 1 中,编译器验证klass.say("hi")并看到存在MyClass可以以这种方式调用的方法。那时,并不关心klass引用变量是在运行时引用一个MyClass对象还是MyClass2对象。因此它奏效了。

您可以在此处参考这些 Oracle 的教程。

于 2013-05-15T16:47:12.867 回答
2

一个类的实例将具有父类的方法以及它自己的方法。如果其中一个子类的签名与父类中的某个方法的签名相同,则将覆盖它。

在此示例中,MyClass2 的实例将具有以下方法:

public void say(String t) //From MyClass2 (override)
public void print(MyClass2 t)
public void anotherPrint(double i)
public void print(MyClass t) //Inherited from MyClass
public void anotherPrint(int i) //Inherited from MyClass

但是您将 klass 声明为 MyClass,因此它将提供此方法

public void say(String t) //From MyClass
public void print(MyClass t)
public void anotherPrint(int i)

现在回答你的问题

1 - 您调用 MyClass 的方法。但是在运行时,实际上 klass 是 MyClass2 的一个对象,而 MyClass2 覆盖了这个方法,所以它会调用来自 MyClass2 的那个

2,3 - 您调用 MyClass 的方法 print。在运行时,klass 来自 MyClass2,但 MyClass2 不会覆盖此方法。签名不一样。所以要调用的是MyClass。使用 MyClass2 对象作为参数可以正常工作,因为 MyClass2 是 MyClass

5 - MyClass 没有任何名为 anotherPrint 的方法接收双精度

于 2013-05-15T17:07:30.413 回答
0

Java中的方法重载和方法覆盖是Java中的两个重要概念,它允许Java程序员声明具有相同名称但行为不同的方法。方法重载和方法覆盖基于 Java 中的多态性。在方法重载的情况下,同名的方法在同一个类中共存,但它们必须具有不同的方法签名,而在方法覆盖的情况下,在派生类或子类中声明同名的方法。方法重载使用静态解决在编译时在 Java 中绑定,而在运行时使用 Java 中的动态绑定来解决方法覆盖。简而言之,当您在 Java 中重载一个方法时,它的方法签名发生了变化,而在覆盖方法签名的情况下保持不变,但一个方法只能在子类中被覆盖。

阅读更多:http: //javarevisited.blogspot.com/2011/12/method-overloading-vs-method-overriding.html#ixzz34EEhLp2u

于 2014-06-10T09:55:15.030 回答
0

问题 1:- 使用 MyClass klass = new MyClass2(); 你已经完成了向上转换,捕获了子类的引用 id(object)。在向上转换的情况下,将调用子类的方法。虽然编译器在编译时检查父类中的 say() 函数,但在运行时编译器看到我们在子类中也有 say() 函数,因此它将它与子类方法绑定并调用它。这就是您将问题 1 输出为 Hello MyClass2 h 的原因

于 2013-05-15T16:57:59.550 回答