前言

抽象类和接口是Java面向对象编程中的高级特性,它们为程序设计提供了强大的抽象能力。通过抽象类和接口,我们可以定义规范、建立契约,实现更加灵活和可扩展的程序架构。理解抽象类和接口的概念、特点和应用场景,对于编写高质量的Java程序至关重要。本文将全面介绍Java中抽象类和接口的知识,帮助您掌握这两个重要的编程概念。

抽象类基础

什么是抽象类

抽象类是一种特殊的类,它不能被实例化,只能被继承。抽象类通常包含一个或多个抽象方法,这些方法只有声明没有实现,必须由子类来实现。

graph TD
    A[抽象类特征] --> B[使用abstract关键字]
    A --> C[不能实例化]
    A --> D[可以包含抽象方法]
    A --> E[可以包含具体方法]
    A --> F[可以有构造方法]
    A --> G[可以有成员变量]
    
    B --> B1[abstract class ClassName]
    C --> C1[只能被继承使用]
    D --> D1[abstract method]
    D --> D2[无方法体]
    E --> E1[有完整实现]
    E --> E2[子类可直接使用]
    F --> F1[供子类调用]
    G --> G1[protected或private]
    
    H[抽象类用途] --> H1[定义通用行为]
    H --> H2[强制子类实现特定方法]
    H --> H3[提供部分实现]
    H --> H4[建立类层次结构]

抽象类语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 抽象类的定义
public abstract class AbstractClass {
// 成员变量
protected String name;

// 构造方法
public AbstractClass(String name) {
this.name = name;
}

// 具体方法
public void concreteMethod() {
System.out.println("这是具体方法的实现");
}

// 抽象方法
public abstract void abstractMethod();

// 另一个抽象方法
public abstract int calculate(int x, int y);
}

图形抽象类示例

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
// 抽象图形类
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 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 setColor(String color) {
this.color = color;
System.out.println(getClass().getSimpleName() + "的颜色设置为:" + color);
}

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

// 抽象方法:计算面积(必须由子类实现)
public abstract double calculateArea();

// 抽象方法:计算周长(必须由子类实现)
public abstract double calculatePerimeter();

// 抽象方法:绘制图形(必须由子类实现)
public abstract void draw();

// 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 draw() {
System.out.printf("绘制%s圆形 - 中心:(%.1f, %.1f),半径:%.1f%n",
color, x, y, radius);
}

// 圆形特有方法
public double getDiameter() {
return 2 * radius;
}

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 draw() {
System.out.printf("绘制%s矩形 - 左上角:(%.1f, %.1f),宽:%.1f,高:%.1f%n",
color, x, y, width, height);
}

// 矩形特有方法
public boolean isSquare() {
return Math.abs(width - height) < 0.001;
}

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; }
}

接口基础

什么是接口

接口是一种完全抽象的类型,它定义了一组方法签名,但不提供实现。接口描述了类应该做什么,而不是如何做。实现接口的类必须提供接口中所有方法的具体实现。

graph TD
    A[接口特征] --> B[使用interface关键字]
    A --> C[完全抽象]
    A --> D[不能实例化]
    A --> E[可以多实现]
    A --> F[支持多继承]
    
    B --> B1[interface InterfaceName]
    C --> C1[所有方法都是抽象的JDK8之前]
    C --> C2[可有默认方法JDK8+]
    D --> D1[只能通过实现类使用]
    E --> E1[一个类可实现多个接口]
    F --> F1[一个接口可继承多个接口]
    
    G[接口成员] --> G1[常量final static]
    G --> G2[抽象方法]
    G --> G3[默认方法JDK8+]
    G --> G4[静态方法JDK8+]
    G --> G5[私有方法JDK9+]
    
    H[接口用途] --> H1[定义契约]
    H --> H2[实现多重继承]
    H --> H3[降低耦合度]
    H --> H4[支持多态]

