前言

继承和多态是面向对象编程的重要特性,它们为代码重用和灵活设计提供了强大支持。继承允许我们基于现有类创建新类,实现代码复用;多态则让我们能够用统一的接口处理不同类型的对象。深入理解继承与多态对于编写高质量的Java程序至关重要。本文将全面介绍Java中继承与多态的概念、实现和应用,帮助您掌握这两个核心特性。

继承基础概念

什么是继承

继承是面向对象编程的核心特性之一,它允许一个类(子类)继承另一个类(父类)的属性和方法。通过继承,子类可以获得父类的特性,同时还可以添加自己的特有功能。

graph TD
    A[继承关系] --> B[父类 Parent Class]
    A --> C[子类 Child Class]
    
    B --> B1[提供公共属性和方法]
    B --> B2[定义通用行为]
    B --> B3[被其他类继承]
    
    C --> C1[继承父类特性]
    C --> C2[扩展新功能]
    C --> C3[重写父类方法]
    
    D[继承的优势] --> D1[代码重用]
    D --> D2[建立类层次结构]
    D --> D3[实现is-a关系]
    D --> D4[支持多态性]
    
    E[继承的特点] --> E1[单继承]
    E --> E2[传递性]
    E --> E3[不能继承private成员]

继承的语法

Java使用extends关键字来实现继承:

1
2
3
4
5
6
7
public class 父类名 {
// 父类成员
}

public class 子类名 extends 父类名 {
// 子类成员
}

动物继承体系示例

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// 父类:动物
public class Animal {
protected String name; // 姓名
protected int age; // 年龄
protected String species; // 物种

public Animal() {
this.name = "未知";
this.age = 0;
this.species = "未知";
}

public Animal(String name, int age, String species) {
this.name = name;
this.age = age;
this.species = species;
}

// 吃的行为
public void eat() {
System.out.println(name + " 正在吃东西");
}

// 睡觉的行为
public void sleep() {
System.out.println(name + " 正在睡觉");
}

// 发出声音的行为(将被子类重写)
public void makeSound() {
System.out.println(name + " 发出声音");
}

// 显示信息
public void showInfo() {
System.out.printf("动物信息 - 姓名:%s,年龄:%d,物种:%s%n",
name, age, species);
}

// Getter和Setter方法
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getSpecies() { return species; }
public void setSpecies(String species) { this.species = species; }
}

// 子类:狗
public class Dog extends Animal {
private String breed; // 品种

public Dog() {
super(); // 调用父类无参构造方法
this.breed = "未知品种";
}

public Dog(String name, int age, String breed) {
super(name, age, "犬科"); // 调用父类有参构造方法
this.breed = breed;
}

// 重写父类的makeSound方法
@Override
public void makeSound() {
System.out.println(name + " 汪汪叫");
}

// 子类特有的方法
public void wagTail() {
System.out.println(name + " 摇尾巴表示友好");
}

public void guard() {
System.out.println(name + " 正在看家护院");
}

// 重写显示信息方法
@Override
public void showInfo() {
super.showInfo(); // 调用父类方法
System.out.println("品种:" + breed);
}

public String getBreed() { return breed; }
public void setBreed(String breed) { this.breed = breed; }
}

// 子类:猫
public class Cat extends Animal {
private boolean isIndoor; // 是否为室内猫

public Cat() {
super();
this.isIndoor = true;
}

public Cat(String name, int age, boolean isIndoor) {
super(name, age, "猫科");
this.isIndoor = isIndoor;
}

// 重写父类的makeSound方法
@Override
public void makeSound() {
System.out.println(name + " 喵喵叫");
}

// 子类特有的方法
public void climb() {
System.out.println(name + " 正在爬树");
}

public void hunt() {
System.out.println(name + " 正在捕猎");
}

// 重写显示信息方法
@Override
public void showInfo() {
super.showInfo();
System.out.println("生活环境:" + (isIndoor ? "室内" : "户外"));
}

public boolean isIndoor() { return isIndoor; }
public void setIndoor(boolean indoor) { isIndoor = indoor; }
}

super关键字

super的作用

super关键字用于访问父类的成员,主要有以下用途:

  1. 调用父类的构造方法
  2. 访问父类的成员变量
  3. 调用父类的成员方法
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
public class Vehicle {
protected String brand;
protected double price;

public Vehicle(String brand, double price) {
this.brand = brand;
this.price = price;
System.out.println("Vehicle构造方法被调用");
}

public void start() {
System.out.println("交通工具启动");
}

public void showInfo() {
System.out.printf("品牌:%s,价格:%.2f万%n", brand, price);
}
}

