多态性,就是不同对象收到相同的消息时,产生不同的行为(即方法)。
在C++中,多态性指用一个名字定义不同的函数,这些函数执行不同但又类似的操作,这样就可以用同一函数名调用不同内容的函数。也就是说,可以用同样的接口访问功能不同的函数,从而实现“一个接口,多种方法”。
在java中,
1、Override 和 Overload 的区分
1)Override
在继承关系中,子类如果定义了一个与父类方法名、参数类型完全相同的方法,则被称为 Override。
子类可以覆写父类的方法,覆写在子类中改变了父类方法的行为。
在方法上加上“@Override”,可以让编译器帮助检查是否进行了正确的覆写。如果没有进行正确的覆写,则编译器会报错。但是“@Override”不是必需的。
2)Overload
如果方法的参数不同,就是Overload,Overload方法是一个 新的方法。
注意:如果方法名相同、方法参数相同,但是方法的返回值不同,也是不同的方法,在Java程序中,出现这种情况,编译器会报错。
2、覆写(Override) Object 方法
所有的 class 最终都继承自 Object,而Object 定义了几个重要的方法:
- toString():将 instance 输出为 String
- equals():判断两个 instance 是否逻辑相等 (在实际项目中覆写过这个)
- hashCode():计算一个 instance 的哈希值
调用 super
在子类的覆写(Override)方法中,如果要调用父类的被覆写的方法,可通过 super 来调用。
3、final
继承可以允许子类覆写父类的方法,如果一个父类不允许子类对它的某方法进行覆写,可以将该方法标记为 final,用 final 修饰的方法不能被 Override。
被 final 修饰的类不能被继承、被 final 修饰的方法不能被重写(Override)、被 final 修饰的字段不能被更改。
4、多态
作用:将不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,抽象出一个通用的逻辑关系,以此来适应不同的业务需求。
针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法。
public void runTwice(Person p){
p.run();
}
上述代码中,传入的参数类型是 Person,但是我们无法知道传入的参数的实际类型是 Person,还是它的子类 Student,又或者是 Person 的其他子类,因此无法确定调用的是不是 Person 类定义的 run() 方法。
所以,多态 的特性就是,运行期才能动态地决定调用的子类方法。
问:这种不确定性的方法调用的作用是什么呢?
答:允许添加更多类型的子类实现功能扩展,却不需要修改基于父类的代码。
参考 廖雪峰——多态 中的代码
编译时多态 和 运行时多态
1)编译时多态:
在编译时就能够确定调用哪个方法。
- 方法重载(Overload)时,都是编译时多态,在编译器可根据参数的数据类型、个数以及次序来确定调用方法。
- 方法重写(Override)时,当 Student son = new Student(); 时,调用 son.name() 时为编译时多态。即,当对象引用本类实例时,为编译时多态;而 Person peo = new Student(); 时,调用 peo.name() 时为运行时动态。
2)运行时多态:
在运行时才能确定调用哪个方法
- 方法重写(Override)时,当 Person val = new Student(); 时,调用 val.name(); 时,为运行时多态,因为此时只有在运行时,才能匹配到对应的方法进行调用。如果 Student类声明了 name() 方法,则执行;否则,执行 Person 类的 name() 方法。
代码如下:
class A {
public String show(D obj) {
return ("A and D");
}
public String show(A obj) {
return ("A and A");
}
}
class B extends A{
public String show(B obj){
return ("B and B");
}
public String show(A obj){
return ("B and A");
}
}
class C extends B{
}
class D extends B{
}
public class Test {
public static void main(String[] args) {
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println("1--" + a1.show(b)); //1--A and A
System.out.println("2--" + a1.show(c)); //2--A and A
System.out.println("3--" + a1.show(d)); //3--A and D
System.out.println("4--" + a2.show(b)); //4--B and A
System.out.println("5--" + a2.show(c)); //5--B and A
System.out.println("6--" + a2.show(d)); //6--A and D
System.out.println("7--" + b.show(b)); //7--B and B
System.out.println("8--" + b.show(c)); //8--B and B
System.out.println("9--" + b.show(d)); //9--A and D
}
}
重点在第四个测试输出。
看到大佬总结的一句话:
父类引用 指向 子类对象,只能调用 父类方法;但是当子类覆写了这个方法,就走子类的方法。
Java重载函数的选择只关注引用类型,不关注内存里面具体的类型,没有运行时多态的产生。