面向对象三大特性之一:多态—java语言
多态的形式
用父类类型的变量 接受子类的对象 便是多态的形式
父类类型 变量名 = new 子类/实现类构造器;
变量名.方法名();
此时不同的子类,虽然是相同的方法,但会有着不同的行为。
为什么要有多态
假设有一个下列的方法。
假设这是一个学生管理系统的注册方法,其中函数的参数 是一个 学生变量。
但是学生管理系统 不只是 学生可以注册,老师和管理员也可以。
所以我们就还需要写一个 老师的注册方法,和管理员的注册方法。
但是这样就会很麻烦,所以这时就可以使用多态。
我们可以换成他们的父类类型来接收(假设刚才的类都继承于Person类)
这样就只需要写一个方法就可以了。
多态的定义和前提
多态的定义:是指同一行为,具有多个不同表现形式。
多态的前提:
- 有继承或实现关系(接口那里会讲到实现)
- 需要重写父类的方法(不重写会没有意义)
- 需要用父类的引用接收 子类的对象。(格式体现)
多态的运行特点
我们来举个例子来理解多态。
现在有三个类,Cat类、Dog类、Bird类。
Cat类:属性:姓名、年龄。行为:吃饭、抓老鼠。
Dog类:属性:姓名、年龄。行为:吃饭、看家。
Bird类:属性:姓名、年龄。行为:吃饭、飞行。
先利用继承,将共性内容提取到Animal类当中。
接着三个子类都重写 父类的 eat 方法。
多态的运行特点是:
- 在调用成员变量时,是编译看左边,运行看左边。
- 在调用成员方法时,是编译看左边,运行看右边。
首先这里的左边和右边指的是,等号的左边和右边 ,也就是指的是 父类和子类。
在调用成员变量时,编译看左边,就是看父类当中有没有这个变量,如果没有,则编译失败;运行看左边,是指 最后的值是由父类的值确定的。
所以最终会打印 10,并且这个值是根据父类的值进行确定的。
在调用方法时,编译看左边是指,先看父类里面有没有 该方法,运行看右边是指,调用的是子类的方法。
首先父类是有 eat 方法的,但是父类的 eat 方法的打印可不是这样的。
父类里面的eat方法是 正在吃饭,所以可见,我们调用的是 子类的 eat方法。
多态的弊端
但是 在这个例子中,如果我想调用 Cat 的 抓老鼠的方法,就会发现调用不了。
这是因为,无论调用的是变量还是方法,都是编译看左边,所以调用的父类都必须 定义,然而 抓老鼠这个方法是 子类Cat的特有的 方法,所以会编译失败。
所以这也是多态的一个弊端,无法调用子类的特有方法。
引用类型转换( 向上转型和向下转型 )
为了解决多态的弊端,于是就出现了 引用类型的转化。
向上转型是 在子类对象 赋值给 父类类型变量的过程中 自动转换的。
而向下转型是将 父类变量 强制转换成子类对象。
也就是说,因为父类里面没有子类的特有方法,那么我就直接将你转化成 子类的类型,也就是 向下转型的意思。
而强制类型转换跟我们的基本类型当时的强转一样。
这时就可以调用我们的子类特有的 方法了。
这里 想法多的同学可能会有这个想法,我能不能将这个Animal类型的变量强转成 别的子类呢,比如Dog类?
我们来实验一下。
我们把 a1强转成了 Dog类,会发现 idea 居然可以 点 出来,但是当我们写下并运行时。
会发现程序会抛出一个异常,类转换异常,所以这样是不可行的。
也就是说,对于向下转型来说,刚开始创建的 是什么类型的对象,只能 向下转型为 对应的类型。
那么对于下面的方法来说,我怎么知道传的是Cat、Dog还是 Bird呢?
为了解决这一问题,就需要用到 instanceof 关键字。
instanceof 关键字
用法如下: instanceof 可以 判断一个对象是否是特定类型(包含该类型及其子类)
这里代码的意思就是,如果 对象p 的类型 是Cat类 或 是Cat 的子类,那么就将 变量a 强转为Cat 类型,此时就可以调用 Cat类的特有方法了。
我们再补全一下,其他类的判断。
此时 根据传进来的对象不同,就会调用该类的特有方法。
instanceof 新特性
在JDK14的时候,Instanceof 出了一个新特性。
可以将判断和赋值写在一块。
这里的含义就是如果 是 该类型或其子类,那么就直接创建该 类型变量,并且赋值给该变量。
完