一、概述
我之前上几期是教学了java类、及面向对象编程的三大基本特性,封装继承和多态。前一期,我是把继承大致讲了一遍,不知道你们对它有没有理解,何为继承,继承有啥好处,可以多继承嘛?等这类问题,我在上一期内容都有讲解,如果你们答不出来,没关系,你们可以再回去瞅瞅。
而这一期,我们要来学习一个新概念--多态,是继封装、继承之后,面向对象的第三大特性。好的,废话不多说,咱这就开始。
二、本期教学目标
- 何为多态?
- 实现多态的前提条件?
- 如何实现多态?
- 多态有何优劣?
- 什么是向上转型和向下转型?
- 多态实例演示
- ...
三、正文
1️⃣概述
说起多态,你们从字面意思上肯定无法理解,没关系,我给大家讲个与生活息息相关的例子,比如啊,对于吃饭这个动作,人是用手用筷子,鸡是嘴琢,鱼是用嘴吸,对于跑这个动作,人跑、鸟跑、猫跑、鸵鸟跑等都是不一样的,对于飞这个动作,飞机、鸟、大雁、飞鱼等展示形式也是不一样的。虽然行为都是一样,但通过不同的事物,对于行为的具体表现都是不一样的,这就是多态。多态,形容的就是这种对于同一行为却表现出多种形态的现象。
2️⃣定义
多态:准确来说,是指对同一行为,对于不同的对象有具体表现不同形式。而对于对象多态,表示对同一方法,不同对象具有不同的实现方法形式。
3️⃣前提条件
要实现多态,那它有那些前提呢?
- 继承或者实现。
- 实现方法重写。
- 父类引用指向子类对象或者接口引用指向实现类对象。
4️⃣多态优劣
那对于多态而言,有何优势又有何劣势呢?这点我分开给大家逐一讲解一下。
- 优势:在实际开发过程中,父类类型作为方法形式参数,传递给子类方法,进行方法调用,更能体现出多态的可拓展性与便利性
- 劣势:多态情况下,只能调用父类的共性内容,不能调用子类特有的内容(方法、属性等)
5️⃣实现多态
如下实现多态?多态往往体现在父类的引用指向它的子类,比如:
父类类型 变量名 = new 子类对象;
变量名.方法名();
6️⃣向上转型
为何要说到这个概念呢?这也是为了弥补因为多态这个特点而导致用父类变量接收子类对象之后,就不能调用子类所特有的方法了(父类没有)这个小问题,所以就需要向上转型,当左边变量类型大于右边对象变量类型,将该过程就成为向上转型,比如:
// 向上转型
Animal dog = new Dog();
此时,dog对象按编译时按照左边变量类型(Animal)处理,就只能调用父类(Animal)中有的变量和方法,而不能调用子类特有的变量和方法了。
大家请看:
public class Dog extends Animal{//子类
private String active;
@Override
public void eat(){
System.out.println("我是狗狗,吃骨头!");
}
public void run(){
System.out.println("我是狗狗,我会跑!");
}
public static void main(String[] args) {
Animal dog = new Dog();
dog.eat();
}
}
dog对象是无法调用自身的run()方法和自定义的属性。
7️⃣向下转型
所以对于有向上转型,就有向下转型,相反,向下转型就是当左边变量类型小于右边变量类型,我们就称为向下转型。此刻,编译期间,按照左边变量类型处理,就可以调用子类特有的方法和属性了。比如:
public static void main(String[] args) {
Animal dog1 = new Dog();
dog1.eat();
if (dog1 instanceof Dog){
Dog dog2 = (Dog)dog1; //向下转型
dog2.run(); // 调用的是Dog的run()方法
}
}
解释一下:为了避免ClassCastException异常,Java有专门提供 instanceof 关键字,作用就是给引用变量做类型校验,只要用instanceof判断如果返回true,那么强转为该类型就一定是安全的,借此也不会报ClassCastException异常。
四、实例演示
接下来我给大家举个例子,重点演示一下。
先来定义一个父类。
@Data
public class Animal {//父类
private String type;
private String name;
private String color;
public void eat() {
System.out.println("我是父类,吃东西!");
}
}
然后再来定义个子类Dog。
@Data
public class Dog extends Animal {//子类
private String active;
@Override
public void eat() {
System.out.println("我是狗狗,吃骨头!");
}
//独有功能
public void lockHome() {
System.out.println("我是狗狗,我会看家");
}
}
大家可以看到,dog2对象可以调用Dog类的lockHome()方法,该dog1是无法调用的,但dog2可以调用,这就是向下转型所致。
总结:
- 成员变量:只看编译时类型, 成员变量没有重写。访问的是父类的成员变量。
- 非虚方法:只看编译时类型,也就是访问的是父类中的方法。