抽象类与接口
约 1198 字大约 4 分钟
布欧-Lewyon
2026-05-14
抽象类与接口是 Java 用于抽象层次的两种手段。学会区分它们各自的适用场景,有助于写出扩展性强、耦合度低的设计。
抽象类
当一组类有共同的字段和部分方法实现,但又不足以给出全部方法的完整实现时,抽象类是最合适的选择。用 abstract 修饰的类称为抽象类,它不能直接实例化,但可以包含抽象方法(只有签名、没有方法体,子类必须实现),也可以包含普通方法与字段。
以下定义图形层次:Shape 抽象类提供了 color 字段和 getColor() 的完整实现,但 area() 无法在父类中确定具体算法,因此声明为抽象方法,强制子类各自实现:
public abstract class Shape {
protected String color;
public Shape(String color) {
this.color = color;
}
// 抽象方法:子类必须实现
public abstract double area();
// 普通方法:子类可直接使用或重写
public String getColor() {
return color;
}
}下面的 Circle 继承 Shape,并实现了 area()——注意 super(color) 调用抽象类构造器,这在抽象类的子类中是合法的:
public class Circle extends Shape {
private double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
}- 抽象类不能直接实例化:
new Shape("red")编译报错。 - 若有抽象方法,子类非抽象子类必须实现所有抽象方法,否则子类也得声明为
abstract。 - 抽象类可以有构造器,虽然不能
new出来,但子类构造时会通过super()调用。
接口(Interface)
接口定义一组方法契约,实现类必须兑现这些方法。Java 8 之后接口能力大幅增强。
基本声明与实现
public interface Flyable {
void fly(); // 默认是 public abstract
}
public interface NoiseMaker {
void makeNoise();
}用 implements 实现接口,一个类可实现多个接口:
public class Airplane implements Flyable, NoiseMaker {
@Override
public void fly() {
System.out.println("飞机起飞");
}
@Override
public void makeNoise() {
System.out.println("轰隆隆");
}
}接口中的默认方法与静态方法(Java 8+)
默认方法允许在接口中提供方法体,实现类可选择重写或不重写:
public interface Flyable {
void fly();
default void preFlightCheck() {
System.out.println("默认:飞行前检查完成");
}
}如果类实现的多个接口有签名相同的默认方法,必须显式重写解决冲突:
public class Bird implements Flyable, NoiseMaker {
// 必须重写,否则编译报错
@Override
public void preFlightCheck() {
Flyable.super.preFlightCheck(); // 可选择调用某接口的默认实现
}
}静态方法属于接口本身,通过 接口名.方法名 调用,与工具方法类似:
public interface Flyable {
static boolean isAirborne(double altitude) {
return altitude > 0;
}
}
// 调用:Flyable.isAirborne(100)接口中还可以放什么
public static final常量(即使不写这些修饰符也是)。- Java 14+ 允许
record定义在接口内部(作为嵌套类型)。 - 不能有实例字段、构造器或非
public的方法(Java 9 允许private辅助方法给默认方法用)。
抽象类 vs 接口:怎么选
| 维度 | 抽象类 | 接口 |
|---|---|---|
| 状态字段 | 可以有 | 不能(只能常量) |
| 构造器 | 有 | 无 |
| 多继承 | 不支持(单继承) | 一个类可实现多个接口 |
| 方法类型 | 抽象 + 普通方法 | 抽象 + 默认 + 静态 |
| 语义 | "是什么"(is-a) | "能做什么"(can-do) |
经验法则:
- 如果多个类共享状态或构造器逻辑,用抽象类。
- 如果侧重于行为契约且可能被不同层次类实现,用接口。
- 尽量优先走接口,需要公共字段/构造器时再考虑抽象类。
小结
- 抽象类(
abstract)可包含字段、构造器、普通方法和抽象方法;子类必须实现所有抽象方法,否则也需声明为abstract。 - 接口(
interface)定义行为契约;Java 8+ 提供default方法和static方法减少实现类样板代码。 - 类可
implements多个接口(弥补单继承局限),签名冲突的默认方法必须显式重写。 - 选择建议:需共享状态/构造器逻辑 → 抽象类;纯行为约定 → 接口;日常优先走接口。
- 易错:接口中的字段默认是
public static final,即使不写也是常量,不要误以为可以修改。接口中的default方法如果与父类中的某个方法签名相同,父类方法优先(类优先原则),不会产生冲突。 - 思考任务:定义一个
Swimmable接口和一个Walkable接口,分别包含move()默认方法。创建一个Duck类实现两者,观察编译器对冲突的处理。
上一节:继承与多态
下一节:枚举
