对象的多态性
多态性在面向对象是一个最重要的概念,在Java中面向对象主要有以下两种主要体现:
- 方法的重载和覆写
对象的多态性
关于方法的重载和覆写的区别
区别点 | 重载 | 覆写 |
---|---|---|
单词 | Overloading | Overriding |
定义 | 方法名称相同,参数和类型或个数不同 | 方法名称、参数的类型、返回值类型全部相同 |
对权限没有要求 | 被覆写的方法不能拥有更严格的权限 | |
范围 | 发生在一个类中 | 发生在继承类中 |
对象的多态性
对象的多态性只要分为以下两种类型
对象向上转型:父类 父类对象= 子类实例
对象向下转型:子类 子类对象 =(子类)父类实例
那为什么要进行对象的转型呢?
通过下面两个例子来看看
class A{
public void fun1(){
System.out.println("A————>public void fun1(){}");
}
public void fun2(){
this.fun1();
}
}
class B extends A{
public void fun1(){
System.out.println("B————>public void fun1(){}");
}
public void fun3(){
System.out.println("B————>public void fun3(){}");
}
}
public class Demo01 {
public static void main(String[] args) {
A a = new B(); //向上转型 子类——>父类
a.fun1();
}
}
/*
运行结果:
B————>public void fun1(){}
*/
上面的程序使用了对象的向上转型,虽然使用了 父类调用了fun1方法,但是调用的是被子类覆写的方法。
也就是说,如果对象发生了向上转型关系后,所调用的方法一点是被子类覆写过的方法。
那么,如果我们要调用子类自己的定义的方法fun3(),就无法通过对象a来调用。
如果想要调用子类自己的方法,则肯定要使用子类的实例,所有此时可以将对象进行向下转型。
class A{
public void fun1(){
System.out.println("A————>public void fun1(){}");
}
public void fun2(){
this.fun1();
}
}
class B extends A{
public void fun1(){
System.out.println("B————>public void fun1(){}");
}
public void fun3(){
System.out.println("B————>public void fun3(){}");
}
}
public class Demo02 {
public static void main(String[] args) {
A a = new B();
B b = (B)a;//向上转型,首先要进行乡下转型
b.fun1();
b.fun2();
b.fun3();
}
}
/*
B————>public void fun1(){}
B————>public void fun1(){}
B————>public void fun3(){}
*/
从以上的程序可以发现,如果要调用子类自己的方法,则一定要用子类声明对象,另外,在子类调用父类的fun2方法,fun2方法要调用fun1方法,但此时的fun1方法已经被子类覆写,则调用的是被子类覆写的方法。
现在我们清晰了重写和重载还有对象多态性的概念了,下面我们来看一道非常经典的题目来加深我们的理解。
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 Demo {
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(a1.show(b)); //1
System.out.println(a1.show(c)); //2
System.out.println(a1.show(d)); //3
System.out.println(a2.show(b)); //4
System.out.println(a2.show(c)); //5
System.out.println(a2.show(d)); //5
System.out.println(b.show(b)); //6
System.out.println(b.show(c)); //7
System.out.println(b.show(d)); //8
}
}
/*
运行结果:
A and A
A and A
A and D
B and A
B and A
A and D
B and B
B and B
A and D
*/
当B继承A之后,实际上B中有3个方法
public String show(B obj){
return (“B and B”);
}
public String show(A obj){
return (“B and A”);
}
public String show(D obj){
return (“A and D”);
}
先分析一下A类中的方法,show(D obj),show(A obj),这两个方法在内部达成了重构,重载后,会优先选择最适合的方法,没有最合适的方法会调用能使用的方法。
先看 A a1 = new A()
第一个 a1.show(b) 调用A类中的方法,内部构成重载,找不到最合适的方法,所以会调用show(A obj) 因为 b是B的对象,而B是A的子类,所以可以传入输出 A and A
第二个a1.show(c) 同上 输出A and A
第三个a1.show(d) 重载会优先选择最适合的方法,就会调用show(D obj),输出A and D
再分析一下B类中的方法
里面有这两个方法 ,show(A obj)覆写了A类的中的show(A obj),内部重载
分析完了B类
再看 A a2 = new B() 实现了对象的向上转型
相当于a2中有A类中show(D obj) 和被B类覆写的方法
public String show(D obj){
return (“A and D”);
}
public String show(A obj){
return (“B and A”);
}
a2.show(b)
a2是A的实例对象,只能调用A中的方法,无法调用B类中的私有方法。内部重构,调用show(A obj),由于实现了对象的向上转型,而且show(A obj)被覆写,会调用子类覆写的方法,输出B and A
a2.show(c) 同上,输出B and A
a2.show(d) 调用 show(D obj) ,输出 A and D
B b = new B();
b.show(b) 这个比较简单, 输出B and B
b.show(c) 重构,优先原则 输出 B and B
b.show(d) 因为B是A的子类,就直接调用父类的的show(D obj) 输出 A and D
个人理解,有误解的地方,欢迎在底下给我留言。