文章目录
- 一、理解多态性
- 二、什么是多态性
- 三、多态性的使用——虚拟方法调用
- 四、多态性使用前提
- 五、适用范围
- 六、虚拟方法调用详细介绍
- 多态性是编译期的还是运行期的?
- 举例
- 每日一考
- 七、instanceof关键字的使用
- 引入:关于多态性的讨论
- 向下转型:instanceof
- 练习1
- 练习2
一、理解多态性
多态性:可以理解为一个事物的多种形态
二、什么是多态性
对象的多态性:父类的引用指向子类的对象(子类的对象赋给父类的引用)
//假设父类是Person,子类是Man和Woman,对象的多态性体现为:
Person p1=new Man();
Person p2=new Woman();
三、多态性的使用——虚拟方法调用
多态性的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法。
有了对象的多态性后,在编译期,我们只能调用父类中声明的方法。但在运行期,我们实际执行的是子类重写父类的方法。
总结:编译看左边,执行看右边。
四、多态性使用前提
①有类的继承关系
②一般会有重写(不然不用多态性,因为子类和父类中的方法一样,没有意义)
五、适用范围
多态性只适用于方法,不适用于属性
也就是说,如果子类和父类中有同名的属性,之后有了多态性,在调用这个属性时,最终调用的是父类中的属性。(属性的编译和运行都看左边)即看Person,不是Man
Person p=new Man();
六、虚拟方法调用详细介绍
子类中定义了与父类同名同参数的方法,在多态的情况下,将此时父类的方法称为虚拟方法(因为子类一般会重写父类的方法,所以父类的方法只是书面上存在,即虚拟存在,最终调用的其实是子类重写后的方法)。父类根据赋给它的不同子类对象,动态调用子类的方法,这样的方法调用在编译期是无法确定的。
多态性是编译期的还是运行期的?
运行期的
举例
import java.util.Random;
//面试题:多态是编译时行为还是运行时行为?
//证明如下:
class Animal {
protected void eat() {
System.out.println("animal eat food");
}
}
class Cat extends Animal {
protected void eat() {
System.out.println("cat eat fish");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("Dog eat bone");
}
}
class Sheep extends Animal {
public void eat() {
System.out.println("Sheep eat grass");
}
}
public class InterviewTest {
public static Animal getInstance(int key) {
switch (key) {
case 0:
return new Cat ();
case 1:
return new Dog ();
default:
return new Sheep ();
}
}
public static void main(String[] args) {
int key = new Random().nextInt(3);
System.out.println(key);
Animal animal = getInstance(key);
animal.eat();//这个方法的调用取决于创建的对象是什么类的,不能一下看出来,所以不是编译期的
}
}
每日一考
答案
私有的属性提供的get、set方法,子类中可以通过调用这两个方法对private属性进行调用。private方法可以通过父类中的权限较大的方法调用,然后子类调用这个权限较大的方法。
七、instanceof关键字的使用
引入:关于多态性的讨论
Person p2=new Man();//Person是父类,Man是子类
p2.earnMoney();//如果earnMoney是子类Man特有的方法,这里会出错
p2.isSmoking=true;//如果isSmoking是子类Man特有的属性,这里会出错
【原因】不能调用子类所特有的属性和方法,因为对象p2是Person型的。
有了对象的多态性后,内存中实际上加载的子类对象中特有的属性和方法,但由于变量声明为父类型,导致编译时只能调用父类中声明的属性和方法。
那如何调用子类中特有的属性和方法?
向下转型:使用强制类型转换符。
Man m1=(Man)p2;
m2.earnMoney();//不会出错
m2.isSmoking=true;//不会出错
【注意】但是,强转时可能出现ClassCastException的异常,所以引入关键字instanceof
向下转型:instanceof
为了避免在向下转型过程中出现ClassCastException的异常,我们在向下转型之前先进性instanceof的判断。一旦返回true,就进行向下转型;返回false,不进行。
if (p2 instanceof Woman){
Woman w1=(Woman)p2;
w1.shopping();
}
if (p2 instanceof Man){
Man m2=(Man)p2;
w2.earnMoney();
}
如果B是A的父类,a instanceof A返回true,则a instanceof B也返回true。反之不一定。
但是由于在前面有Person p2=new Man(); 也就是new的就是Man的对象,那在instanceof判断时一定是true。
练习1
声明的子、父类:
问题(输出的值):
答案:
1、20(此时还不涉及多态性,因为创建的是sub对象且赋给的也是sub类型变量,属性就是sub的,如果sub和base中定义的属性不同名,输出的是sub.base的属性名,那么this会先在sub定义的属性中找,没有;然后在去父类定义的属性找,输出的是父类的属性值)
2、20()
3、true,因为==对于引用数据类型来讲,比较的是两个引用数据类型的地址值是否相同
4、10,涉及到多态性,编译看左边
5、20,涉及到多态性,编译看左边,运行看右边
【提示】在真正编译时,子、父类中不要出现同名的属性,容易混
练习2
1、问题:输出的结果?
2、
已有的类型定义:
答案:
1、sub_1,因为sub1中的int[] arr和base1中的int…arr是一样的,所以sub1和base1中的方法构成了重写。多态性赋给的父类变量是base1类型,所以调用的是base1中的方法add,而sub1中能与base1中的方法add构成重写的只有int[] arr,而不是int b,int c,所以不会调用sub1中的第二个方法。
2、sub_2,强制向下转型后,是调用sub1中的方法,而sub1中能和add(1,2,3)严丝合缝的是add(int a,int b,int c)这个方法