Java 抽象类与接口
抽象是面向对象编程的四大支柱之一。它旨在隐藏复杂的实现细节,只向用户暴露必要的功能。在 Java 中,抽象主要通过抽象类 (Abstract Class) 和接口 (Interface) 这两种机制来实现。
抽象类 (Abstract Class)
抽象类是一个不能被实例化的类,它充当其他类的父类(基类)。它用于定义一种类型的通用模板,其中可以包含一些已经实现的具体方法和一些必须由子类实现的抽象方法。
- 使用
abstract关键字来声明一个抽象类。 - 抽象类可以包含实例变量、构造方法、具体方法(有方法体)和抽象方法。
- 抽象方法:只有方法签名,没有方法体,使用
abstract关键字声明。任何继承了抽象类的子类,都必须实现其所有的抽象方法,除非该子类自己也是一个抽象类。
java
// 1. 定义一个抽象类
abstract class Shape {
private String color;
// 抽象类可以有构造方法,主要用于子类调用
public Shape(String color) {
this.color = color;
}
// 这是一个具体方法
public String getColor() {
return color;
}
// 这是一个抽象方法,没有方法体
public abstract double getArea();
}
// 2. 创建一个子类继承抽象类
class Circle extends Shape {
private double radius;
public Circle(String color, double radius) {
super(color); // 调用父类的构造方法
this.radius = radius;
}
// 3. 必须实现父类所有的抽象方法
@Override
public double getArea() {
return Math.PI * radius * radius;
}
}
public class Main {
public static void main(String[] args) {
// Shape myShape = new Shape("Red"); // 编译错误!不能实例化抽象类
Shape circle = new Circle("Blue", 5.0);
System.out.println("颜色: " + circle.getColor());
System.out.println("面积: " + circle.getArea());
}
}接口 (Interface)
接口是一种完全抽象的引用类型,它定义了一组方法(行为契约),任何实现了该接口的类都必须提供这些方法的具体实现。接口是实现多重继承的一种方式。
- 使用
interface关键字来声明一个接口。 - 接口中的所有方法默认都是
public abstract的(Java 8 之前)。 - 接口中的所有变量默认都是
public static final的(即常量)。 - 一个类使用
implements关键字来实现一个或多个接口。
java
// 1. 定义一个接口
interface Playable {
// public static final 会被自动添加
int MAX_VOLUME = 100;
// public abstract 会被自动添加
void play();
void stop();
}
// 2. 创建一个类实现接口
class MusicPlayer implements Playable {
@Override
public void play() {
System.out.println("音乐开始播放...");
}
@Override
public void stop() {
System.out.println("音乐停止。");
}
}
// 另一个类实现同一个接口
class VideoPlayer implements Playable {
@Override
public void play() {
System.out.println("视频开始播放...");
}
@Override
public void stop() {
System.out.println("视频停止。");
}
}Java 8+ 接口的增强
从 Java 8 开始,接口可以包含 default 方法和 static 方法,这为接口增加了更多的灵活性。
- 默认方法 (
defaultmethod): 允许在接口中提供一个方法的默认实现。实现该接口的类可以不重写此方法,直接使用默认版本。 - 静态方法 (
staticmethod): 属于接口本身,只能通过接口名调用。
java
interface Loggable {
// 抽象方法
void log(String message);
// 默认方法
default void logInfo(String message) {
log("[INFO] " + message);
}
// 静态方法
static String getLoggerName() {
return "DefaultLogger";
}
}抽象类 vs. 接口
| 特性 | 抽象类 (Abstract Class) | 接口 (Interface) |
|---|---|---|
| 目的 | 定义一种类型的通用模板,实现代码复用 | 定义一种行为契约,规范类的能力 |
| 继承关系 | 子类使用 extends 关键字,Java 中单继承 | 实现类使用 implements 关键字,一个类可以实现多个接口 |
| 成员变量 | 可以包含任何类型的成员变量(实例变量、静态变量) | 只能包含 public static final 常量 |
| 构造方法 | 可以有构造方法 | 不能有构造方法 |
| 方法 | 可以包含抽象方法和具体方法 | Java 8 前只能有抽象方法,之后可以有 default 和 static 方法 |
| 关系 | “is-a” (是一个) 关系,强调本质归属 | “has-a” (有一个) 或 “can-do” (能做) 关系,强调能力 |
何时使用?
- 使用抽象类: 当你想创建一个基类,它包含了一些子类共享的通用代码和状态时。当类之间存在明显的层级和“is-a”关系时(例如
Dogis anAnimal)。 - 使用接口: 当你想定义一个角色或能力,而这个能力可以被不同层级的类所拥有时。当你希望一个类能拥有多种不相关的行为时(例如一个
Bird类可以同时实现Flyable和Singable接口)。