public class Car extends Vehicle {
private int doors;
private String fuelType;

public Car(String brand, double price, int doors, String fuelType) {
super(brand, price); // 调用父类构造方法
this.doors = doors;
this.fuelType = fuelType;
System.out.println("Car构造方法被调用");
}

@Override
public void start() {
super.start(); // 调用父类方法
System.out.println("汽车发动机启动");
}

@Override
public void showInfo() {
super.showInfo(); // 调用父类方法
System.out.printf("车门数:%d,燃料类型:%s%n", doors, fuelType);
}

public void honk() {
System.out.println(super.brand + " 汽车鸣笛"); // 访问父类成员变量
}
}

方法重写(Override)

方法重写的概念

方法重写是指子类提供父类方法的新实现。重写的方法必须与父类方法具有相同的方法签名(方法名、参数列表和返回类型)。

方法重写规则

graph TD
    A[方法重写规则] --> B[方法签名必须相同]
    A --> C[访问权限不能更严格]
    A --> D[返回类型相同或为子类型]
    A --> E[异常不能更宽泛]
    
    B --> B1[方法名相同]
    B --> B2[参数列表相同]
    B --> B3[使用@Override注解]
    
    C --> C1[public > protected > default > private]
    C --> C2[子类访问权限 >= 父类访问权限]
    
    D --> D1[基本类型必须完全相同]
    D --> D2[引用类型可以是子类型]
    
    E --> E1[可以抛出更少的异常]
    E --> E2[可以抛出更具体的异常]
    
    F[重写vs重载] --> F1[重写是运行时多态]
    F --> F2[重载是编译时多态]
    F --> F3[重写在继承关系中]
    F --> F4[重载在同一类中]

图形继承体系示例

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// 抽象父类:形状
public abstract class Shape {
protected String color;
protected double x, y; // 位置坐标

public Shape(String color, double x, double y) {
this.color = color;
this.x = x;
this.y = y;
}

// 抽象方法:计算面积
public abstract double calculateArea();

// 抽象方法:计算周长
public abstract double calculatePerimeter();

// 具体方法:移动
public void move(double deltaX, double deltaY) {
this.x += deltaX;
this.y += deltaY;
System.out.printf("%s移动到位置(%.1f, %.1f)%n", getClass().getSimpleName(), x, y);
}

// 具体方法:显示信息
public void showInfo() {
System.out.printf("%s - 颜色:%s,位置:(%.1f, %.1f)%n",
getClass().getSimpleName(), color, x, y);
}

// Getter方法
public String getColor() { return color; }
public double getX() { return x; }
public double getY() { return y; }
}

// 圆形类
public class Circle extends Shape {
private double radius;

public Circle(String color, double x, double y, double radius) {
super(color, x, y);
this.radius = radius;
}

@Override
public double calculateArea() {
return Math.PI * radius * radius;
}

@Override
public double calculatePerimeter() {
return 2 * Math.PI * radius;
}

@Override
public void showInfo() {
super.showInfo();
System.out.printf("半径:%.1f,面积:%.2f,周长:%.2f%n",
radius, calculateArea(), calculatePerimeter());
}

public double getRadius() { return radius; }
public void setRadius(double radius) { this.radius = radius; }
}

// 矩形类
public class Rectangle extends Shape {
protected double width, height;

public Rectangle(String color, double x, double y, double width, double height) {
super(color, x, y);
this.width = width;
this.height = height;
}

@Override
public double calculateArea() {
return width * height;
}

@Override
public double calculatePerimeter() {
return 2 * (width + height);
}

@Override
public void showInfo() {
super.showInfo();
System.out.printf("宽度:%.1f,高度:%.1f,面积:%.2f,周长:%.2f%n",
width, height, calculateArea(), calculatePerimeter());
}

public double getWidth() { return width; }
public void setWidth(double width) { this.width = width; }
public double getHeight() { return height; }
public void setHeight(double height) { this.height = height; }
}

// 正方形类(继承矩形)
public class Square extends Rectangle {
public Square(String color, double x, double y, double side) {
super(color, x, y, side, side);
}

@Override
public void setWidth(double side) {
super.setWidth(side);
super.setHeight(side);
}

@Override
public void setHeight(double side) {
super.setWidth(side);
super.setHeight(side);
}

public void setSide(double side) {
setWidth(side);
}

public double getSide() {
return width;
}
}

多态性

多态的概念

多态(Polymorphism)是指同一个接口可以有多种不同的实现方式。在Java中,多态主要通过继承和方法重写来实现,允许父类引用指向子类对象。

多态的实现机制

