# Java教程 - 7 面向对象
# 7.6 多态
# 1 什么是多态
多态就是多种状态,同一个类型的父类型对象,因为指向的是不同的子对象,而表现出的不同的状态。
所以多态是建立在继承的基础之上的。
举个栗子:
class Bird {
public void tweet() {}
}
class Sparrow extends Bird {
public void tweet() {
System.out.println("我会啾啾叫");
}
}
class Pigeon extends Bird {
public void tweet() {
System.out.println("我会咕咕叫");
}
}
public class ExtendTest {
public static void main(String[] args) {
Bird bird1 = new Sparrow(); // 创建一个麻雀对象
Bird bird2 = new Pigeon(); // 创建一个鸽子对象
bird1.tweet();
bird2.tweet();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
上面麻雀类和鸽子类都继承自鸟类,然后创建了一个麻雀对象和鸽子对象,都赋给了鸟类,通过两个对象分别调用 tweet() 方法。
执行结果:
我会啾啾叫 我会咕咕叫
虽然两个都是鸟类的变量,执行的都是 tweet() 方法,但是因为是不同的子类对象,却得到不同的结果。
以父类做定义声明,以子类做实际的工作,用于获取同一个行为的不同状态,这就是多态。
那也没看出多态有什么用啊。
多态的作用:
- 提高代码的维护性
- 提高代码的扩展性
- 把不同的子类对象当做父类来看待,可以屏蔽不同子类对象之间的差异,写出通用的代码,以适应需求的不断变化。
# 2 多态的使用
说了那么多,有点虚,举个栗子:
实现一个功能:学生做交通工具去某个地方,交通工具可能是汽车、飞机。
先定一个汽车类:
传入一个目的地,就可以开车去了。
class Car {
public void run(String destination) {
System.out.println("开车去->" + destination);
}
}
2
3
4
5
再定义一个飞机类:
class Plane {
public void fly(String destination) {
System.out.println("飞去->" + destination);
}
}
2
3
4
5
然后定义一个学生类:
class Student {
public void goTo(Object vehicle, String destination) { // 传入交通工具
if (vehicle instanceof Car) { // 判断交通工具的类型,然后调用交通工具的方法
Car car = (Car) vehicle;
car.run(destination);
} else if (vehicle instanceof Plane) {
Plane plane = (Plane) vehicle;
plane.fly(destination);
}
}
}
2
3
4
5
6
7
8
9
10
11
学生类有一个 goTo() 方法,接收交通工具和目的地,然后在方法中判断交通工具的类型,然后调用交通工具的方法。
调用代码:
public class ObjectTest {
public static void main(String[] args) {
Student stu = new Student();
Car car = new Car();
Plane plane = new Plane();
stu.goTo(car, "北京");
stu.goTo(plane, "新疆");
}
}
2
3
4
5
6
7
8
9
10
执行结果:
开车去->北京 飞去->新疆
上面的代码可以实现功能,但是不易于扩展,如果我们现在增加一个交通工具火车,则还需要修改 Student 类的 goTo()
方法,针对新的交通工具来处理,因为学生类和汽车、飞机类直接存在依赖关系,耦合性高。
这样是违反了设计原则中的开闭原则,对扩展是开放的,对修改是封闭的,也就是允许在不改变它代码的前提下变更它的行为。
所以上面的代码扩展性就比较差了,那么怎么来优化代码,降低代码的耦合性呢?
这就需要用到多态了。
首先定义一个父类Vehicle(交通工具类),并定义一个transport()方法,都是交通工具,都是运输功能嘛。
然后让Car类和Plane类都继承这个父类,因为不同的子类运输方式不一样,所以子类需要重写父类的方法,实现自己的功能。
class Vehicle {
void transport(String destination) {}
}
class Car extends Vehicle {
public void transport(String destination) {
System.out.println("开车去->" + destination);
}
}
class Plane extends Vehicle {
public void transport(String destination) {
System.out.println("飞去->" + destination);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
然后修改学生类:
class Student {
public void goTo(Vehicle vehicle, String destination) { // 传入交通工具
vehicle.transport(destination);
}
}
2
3
4
5
学生类只需要调用交通工具的运输功能就可以了。
调用的代码不变:
public class ObjectTest {
public static void main(String[] args) {
Student stu = new Student();
Car car = new Car();
Plane plane = new Plane();
stu.goTo(car, "北京");
stu.goTo(plane, "新疆");
}
}
2
3
4
5
6
7
8
9
10
执行结果:
开车去->北京 飞去->新疆
上面的代码使用了多态,学生类与各个交通工具子类已经不直接产生关系,遵从了设置原则中的依赖倒置原则(程序依赖于抽象接口,不要依赖于具体实现)。
此时如果新增一个火车的交通工具,不用再修改学生类的代码,代码耦合性大大降低。
# 3 抽象类
什么是抽象类?
含有抽象方法的类成为抽象类。
那什么是抽象方法?
抽象方法就是没有方法体,方法体为空的方法。
上面的 Vehicle
类中的 transport
方法,没有方法体,我们可以将它定义为一个抽象方法,在方法前面添加 abstract
关键字。
包含抽象方法的类必须是抽象类,所以类也必须添加 abstract
关键字,将 Vehicle
定义为抽象类。
abstract class Vehicle {
abstract public void transport(String destination);
}
2
3
包含抽象方法的类必须是抽象类,但是抽象类中可以没有抽象方法。
举个栗子:
abstract class Vehicle {
/**
* 抽象方法
*/
abstract public void transport(String destination);
/**
* 非抽象方法
*/
public void test() {
System.out.println("测试方法");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
抽象类是不能被实例化的,在开发中,都会提供抽象类的子类,通过子类来实例化对象。
子类继承抽象类需要实现(重写)抽象类中所有的抽象方法,如果有抽象方法没有实现,那么这个类也必须定义为抽象类。
抽象类有什么作用呢?
抽象类是不能被实例化的,也就是不能用来创建对象。一般抽象类都是作为父类使用的,父类用来确定有哪些方法,相当于用来确定设计的标准,用于对子类的约束。
子类用来实现父类的标准,负责具体的实现,配合多态使用,获得不同的工作状态。
# 4 接口
如果一个抽象类中的方法都是抽象方法,我们可以将这个抽象类定义成接口。
在 Java 中,定义接口使用 interface
关键字,和定义抽象类是不同的,而且接口中的方法都是 abstract public
,所以可以省略 abstract 和 public 修饰。
另外,一个类通过 extends
来继承抽象类,但是是通过 implements
来实现接口的。
举个栗子:
// 定义一个抽象类
abstract class Bird {
abstract public void tweet();
}
// 定义一个接口
interface Animal {
void breathe();
}
// 定义一个接口
interface Helpful {
void help();
}
class Sparrow extends Bird implements Animal, Helpful {
void tweet() {
System.out.println("我会啾啾叫");
}
public void breathe() {
System.out.println("我要呼吸");
}
public void help() {
System.out.println("会吃虫子");
}
}
public class ObjectTest {
public static void main(String[] args) {
Animal animal = new Sparrow();
animal.breathe();
}
}
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
上面我们定义了一个抽象类 Bird
,和两个接口 Animal
、Helpful
。
然后定义了一个 Sparrow
类作为子类继承 Bird
类,同时实现了 Animal
、Helpful
两个接口。此时才看出抽象类和接口的不同。
一个类只能有一个父类,但是可以有多个接口。一个类需要实现抽象类和接口中所有的抽象方法,否则编译会报错。
接口中只能定义常量和抽象方法,所以抽象类的作用主要是提供约束和规范,在后面的JDK高版本中,接口中也可以定义方法了。
结合前面多态的使用去理解,前面多态的使用中,使用的是父类来完成的,我们也可以使用接口来完成。
代码:
interface Vehicle { // 定义交通工具的接口,所有的交通工具都需要实现该接口
void transport(String destination);
}
class Car implements Vehicle { // 需要实现交通工具接口
public void transport(String destination) {
System.out.println("开车去->" + destination);
}
}
class Plane implements Vehicle { // 需要实现交通工具接口
public void transport(String destination) {
System.out.println("飞去->" + destination);
}
}
class Student {
public void goTo(Vehicle vehicle, String destination) { // 传入的参数是交通工具,交通工具都必须有transport()方法
vehicle.transport(destination);
}
}
public class ObjectTest {
public static void main(String[] args) {
Student stu = new Student();
Car car = new Car();
Plane plane = new Plane();
stu.goTo(car, "北京");
stu.goTo(plane, "新疆");
}
}
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