0%

Java(13)面向对象三大特性之多态性

18. 面向对象三大特性之多态性

18.1 多态的定义

代码当中体现多态性,其实就是一句话:父类引用指向子类对象.

格式:

1
父类名称 对象名 = new 子类名称();

或者:

1
接口名称 对象名 = new 实现类名称();

父类:

1
2
3
4
5
6
7
8
9
10
11
package com.tipdm3.Demo01;

public class Fu {
public void method(){
System.out.println("父类方法");
}

public void methodFu(){
System.out.println("父类特有方法!");
}
}

子类:

1
2
3
4
5
6
7
8
package com.tipdm3.Demo01;

public class Zi extends Fu{
@Override
public void method() {
System.out.println("子类方法");
}
}

主类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.tipdm3.Demo01;

/**
* 代码当中体现多态性,其实就是一句话:父类引用指向子类对象.
* 格式:
* 父类名称 对象名 = new 子类名称();
* 或者:
* 接口名称 对象名 = new 实现类名称();
*/
public class demo1 {
public static void main(String[] args) {
// 使用多态的写法
// 左侧父类的引用,指向了右侧子类的对象
Fu obj = new Zi();

obj.method();
obj.methodFu();
}
}
1
2
3
4
子类方法
父类特有方法!

进程已结束,退出代码0

image-20211213175418208

多态成员变量的访问

访问成员变量的两种方式:

  1. 直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找

  2. 间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找

父类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.tipdm3.Demo02;

public class Fu{
int num = 10;
public void showNum(){
System.out.println(num);
}

public void method(){
System.out.println("父类方法");
}

public void methodFu(){
System.out.println("父类特有方法");
}
}

子类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.tipdm3.Demo02;

public class Zi extends Fu{
int num = 20;
int age = 18;

@Override
public void showNum() {
System.out.println(num);
}

@Override
public void method(){
System.out.println("子类方法");
}

public void methodZi() {
System.out.println("子类特有方法");
}
}

主类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.tipdm3.Demo02;

/**
* 访问成员变量的两种方式:
* 1. 直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找
* 2. 间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找
*/
public class demo01 {
public static void main(String[] args) {
// 使用多态的写法,父类引用指向子类对象
Fu obj = new Zi();
System.out.println(obj.num); // 10
// System.out.println(age.num); // 错误写法
System.out.println("=============");
obj.showNum(); // 子类没有覆盖重写,就算父: 10 覆盖重写,就是子: 20
}
}
1
2
3
4
5
10
=============
20

进程已结束,退出代码0

到这里,其实可以做一个小总结;多态其实就是为了使用子类对父类中的某些方法进行修改,而不影响父类中其他功能的正常运行。

多态成员方法的访问

在多态的代码当中,成员方法的访问规则是:

看new的是谁,就优先用谁,没有就向上找

口诀:

  • 编译看左边,运行看右边

主类(子类和父类同上节):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.tipdm3.Demo02;

/**
* 在多态的代码当中,成员方法的访问规则是:
* 看new的是谁,就优先用谁,没有就向上找
*
* 口诀:编译看左边,运行看右边
*
* 对比一下:
* 成员变量: 编译看左边,运行还看左边
* 成员方法: 编译看左边,运行看右边
*/
public class demo02 {
public static void main(String[] args) {
Fu obj = new Zi(); // 多态

obj.method(); // 父子都有,优先用子
obj.methodFu(); // 子类没有,父类有,向上找到父类

// 编译看左边,左边是Fu, Fu当中没有methodZi方法,所以编译报错.
// obj.methodZi(); // 错误写法!
}
}
1
2
3
4
子类方法
父类特有方法

进程已结束,退出代码0

对比一下多态中访问成员变量和方法的不同点:

  • 成员变量: 编译看左边,运行还看左边
  • 成员方法: 编译看左边,运行看右边

18.4 多态的向上转型和向下转型

  • 向上转型一定是安全的,没有问题的,正确的.
  • 但是有一个弊端:
  • 对象一旦向上转型成为父类,那么就无法调用子类原本特有的内容.
  • 解决方案:用对象的向下转型【还原】.
  • 本来是猫才能还原成为猫

image-20211213181141633

父类:

1
2
3
4
5
package com.tipdm3.Demo03;

public abstract class Animal {
public abstract void eat();
}

子类——猫:

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.tipdm3.Demo03;

public class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}

// 子类特有方法
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}

子类——狗

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.tipdm3.Demo03;

public class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃骨头");
}

// 子类特有方法
public void watchHouse(){
System.out.println("狗看家");
}
}

主类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.tipdm3.Demo03;

/**
* 向上转型一定是安全的,没有问题的,正确的.
* 但是有一个弊端:
* 对象一旦向上转型成为父类,那么就无法调用子类原本特有的内容.
*
* 解决方案:用对象的向下转型[还原].
* 本来是猫才能还原成为猫
*/

public class demo1 {
public static void main(String[] args) {
// 对象的向上转型,就算:父类引用指向子类对象
Animal animal = new Cat();
animal.eat(); // 猫吃鱼
// animal.catchMouse(); // 错误写法!无法找到特有内容.

Cat animal1 = (Cat) animal; // 对象向下转型,还原
animal1.catchMouse();

// Dog animal2 = (Dog) animal; // 对象本来是猫,向下转型成为狗就会报错(虽然编译不报错)
// animal2.watchHouse();
}
}
1
2
3
4
猫吃鱼
猫抓老鼠

进程已结束,退出代码0

如何保证使用向下转型不会出错呢?

  • 加上对象类型判断即可
  • 使用instanceof方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.tipdm3.Demo03;