graph TD
    A[Java多态机制] --> B[编译时类型检查]
    A --> C[运行时动态绑定]
    
    B --> B1[根据引用类型检查]
    B --> B2[确保方法存在]
    B --> B3[验证访问权限]
    
    C --> C1[根据对象实际类型]
    C --> C2[调用对应的重写方法]
    C --> C3[实现一个接口多种实现]
    
    D[多态的条件] --> D1[继承关系]
    D --> D2[方法重写]
    D --> D3[父类引用指向子类对象]
    
    E[多态的优势] --> E1[代码灵活性]
    E --> E2[可扩展性]
    E --> E3[统一接口处理]
    E --> E4[降低耦合度]

多态示例:动物园管理系统

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
public class Zoo {
private Animal[] animals;
private int count;

public Zoo(int capacity) {
animals = new Animal[capacity];
count = 0;
}

// 添加动物(多态:可以接收任何Animal子类对象)
public void addAnimal(Animal animal) {
if (count < animals.length) {
animals[count++] = animal;
System.out.println("添加动物:" + animal.getName());
} else {
System.out.println("动物园已满,无法添加更多动物");
}
}

// 让所有动物发出声音(多态的体现)
public void makeAllAnimalsSound() {
System.out.println("=== 动物园的声音 ===");
for (int i = 0; i < count; i++) {
animals[i].makeSound(); // 运行时根据实际对象类型调用相应方法
}
}

// 喂食所有动物
public void feedAllAnimals() {
System.out.println("=== 喂食时间 ===");
for (int i = 0; i < count; i++) {
animals[i].eat();
}
}

// 显示所有动物信息
public void showAllAnimals() {
System.out.println("=== 动物园动物列表 ===");
for (int i = 0; i < count; i++) {
animals[i].showInfo();
System.out.println();
}
}

// 统计动物种类
public void countAnimalTypes() {
int dogCount = 0, catCount = 0, otherCount = 0;

for (int i = 0; i < count; i++) {
if (animals[i] instanceof Dog) {
dogCount++;
} else if (animals[i] instanceof Cat) {
catCount++;
} else {
otherCount++;
}
}

System.out.println("=== 动物种类统计 ===");
System.out.println("狗:" + dogCount + "只");
System.out.println("猫:" + catCount + "只");
System.out.println("其他:" + otherCount + "只");
}
}

// 测试多态
public class ZooTest {
public static void main(String[] args) {
Zoo zoo = new Zoo(10);

// 创建不同类型的动物对象
Animal dog1 = new Dog("小白", 3, "金毛");
Animal dog2 = new Dog("小黑", 2, "拉布拉多");
Animal cat1 = new Cat("小花", 1, true);
Animal cat2 = new Cat("小灰", 4, false);

// 添加动物到动物园
zoo.addAnimal(dog1);
zoo.addAnimal(dog2);
zoo.addAnimal(cat1);
zoo.addAnimal(cat2);

// 展示多态性
zoo.makeAllAnimalsSound();
zoo.feedAllAnimals();
zoo.showAllAnimals();
zoo.countAnimalTypes();
}
}

向上转型和向下转型

向上转型(Upcasting)

向上转型是指将子类对象赋值给父类引用,这是自动进行的,也是安全的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class CastingDemo {
public static void main(String[] args) {
// 向上转型(自动转型)
Animal animal1 = new Dog("旺财", 3, "柴犬");
Animal animal2 = new Cat("咪咪", 2, true);

// 可以调用父类方法
animal1.eat();
animal1.makeSound(); // 调用的是Dog重写的方法

// 不能直接调用子类特有方法
// animal1.wagTail(); // 编译错误
}
}

向下转型(Downcasting)

向下转型是指将父类引用转换为子类引用,需要显式转换,并且存在风险。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class DowncastingDemo {
public static void main(String[] args) {
Animal animal = new Dog("小明", 5, "哈士奇");

// 向下转型前先检查类型
if (animal instanceof Dog) {
Dog dog = (Dog) animal; // 显式向下转型
dog.wagTail(); // 现在可以调用Dog特有的方法
dog.guard();
}

// 错误的向下转型会抛出ClassCastException
try {
Cat cat = (Cat) animal; // 运行时异常
} catch (ClassCastException e) {
System.out.println("类型转换失败:" + e.getMessage());
}
}
}

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
30
31
32
33
34
35
public class InstanceofDemo {
public static void performAnimalAction(Animal animal) {
// 基础行为
animal.eat();
animal.makeSound();

// 根据具体类型执行特定行为
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.wagTail();
dog.guard();
} else if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.climb();
cat.hunt();
}

// 检查继承关系
System.out.println(animal.getName() + " instanceof Animal: " +
(animal instanceof Animal));
System.out.println(animal.getName() + " instanceof Dog: " +
(animal instanceof Dog));
System.out.println(animal.getName() + " instanceof Cat: " +
(animal instanceof Cat));
}

public static void main(String[] args) {
Animal dog = new Dog("汪汪", 4, "边牧");
Animal cat = new Cat("喵喵", 3, false);

performAnimalAction(dog);
System.out.println();
performAnimalAction(cat);
}
}