接口语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 接口的定义
public interface MyInterface {
// 常量(public static final 可以省略)
int CONSTANT_VALUE = 100;
String DEFAULT_NAME = "Default";

// 抽象方法(public abstract 可以省略)
void method1();
int method2(String param);

// 默认方法(Java 8+)
default void defaultMethod() {
System.out.println("这是默认方法");
}

// 静态方法(Java 8+)
static void staticMethod() {
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
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// 可飞行接口
public interface Flyable {
// 常量:最大飞行高度
double MAX_ALTITUDE = 10000.0;

// 抽象方法
void fly();
void land();

// 默认方法:检查是否可以飞行
default boolean canFly() {
return true;
}

// 默认方法:获取飞行速度
default double getFlightSpeed() {
return 50.0; // 默认速度 km/h
}

// 静态方法:转换速度单位
static double convertSpeed(double kmPerHour) {
return kmPerHour * 0.277778; // 转换为 m/s
}
}

// 可游泳接口
public interface Swimmable {
// 常量:最大潜水深度
double MAX_DEPTH = 1000.0;

// 抽象方法
void swim();
void dive(double depth);

// 默认方法:检查是否可以游泳
default boolean canSwim() {
return true;
}

// 默认方法:获取游泳速度
default double getSwimSpeed() {
return 10.0; // 默认速度 km/h
}
}

// 可跑步接口
public interface Runnable {
// 抽象方法
void run();
void stop();

// 默认方法:获取跑步速度
default double getRunSpeed() {
return 20.0; // 默认速度 km/h
}
}

// 鸟类:实现可飞行接口
public class Bird implements Flyable {
private String name;
private double flightSpeed;
private boolean isFlying;

public Bird(String name, double flightSpeed) {
this.name = name;
this.flightSpeed = flightSpeed;
this.isFlying = false;
}

@Override
public void fly() {
if (!isFlying) {
isFlying = true;
System.out.println(name + " 开始飞行,速度:" + flightSpeed + " km/h");
} else {
System.out.println(name + " 已经在飞行中");
}
}

@Override
public void land() {
if (isFlying) {
isFlying = false;
System.out.println(name + " 降落了");
} else {
System.out.println(name + " 已经在地面上");
}
}

@Override
public double getFlightSpeed() {
return flightSpeed;
}

public String getName() { return name; }
public boolean isFlying() { return isFlying; }
}

// 鱼类:实现可游泳接口
public class Fish implements Swimmable {
private String name;
private double swimSpeed;
private double currentDepth;

public Fish(String name, double swimSpeed) {
this.name = name;
this.swimSpeed = swimSpeed;
this.currentDepth = 0.0;
}

@Override
public void swim() {
System.out.println(name + " 正在游泳,速度:" + swimSpeed + " km/h");
}

@Override
public void dive(double depth) {
if (depth > MAX_DEPTH) {
System.out.println("潜水深度超过最大限制:" + MAX_DEPTH + "米");
return;
}

currentDepth = depth;
System.out.printf("%s 潜水到深度:%.1f米%n", name, depth);
}

@Override
public double getSwimSpeed() {
return swimSpeed;
}

public String getName() { return name; }
public double getCurrentDepth() { return currentDepth; }
}

// 鸭子:实现多个接口
public class Duck implements Flyable, Swimmable, Runnable {
private String name;
private boolean isFlying;
private boolean isSwimming;
private boolean isRunning;

public Duck(String name) {
this.name = name;
this.isFlying = false;
this.isSwimming = false;
this.isRunning = false;
}

// 实现飞行接口
@Override
public void fly() {
isFlying = true;
System.out.println(name + " 开始飞行");
}

@Override
public void land() {
isFlying = false;
System.out.println(name + " 降落了");
}

// 实现游泳接口
@Override
public void swim() {
isSwimming = true;
System.out.println(name + " 开始游泳");
}

@Override
public void dive(double depth) {
if (depth > 10.0) { // 鸭子潜水能力有限
System.out.println(name + " 不能潜水这么深");
return;
}
System.out.printf("%s 潜水到深度:%.1f米%n", name, depth);
}

// 实现跑步接口
@Override
public void run() {
isRunning = true;
System.out.println(name + " 开始跑步");
}

@Override
public void stop() {
isRunning = false;
isSwimming = false;
System.out.println(name + " 停止了所有运动");
}

// 鸭子特有方法
public void quack() {
System.out.println(name + " 嘎嘎叫");
}

public String getName() { return name; }
public boolean isFlying() { return isFlying; }
public boolean isSwimming() { return isSwimming; }
public boolean isRunning() { return isRunning; }
}

抽象类与接口的区别

详细对比

graph TD
    A[抽象类 vs 接口] --> B[语法差异]
    A --> C[功能差异]
    A --> D[使用场景]
    
    B --> B1[abstract class vs interface]
    B --> B2[extends vs implements]
    B --> B3[单继承 vs 多实现]
    
    C --> C1[可有构造方法 vs 不可有]
    C --> C2[可有成员变量 vs 只有常量]
    C --> C3[可有具体方法 vs 主要是抽象]
    
    D --> D1[is-a关系 vs can-do关系]
    D --> D2[共同基类 vs 功能契约]
    D --> D3[部分实现 vs 规范定义]
    
    E[选择原则] --> E1[有共同实现选抽象类]
    E --> E2[纯抽象规范选接口]
    E --> E3[多重继承需求选接口]
    E --> E4[强类型关系选抽象类]

对比表格

特性 抽象类 接口
关键字 abstract class interface
继承方式 extends(单继承) implements(多实现)
构造方法 可以有 不能有
成员变量 可以有各种类型 只能有public static final
方法类型 抽象+具体方法 抽象+默认+静态方法
访问修饰符 各种修饰符 方法默认public
实例化 不能直接实例化 不能实例化
设计理念 is-a关系 can-do能力

选择指南示例

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
// 场景1:使用抽象类 - 动物基类
public abstract class Animal {
protected String name;
protected int age;

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

// 所有动物的共同行为
public void sleep() {
System.out.println(name + " 正在睡觉");
}

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

// 每种动物发声不同,由子类实现
public abstract void makeSound();

// Getter方法
public String getName() { return name; }
public int getAge() { return age; }
}

// 场景2:使用接口 - 能力定义
public interface Drawable {
void draw();
void setColor(String color);

default void showDrawInfo() {
System.out.println("这是一个可绘制的对象");
}
}

public interface Serializable {
String serialize();
void deserialize(String data);
}

// 图形类:既是形状又有绘制和序列化能力
public class Circle extends Shape implements Drawable, Serializable {
private double radius;
private String color;

public Circle(double radius) {
this.radius = radius;
this.color = "black";
}

// 实现绘制接口
@Override
public void draw() {
System.out.println("绘制" + color + "圆形,半径:" + radius);
}

@Override
public void setColor(String color) {
this.color = color;
}

// 实现序列化接口
@Override
public String serialize() {
return "Circle{radius=" + radius + ",color=" + color + "}";
}

@Override
public void deserialize(String data) {
// 简单的反序列化实现
System.out.println("从数据恢复圆形:" + data);
}
}

Java 8+ 接口新特性

默认方法

Java 8引入了默认方法,允许在接口中提供方法的默认实现:

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
public interface Collection<E> {
// 传统抽象方法
boolean add(E e);
boolean remove(Object o);
int size();

// 默认方法(Java 8+)
default boolean isEmpty() {
return size() == 0;
}

default void forEach(Consumer<? super E> action) {
for (E element : this) {
action.accept(element);
}
}

// 静态方法(Java 8+)
static <T> Collection<T> emptyCollection() {
return new ArrayList<>();
}
}

// 函数式接口示例
@FunctionalInterface
public interface Calculator {
// 抽象方法
double calculate(double a, double b);

// 默认方法
default void printResult(double a, double b) {
System.out.printf("%.2f 运算 %.2f = %.2f%n", a, b, calculate(a, b));
}

// 静态方法
static void showHelp() {
System.out.println("计算器使用说明:提供两个数字进行计算");
}
}

// 使用示例
public class CalculatorDemo {
public static void main(String[] args) {
// 使用Lambda表达式实现接口
Calculator adder = (a, b) -> a + b;
Calculator multiplier = (a, b) -> a * b;

// 调用默认方法
adder.printResult(10, 5);
multiplier.printResult(10, 5);

// 调用静态方法
Calculator.showHelp();
}
}

私有方法(Java 9+)

Java 9允许在接口中定义私有方法,用于复用代码:

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
public interface Logger {
// 抽象方法
void log(String message);

// 默认方法
default void logInfo(String message) {
log(formatMessage("INFO", message));
}

default void logError(String message) {
log(formatMessage("ERROR", message));
}

default void logWarning(String message) {
log(formatMessage("WARNING", message));
}

// 私有方法:代码复用(Java 9+)
private String formatMessage(String level, String message) {
return String.format("[%s] %s - %s",
level,
java.time.LocalDateTime.now(),
message);
}

// 私有静态方法(Java 9+)
private static String getDefaultFormat() {
return "[%s] %s";
}
}

实际应用:设计模式

策略模式

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
// 策略接口
public interface PaymentStrategy {
boolean pay(double amount);
String getPaymentMethod();

default void printPaymentInfo(double amount) {
System.out.printf("使用%s支付%.2f元%n", getPaymentMethod(), amount);
}
}

// 具体策略实现
public class CreditCardPayment implements PaymentStrategy {
private String cardNumber;
private String holderName;

public CreditCardPayment(String cardNumber, String holderName) {
this.cardNumber = cardNumber;
this.holderName = holderName;
}

@Override
public boolean pay(double amount) {
printPaymentInfo(amount);
System.out.println("信用卡支付处理中...");
// 模拟支付处理
return amount > 0;
}

@Override
public String getPaymentMethod() {
return "信用卡(**** " + cardNumber.substring(cardNumber.length() - 4) + ")";
}
}

public class PayPalPayment implements PaymentStrategy {
private String email;

public PayPalPayment(String email) {
this.email = email;
}

@Override
public boolean pay(double amount) {
printPaymentInfo(amount);
System.out.println("PayPal支付处理中...");
return amount > 0;
}

@Override
public String getPaymentMethod() {
return "PayPal(" + email + ")";
}
}

// 上下文类
public class ShoppingCart {
private PaymentStrategy paymentStrategy;
private double totalAmount;

public void setPaymentStrategy(PaymentStrategy strategy) {
this.paymentStrategy = strategy;
}

public void addItem(String item, double price) {
totalAmount += price;
System.out.println("添加商品:" + item + " - ¥" + price);
}

public boolean checkout() {
if (paymentStrategy == null) {
System.out.println("请选择支付方式");
return false;
}

System.out.println("总金额:¥" + totalAmount);
return paymentStrategy.pay(totalAmount);
}
}

观察者模式

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
// 观察者接口
public interface Observer {
void update(String message);
String getObserverName();

default void showUpdateNotification(String message) {
System.out.printf("[%s] 收到更新:%s%n", getObserverName(), message);
}
}

// 主题接口
public interface Subject {
void attach(Observer observer);
void detach(Observer observer);
void notifyObservers(String message);
}

// 具体观察者
public class EmailSubscriber implements Observer {
private String email;

public EmailSubscriber(String email) {
this.email = email;
}

@Override
public void update(String message) {
showUpdateNotification(message);
System.out.println("发送邮件到:" + email);
}

@Override
public String getObserverName() {
return "邮件订阅者(" + email + ")";
}
}

public class SMSSubscriber implements Observer {
private String phoneNumber;

public SMSSubscriber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}

@Override
public void update(String message) {
showUpdateNotification(message);
System.out.println("发送短信到:" + phoneNumber);
}

@Override
public String getObserverName() {
return "短信订阅者(" + phoneNumber + ")";
}
}

// 具体主题
public class NewsPublisher implements Subject {
private List<Observer> observers;
private String latestNews;

public NewsPublisher() {
this.observers = new ArrayList<>();
}

@Override
public void attach(Observer observer) {
observers.add(observer);
System.out.println(observer.getObserverName() + " 已订阅新闻");
}

@Override
public void detach(Observer observer) {
observers.remove(observer);
System.out.println(observer.getObserverName() + " 已取消订阅");
}

@Override
public void notifyObservers(String message) {
System.out.println("=== 发布新闻 ===");
for (Observer observer : observers) {
observer.update(message);
}
}

public void publishNews(String news) {
this.latestNews = news;
notifyObservers(news);
}
}

最佳实践与设计原则

接口隔离原则

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
// 不好的设计:接口太大
interface BadWorker {
void work();
void eat();
void sleep();
void code();
void design();
void test();
}

// 好的设计:接口分离
interface Worker {
void work();
}

interface Eater {
void eat();
}

interface Sleeper {
void sleep();
}

interface Programmer extends Worker {
void code();
}

interface Designer extends Worker {
void design();
}

interface Tester extends Worker {
void test();
}

// 具体实现
public class SoftwareDeveloper implements Programmer, Eater, Sleeper {
private String name;

public SoftwareDeveloper(String name) {
this.name = name;
}

@Override
public void work() {
System.out.println(name + " 开始工作");
}

@Override
public void code() {
System.out.println(name + " 正在编程");
}

@Override
public void eat() {
System.out.println(name + " 正在吃饭");
}

@Override
public void sleep() {
System.out.println(name + " 正在睡觉");
}
}

总结

抽象类和接口是Java面向对象编程的高级特性,本文全面介绍了这两个重要概念:

  1. 抽象类基础:理解抽象类的定义、特点和使用方法
  2. 接口基础:掌握接口的概念、语法和实现机制
  3. 区别对比:明确抽象类和接口的差异和选择原则
  4. Java 8+新特性:了解默认方法、静态方法和私有方法
  5. 设计模式应用:学习策略模式和观察者模式的实现
  6. 最佳实践:掌握接口隔离原则等设计原则

抽象类适用于有共同实现的is-a关系,而接口适用于定义can-do能力的契约。合理使用抽象类和接口能够提高代码的可维护性、可扩展性和复用性,是编写高质量Java程序的重要技能。

参考资料

  1. Oracle Java Documentation - Abstract Classes and Interfaces
  2. Effective Java by Joshua Bloch
  3. Design Patterns: Elements of Reusable Object-Oriented Software
  4. Java核心技术卷I - 抽象类与接口
    abbrlink: 1