一、面向对象三大特征之三:多态
1、多态是什么?
- 同类型的对象,执行同一个行为,会表现出不同的行为特征。
- 比如:猫和狗都是动物类型,执行同一个行为,但是会表现出不同的行为特征;
2、多态的常见形式
父类类型 对象名称 = new 子类构造器;
接口 对象名称 = new 实现类构造器;
package com.app.d1_polymorphism;
/**
动物类:父类——抽象类
*/
public abstract class Animal {
public abstract void run();
}
package com.app.d1_polymorphism;
/**
狗类:子类,继承动物类
*/
public class Dog extends Animal{
@Override
public void run() {
System.out.println("🐕跑得很快~~");
}
}
package com.app.d1_polymorphism;
/**
乌龟类:子类,继承动物类
*/
public class Tortoise extends Animal{
@Override
public void run() {
System.out.println("🐢跑不起来!");
}
}
package com.app.d1_polymorphism;
/**
目标:认识多态,理解多态的形式和概念
*/
public class PolymorphismTest {
public static void main(String[] args) {
// 以前的形式
Dog d1 = new Dog();
d1.run();
Tortoise t1 = new Tortoise();
t1.run();
System.out.println("---------------------------------");
// 1、多态的形式:父类类型 对象名称 = new 子类构造器;
// 其实多态的这个形式就是:右边的狗对象 赋值给 左边的动物对象
// 狗对象只代表一种动物;动物对象代表所有的动物。属于小范围的 赋值给 大范围的,合情合理!
Animal d2 = new Dog();
d2.run();
Tortoise t2 = new Tortoise();
t2.run();
}
}
🐕跑得很快~~
🐢跑不起来!
---------------------------------
🐕跑得很快~~
🐢跑不起来!
Process finished with exit code 0
3、多态中成员访问特点
- 方法调用:编译看左边,运行看右边;
- 变量调用:编译看左边,运行也看左边。 (多态侧重行为多态)
4、多态的前提
- 有继承/实现关系;有父类引用指向子类对象;有方法重写。
二、多态的优势
1、优势
(1)在多态形式下,右边对象可以实现解耦合,便于扩展和维护;
-
没换掉之前
-
换掉之后,run行为也会随着对象而变,不需要改动代码
-
解耦合: 什么意思呢?
- 举例说明:
- 比如你家孩子买了一个变形金刚的玩具,玩着玩着没电了,这时候只需要换电池,就可以继续玩;
- 再比如你买了一辆电动车,哪天轮胎爆了,这时候只需要去维修店换轮胎,是不是就可以继续开了。
- 举例说明:
(2)定义方法的时候,使用父类型作为参数,该方法就可以接收这个父类的一切子类对象,体现出多态的扩展与便利;
-
所有动物参加赛跑比赛
-
执行原理:
2、多态下产生的问题
-
多态下不能使用子类的独有功能;
-
因为父类是被继承的,因此继承了父类的子类会有父类的一切行为,但是父类是没有子类独有行为的。
-
三、多态下引用类型的类型转换
1、引用类型是什么?
- 也就是我们自己定义的:动物类、学生类、猫类、老师类等等。
2、引用类型的类型转换
-
自动类型转换(从子到父):子类对象赋值给父类类型的变量指向。
- 因为父类范围大,所有子类对象赋值给父类类型的变量指向—>属于:小范围 赋值给 大范围,因此是自动类型转换。
-
强制类型转换(从父到子):
-
此时必须进行强制类型转换:
子类 对象变量 = (子类)父类类型的变量;
-
作用:可以解决多态下的劣势,可以实现调用子类的独有行为(方法功能)。
-
注意:如果 转型后的类型 和对象真实类型 不是同一种类型,那么在转换的时候就会出现 ClassCastException;
-
3、instanceof关键字
-
Java建议多态下的引用类型在强制类型转换的前,使用
instanceof
关键字判断当前对象的真实类型,再进行强制类型转换。变量名 instanceof 真实类型 判断 instanceof 左边的变量指向的对象的真实类型,是否与右边的类型或者与其子类类型一样,是则返回true,反之。
4、知识总结
1、引用类型的类型转换,有几种方式?
- 有自动类型转换、强制类型转换。
2、强制类型转换能解决什么问题?需要注意什么?
- 可以解决多态下不能访问子类独有功能的问题,强制转换后可以访问。
- 有继承或者实现关系的两个类型就可以进行强制转换,编译无问题;
- 运行时,如果发现强制转换后的类型不是对象的真实类型则会报错;
- 注意:java建议强制转换前使用
instanceof
关键字判断一下当前对象的真实类型,再进行强制类型转换。
四、多态综合案例
1、案例一
此案例,无实际含义,目的是为了通过此案例理解之前所学的语法:
-
需求:
-
使用面向对象编程模拟:设计一个电脑对象,可以安装2个USB设备;
-
鼠标:被安装时可以完成接入、调用点击、拔出功能;
-
键盘:被安装时可以完成接入、调用打字、拔出功能。
-
-
分析实现:
-
1、定义一个USB的接口:(申明USB设备的规范必须是:可以接入、拔出);
-
2、定义2个USB实现类代表鼠标、键盘,让其实现USB接口,并分别定义它们自己独有的功能;
-
3、创建电脑对象,创建2个USB实现类对象,分别安装到电脑中并触发功能的执行。
-
USB接口
package com.app.d4_polymorphism_test; /** 1、定义一个USB的接口:(申明USB设备的规范必须是:可以接入、拔出); */ public interface USB { void access(); // 接入 void pullOut(); // 拔出 }
-
键盘实现类
package com.app.d4_polymorphism_test; /** 2、定义USB实现类代表键盘,让其实现USB接口 */ public class KeyBoard implements USB{ /** b、定义键盘名称:商标 */ private String keyBoardName; /** c、提供无参、有参构造器 */ public KeyBoard() { } public KeyBoard(String keyBoardName) { this.keyBoardName = keyBoardName; } /** d、提供变量对应的getter、setter方法,暴露其取值和赋值 */ public String getKeyBoardName() { return keyBoardName; } public void setKeyBoardName(String keyBoardName) { this.keyBoardName = keyBoardName; } /** a、实现USB接口的规范:接入、拔出 */ @Override public void access() { System.out.println(keyBoardName + "已成功连接电脑~"); } @Override public void pullOut() { System.out.println(keyBoardName + "已安全拔出电脑~"); } /** e、并定义它自己独有的功能:打字 */ public void tap() { System.out.println(keyBoardName + "敲击了:老铁,没毛病~~ 666~~~"); } }
-
鼠标实现类
package com.app.d4_polymorphism_test; /** 2、定义USB实现类代表鼠标,让其实现USB接口 */ public class Mouse implements USB{ /** b、定义鼠标名称:商标 */ private String mouseName; /** c、提供无参、有参构造器 */ public Mouse() { } public Mouse(String mouseName) { this.mouseName = mouseName; } /** d、提供变量对应的getter、setter方法,暴露其取值和赋值 */ public String getMouseName() { return mouseName; } public void setMouseName(String mouseName) { this.mouseName = mouseName; } /** a、实现USB接口的规范:接入、拔出 */ @Override public void access() { System.out.println(mouseName + "已成功连接电脑~"); } @Override public void pullOut() { System.out.println(mouseName + "已安全拔出电脑~"); } /** e、并定义它自己独有的功能:点击 */ public void click() { System.out.println(mouseName + "点击了屏幕~"); } }
-
电脑类
package com.app.d4_polymorphism_test; /** 3、创建电脑对象:计算机类 */ public class Computer { /** a、定义计算机名称:商标 */ private String computerName; /** b、提供有参和无参构造器 */ public Computer() { } public Computer(String computerName) { this.computerName = computerName; } /** c、提供变量对应的getter、setter方法,暴露其取值和赋值 */ public String getComputerName() { return computerName; } public void setComputerName(String computerName) { this.computerName = computerName; } /** 模拟电脑开机 */ public void start(){ System.out.println(computerName + "开机了~"); } /** d、提供安装USB时的入口 */ public void installUSB(USB usb){ // USB设备连接电脑 usb.access(); // usb 有可能是键盘,也有可能是鼠标 // 在接入时,先判读这个设备是键盘还是鼠标 if (usb instanceof KeyBoard) { // 是键盘,将USB强制转换成键盘 KeyBoard k = (KeyBoard) usb; k.tap(); }else if (usb instanceof Mouse) { // 是鼠标,将USB强制转换成鼠标 Mouse m = (Mouse) usb; m.click(); } // USB设备安全拔出电脑 usb.pullOut(); } }
-
测试类
package com.app.d4_polymorphism_test; /** 目标:USB设备模拟。 通过此案例综合起之前所学的语法,训练面向对象的思维。 需求: 使用面向对象编程模拟:设计一个电脑对象,可以安装2个USB设备; 鼠标:被安装时可以完成接入、调用点击、拔出功能; 键盘:被安装时可以完成接入、调用打字、拔出功能。 */ public class Test { public static void main(String[] args) { // a、创建电脑对象 Computer c1 = new Computer("联想电脑"); // b、开机 c1.start(); // c、创建键盘、鼠标对象 USB u1 = new KeyBoard("雷蛇"); USB u2 = new Mouse("罗技鼠标"); // d、连接电脑 c1.installUSB(u1); c1.installUSB(u2); } }
-
控制台
联想电脑开机了~ 雷蛇已成功连接电脑~ 雷蛇敲击了:老铁,没毛病~~ 666~~~ 雷蛇已安全拔出电脑~ 罗技鼠标已成功连接电脑~ 罗技鼠标点击了屏幕~ 罗技鼠标已安全拔出电脑~ Process finished with exit code 0
-