final关键字

final的用途

final关键字在继承中有重要作用,它可以用来限制继承和重写:

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
// final类:不能被继承
public final class String {
// String类的实现
}

// 包含final方法的类
public class FinalMethodDemo {
// final方法:不能被重写
public final void doSomething() {
System.out.println("这个方法不能被重写");
}

// 可以被重写的方法
public void doOtherThing() {
System.out.println("这个方法可以被重写");
}
}

public class SubClass extends FinalMethodDemo {
// 编译错误:不能重写final方法
// public void doSomething() { }

@Override
public void doOtherThing() {
System.out.println("重写了父类方法");
}
}

// final变量示例
public class FinalVariableDemo {
private final String CONSTANT = "常量值"; // final实例变量
private static final int MAX_SIZE = 100; // final类变量

public void method(final int parameter) { // final参数
final int localVar = 10; // final局部变量

// 以下语句会导致编译错误
// parameter = 20;
// localVar = 30;
}
}

继承与多态的最佳实践

设计原则

  1. 里氏替换原则:子类对象应该能够替换父类对象
  2. 开闭原则:对扩展开放,对修改关闭
  3. 依赖倒置原则:依赖抽象而不是具体实现

实际应用:支付系统

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// 抽象支付类
public abstract class Payment {
protected double amount;
protected String orderId;

public Payment(double amount, String orderId) {
this.amount = amount;
this.orderId = orderId;
}

// 模板方法:定义支付流程
public final boolean processPayment() {
if (!validatePayment()) {
return false;
}

boolean result = executePayment();
if (result) {
sendConfirmation();
}
return result;
}

// 抽象方法:由子类实现具体支付逻辑
protected abstract boolean executePayment();

// 钩子方法:子类可以重写
protected boolean validatePayment() {
return amount > 0 && orderId != null;
}

protected void sendConfirmation() {
System.out.println("订单" + orderId + "支付确认已发送");
}
}

// 支付宝支付
public class AlipayPayment extends Payment {
private String alipayAccount;

public AlipayPayment(double amount, String orderId, String alipayAccount) {
super(amount, orderId);
this.alipayAccount = alipayAccount;
}

@Override
protected boolean executePayment() {
System.out.printf("使用支付宝账户%s支付%.2f元,订单号:%s%n",
alipayAccount, amount, orderId);
// 模拟支付处理
return true;
}

@Override
protected boolean validatePayment() {
return super.validatePayment() && alipayAccount != null;
}
}

// 微信支付
public class WechatPayment extends Payment {
private String wechatId;

public WechatPayment(double amount, String orderId, String wechatId) {
super(amount, orderId);
this.wechatId = wechatId;
}

@Override
protected boolean executePayment() {
System.out.printf("使用微信账户%s支付%.2f元,订单号:%s%n",
wechatId, amount, orderId);
// 模拟支付处理
return true;
}
}

// 银行卡支付
public class BankCardPayment extends Payment {
private String cardNumber;
private String bankName;

public BankCardPayment(double amount, String orderId,
String cardNumber, String bankName) {
super(amount, orderId);
this.cardNumber = cardNumber;
this.bankName = bankName;
}

@Override
protected boolean executePayment() {
System.out.printf("使用%s银行卡(**** %s)支付%.2f元,订单号:%s%n",
bankName, cardNumber.substring(cardNumber.length()-4),
amount, orderId);
return true;
}
}

// 支付处理器
public class PaymentProcessor {
public void processPayments(Payment[] payments) {
for (Payment payment : payments) {
System.out.println("开始处理支付...");
boolean success = payment.processPayment();
System.out.println("支付结果:" + (success ? "成功" : "失败"));
System.out.println("---");
}
}
}

总结

继承与多态是Java面向对象编程的核心特性,本文深入介绍了这两个重要概念:

  1. 继承基础:理解继承的概念、语法和继承关系的建立
  2. super关键字:掌握访问父类成员的方法
  3. 方法重写:学会正确重写父类方法并遵循重写规则
  4. 多态性:理解多态的实现机制和运行时动态绑定
  5. 类型转换:掌握向上转型和向下转型的使用
  6. final关键字:了解如何限制继承和重写
  7. 最佳实践:学习在实际项目中合理运用继承与多态

继承提供了代码重用的机制,而多态则为程序设计带来了极大的灵活性。正确理解和运用这两个特性,能够帮助我们设计出更优雅、更易维护的面向对象程序。

参考资料

  1. Oracle Java Documentation - Inheritance
  2. Effective Java by Joshua Bloch
  3. Design Patterns: Elements of Reusable Object-Oriented Software
  4. Java核心技术卷I - 继承与多态