/**
* 如何才能知道一个父类引用的对象,本来是什么子类?
* 格式:
* 对象 instanceof 类名称
* 这将会得到一个boolean值结果,也就是判断前面的对象能不能当做后面类型的实例.
*/
public class demo2Instanceof {
public static void main(String[] args) {
Animal animal = new Dog(); // 本来是一只猫
animal.eat(); // 猫吃鱼
giveMeAPet(animal);
}

public static void giveMeAPet(Animal animal){
// 如果希望调用子类特有的方法,需要向下转型
// 判断一下父类引用animal
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.watchHouse();
}

if (animal instanceof Cat){
Cat cat = (Cat) animal;
cat.catchMouse();
}
}
}
1
2
3
4
狗吃骨头
狗看家

进程已结束,退出代码0

接口和多态综合案例:定义USB接口,具备最基本的开启功能和关闭功能.

鼠标和键盘要想能在电脑上使用,那么鼠标和键盘也必须遵守USB规范,实现USB接口,否则鼠标和键盘的生成出来也无法使用.

分析:

​ 一共有电脑,鼠标,键盘三个类,并且三个类都属于电子产品。他们都有开启、关闭、使用设备功能。其中,鼠标和键盘是USB设备,需要遵循USB接口协议。

最终项目结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
USB —— USB接口
{
public abstract void open();
public abstract void close();
public abstract void use();
}
ElectronicProduct —— 电子产品(父类,遵行USB接口协议)

{
String name; // 设备名称
boolean STATE_OPEN; // 用于记录设备的开启状态
open(); // 打开设备方法
use(); // 使用设备方法
close(); // 关闭设备方法
usbFunction(); // USB设备功能
showState(); // 查询设备工作状态
}

Computer—— 电脑(ElectronicProduct 子类)
{
useUSBDevice(); // 使用USB设备方法
}

Mouse —— 鼠标(ElectronicProduct 子类)
{
usbFunction(); // 鼠标功能
}

KeyBoard —— 键盘(ElectronicProduct 子类)
{
usbFunction(); // 键盘功能
}

USB接口:

1
2
3
4
5
6
7
8
9
package com.tipdm3.Demo04;

public interface USB {
public abstract void open();

public abstract void close();

public abstract void use();
}

ElectronicProduct类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.tipdm3.Demo04;

public class ElectronicProduct implements USB{
private String name; // 设备名称
private boolean STATE_OPEN = false; // 设备是否开启,默认关闭

// 有参构造函数
public ElectronicProduct(String name) {
this.name = name;
}

// 无参构造函数
public ElectronicProduct() {
}

// 实现USB接口open方法
@Override
public void open() {
System.out.println("打开" + this.name);
this.STATE_OPEN = true;
}

// 实现USB接口close方法
@Override
public void close() {
System.out.println("关闭" + this.name);
this.STATE_OPEN = false;
}

// 实现USB接口use方法
@Override
public void use() {
if (this.STATE_OPEN) {
this.usbFunction();
}else{
System.out.println("请先开启" + this.name);
}
}

// USB设备功能
public void usbFunction(){
System.out.println("=============USB功能=============");
}

// SETTER/GETTER方法
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public boolean isSTATE_OPEN() {
return STATE_OPEN;
}

public void setSTATE_OPEN(boolean STATE_OPEN) {
this.STATE_OPEN = STATE_OPEN;
}

// 查询设备状态信息
public void showState(){
System.out.println(this.name + " State:" + this.STATE_OPEN);
}

}

Computer类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.tipdm3.Demo04;

public class Computer extends ElectronicProduct{
@Override
public void open() {
System.out.println("开启电脑");
super.setSTATE_OPEN(true);
}

@Override
public void close() {
System.out.println("关闭电脑");
super.setSTATE_OPEN(false);
}

public void useUSBDevice(USB usb){
usb.open();
usb.use();
usb.close();
}
}

Mouse类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.tipdm3.Demo04;

public class Mouse extends ElectronicProduct{
public Mouse(String name) {
super(name);
}

public Mouse() {
}

@Override
public void usbFunction() {
System.out.println("=============点击!============");
}
}

KeyBoard类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.tipdm3.Demo04;

public class KeyBoard extends ElectronicProduct{
public KeyBoard() {
}

public KeyBoard(String name) {
super(name);
}

@Override
public void usbFunction() {
System.out.println("==============打字!============");
}
}

主类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.tipdm3.Demo04;

/**
* 定义USB接口,具备最基本的开启功能和关闭功能.
* 鼠标和键盘要想能在电脑上使用,那么鼠标和键盘也必须遵守USB规范,实现USB接口,否则鼠标和键盘的生成出来也无法使用.
*/
public class demo {
public static void main(String[] args) {
Computer computer = new Computer();
// 开启电脑
computer.open();
// 判断电脑是否开启
if(computer.isSTATE_OPEN()){
// 在电脑开启的前提下,定义一个鼠标,并使用鼠标
// Mouse mouse = new Mouse("鼠标");
// USB usb = mouse;
// computer.useUSBDevice(usb);
// 上方写法与下面等价
Mouse mouse = new Mouse("鼠标"); // 发生了自动类型转换
computer.useUSBDevice(mouse);

// 在电脑开启的前提下,定义一个键盘,并使用键盘
KeyBoard keyBoard = new KeyBoard("键盘");
computer.useUSBDevice(keyBoard);
}else{
System.out.println("请先开启电脑,再使用USB设备!");
}
// 关闭电脑
computer.close();
}

}
1
2
3
4
5
6
7
8
9
10
开启电脑
打开鼠标
=============点击!============
关闭鼠标
打开键盘
==============打字!============
关闭键盘
关闭电脑

进程已结束,退出代码0
-------------本文结束感谢您的阅读-------------