除了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
类的方法。在这里,我使用了一个适合每个重写和重载(同时)方法的参数。为什么klass对象总是从MyClass
类调用该方法?
这很正常。毫无疑问。
5.抛出异常是对的。 klass对象没有带双参数的方法。但是,为什么它不被称为MyClass2
类的方法,就像它发生在问题1?
1. klass对象是MyClass的实例。
不,它是MyClass
类型的引用变量,但是指的是MyClass2.
的对象
2.为什么它从MyClass2类执行方法?
由于你在say()
的一个对象上调用MyClass2
,它执行say()
的MyClass2
。预期的行为。
这在Java中称为运行时多态。这提供了覆盖类层次结构树中已有的功能的能力。在运行时,将调用哪个版本的方法是基于存储在该引用变量中的实际对象的类型,而不是基于引用变量的类型。
3.在问题1中,klass调用Class2类的方法。在这里,我使用了一个适合每个重写和重载(同时)方法的参数。为什么klass对象总是从MyClass类调用该方法?
这不是重写方法。具有相同签名(名称,加上其参数的数量和类型)的子类中的实例方法和作为超类中的实例方法的返回类型会覆盖超类的方法。重写方法也可以返回返回类型的子类型通过重写的方法。这称为协变返回类型。您的方法签名是不同的。因此调用klass.print()
,其中klass
是MyClass
参考将始终指print()
的MyClass
。
4.抛出异常是对的。 klass对象没有带双参数的方法。但是,为什么它不是从MyClass2类调用的方法,就像在问题1中发生的那样?
因为在编译时,编译器会验证您是否可以基于引用类型调用方法。这里的引用类型是MyClass
,因为MyClass
没有定义anotherPrint(double)
,编译器抱怨。它是一个编译时检查。在问题1中,编译器验证了klass.say("hi")
并且它看到MyClass
中存在一个可以这种方式调用的方法。那时,并不关心klass
引用变量是否会在运行时引用MyClass
对象或MyClass2
对象。因此它奏效了。
您可以参考这些Oracle的教程here。
类的实例将具有父(s)的方法以及它自己的方法。如果其中一个子类的签名与父方法中的方法签名相同,则会覆盖它。
在此示例中,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是MyClass,因此可以使用MyClass2对象作为参数
5 - MyClass没有任何名为anotherPrint的方法接收double
问题1: - 使用MyClass klass = new MyClass2();你已经完成了向上转换,捕获了子类的引用id(对象)。在向上转换的情况下,调用子类的方法。虽然编译器在comiple时检查parent中的say()函数,但是在运行时编译器看到我们在child中也有say()函数,所以它将它与子类方法绑定并调用它。这就是你将问题1的输出作为Hello MyClass2 h的原因
Java中的方法重载和方法重写是Java中的两个重要概念,它允许Java程序员声明具有相同名称但行为不同的方法。方法重载和方法重写基于Java中的多态。在方法重载的情况下,具有相同名称的方法共存于同一个类中但它们必须具有不同的方法签名,而在方法重写的情况下,在派生类或子类中声明具有相同名称的方法。方法使用静态解决重载在编译时在Java中绑定,而在运行时使用Java中的动态绑定解析方法重写。简而言之,当您在Java中重载方法时,其方法签名已更改,而在重写方法签名保持相同但方法只能在子类中重写时。由于Java支持多态性并在运行时解析对象,因此它能够在Java中调用重写方法
阅读更多:http://javarevisited.blogspot.com/2011/12/method-overloading-vs-method-overriding.html#ixzz34EEhLp2u