面向对象的特征之三:多态性

一、理解多态性

多态性:可以理解为一个事物的多种形态

二、什么是多态性

对象的多态性:父类的引用指向子类的对象(子类的对象赋给父类的引用)

//假设父类是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)这个方法

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值