核心思想:蓝图与建筑
- 接口:就像一份“功能需求说明书”,它只规定了“能做什么”(有哪些功能),但不规定“怎么做”(具体实现),一份说明书要求“产品必须能飞”和“产品必须能发声”,但没说怎么飞,怎么发声。
- 抽象类:就像一份“半成品设计方案”,它既有一些已经实现好的、通用的部分(产品都有名字和重量”),也包含一些需要子类去具体实现的部分(如何飞行”),它提供了一个基础模板。
- 实现类:就是最终的、具体的建筑,它根据“功能需求说明书”(接口)和“半成品设计方案”(抽象类),建造出真正可以使用的、能飞能叫的无人机或者飞机。
接口
接口是 Java 中一种引用类型,它像一份契约,定义了一组方法的规范,但不提供具体实现。

主要特点:
- 纯抽象:在 Java 8 之前,接口里的方法默认是
public abstract的,不能有方法体,从 Java 8 开始,接口可以包含default方法和static方法(可以有方法体)。 - 常量:接口中可以定义常量,默认是
public static final的。 - 多实现:一个类可以实现多个接口,这弥补了 Java 单继承的不足,是实现多态的重要方式。
- 解耦:通过接口,调用方(客户端)只关心接口,而不关心具体的实现类,这使得代码的耦合度降低,更易于扩展和维护。
代码示例:
// 1. 定义一个接口 - 功能需求说明书
public interface Flyable {
// 抽象方法,规定所有能飞的东西都必须实现这个方法
void fly();
// Java 8+ 可以有默认方法,提供默认实现
default void takeOff() {
System.out.println("准备起飞...");
}
}
// 2. 定义另一个接口
public interface Singable {
void sing();
}
// 3. 定义一个实现类 - 飞机,它同时实现了两个接口
public class Airplane implements Flyable, Singable {
@Override
public void fly() {
System.out.println("飞机通过引擎和机翼高速飞行。");
}
@Override
public void sing() {
System.out.println("飞机发出轰鸣的歌声。");
}
}
// 4. 另一个实现类 - 鸟,也实现了这两个接口
public class Bird implements Flyable, Singable {
@Override
public void fly() {
System.out.println("鸟通过扇动翅膀在天空翱翔。");
}
@Override
public void sing() {
System.out.println("鸟儿唱着悦耳的歌。");
}
}
// 5. 使用
public class Main {
public static void main(String[] args) {
// 使用接口类型引用对象,体现了多态
Flyable airplane = new Airplane();
Flyable bird = new Bird();
airplane.fly(); // 输出: 飞机通过引擎和机翼高速飞行。
airplane.takeOff(); // 输出: 准备起飞... (使用了默认方法)
bird.fly(); // 输出: 鸟通过扇动翅膀在天空翱翔。
bird.takeOff(); // 输出: 准备起飞... (使用了默认方法)
// 如果想调用 sing() 方法,需要使用 Singable 类型
Singable singingBird = new Bird();
singingBird.sing(); // 输出: 鸟儿唱着悦耳的歌。
}
}
抽象类
抽象类是不能被实例化的类,它作为基类,为子类提供一个共同的模板。
主要特点:
- 不能实例化:你不能直接创建
new AbstractClass(),它必须被继承。 - 包含抽象方法:至少包含一个
abstract方法(没有方法体的方法),子类必须实现所有父类的抽象方法,否则子类也必须被声明为抽象类。 - 包含具体方法:可以包含有具体实现的方法(非抽象方法),子类可以直接继承使用这些方法,也可以重写它们。
- 有构造方法:抽象类可以有构造方法,用于在创建子类实例时初始化抽象类的成员变量。
- 单继承:一个类只能继承一个抽象类(因为 Java 只支持单继承)。
代码示例:
// 1. 定义一个抽象类 - 半成品设计方案
public abstract class Animal {
// 成员变量
protected String name;
// 构造方法,用于初始化子类共有的属性
public Animal(String name) {
this.name = name;
}
// 抽象方法,所有动物都必须有吃东西的行为,但方式不同
public abstract void eat();
// 具体方法,所有动物都会睡觉,实现方式相同
public void sleep() {
System.out.println(name + " 正在睡觉。");
}
}
// 2. 定义一个实现类 - 具体的动物
public class Dog extends Animal {
public Dog(String name) {
super(name); // 调用父类的构造方法
}
@Override
public void eat() {
System.out.println(name + " 正在啃骨头。");
}
}
// 3. 另一个实现类
public class Cat extends Animal {
public Cat(String name) {
super(name); // 调用父类的构造方法
}
@Override
public void eat() {
System.out.println(name + " 正在吃鱼。");
}
}
// 4. 使用
public class Main {
public static void main(String[] args) {
// Animal animal = new Animal("Tom"); // 编译错误!不能实例化抽象类
Dog dog = new Dog("旺财");
Cat cat = new Cat("咪咪");
dog.eat(); // 输出: 旺财 正在啃骨头。
dog.sleep(); // 输出: 旺财 正在睡觉。
cat.eat(); // 输出: 咪咪 正在吃鱼。
cat.sleep(); // 输出: 咪咪 正在睡觉。
}
}
实现类
实现类是具体、完整的类,它通过 implements 关键字实现接口,或通过 extends 关键字继承抽象类,并提供所有未实现的方法的具体逻辑。
主要特点:
- 具体化:它是一个完整的、可被实例化的类。
- 实现接口:使用
implements关键字,必须实现接口中的所有抽象方法(除非自身是抽象类)。 - 继承抽象类:使用
extends关键字,必须实现父类中的所有抽象方法(除非自身是抽象类)。 - 可以扩展:可以在实现类中添加自己的新属性和方法。
在上面的 Airplane, Bird, Dog, Cat 的例子中,它们都是实现类。
核心区别与选择:何时使用?
这是一个非常重要的问题,用错了会导致设计上的问题。

| 特性 | 接口 | 抽象类 |
|---|---|---|
| 继承/实现 | implements (实现),一个类可实现多个接口 |
extends (继承),一个类只能继承一个抽象类 |
| 方法 | Java 8+ 可包含 default, static 方法和抽象方法 |
可包含抽象方法和具体方法 |
| 变量 | 只能是 public static final 常量 |
可以是各种类型的成员变量 |
| 构造方法 | 没有 | 有,用于子类调用 |
| 目的 | 定义能力/规范,回答“能做什么?” | 定义本质/模板,回答“是什么?” |
| 灵活性 | 高,解耦,支持多实现 | 低,耦合度高,但结构更清晰 |
选择指南
什么时候应该使用接口?
- 定义规范/契约:当你想定义一个类应该具备哪些功能时,而不关心它是什么。
List,Set,Map都定义了集合的通用操作。 - 实现多态:当一个类需要具备多种不同的能力时,一个
Duck类既需要Flyable,也需要Swimmable。 - 解耦:当你希望调用方只依赖于接口,而不是具体的实现时,这样未来可以轻松替换实现类而不影响调用方代码。
什么时候应该使用抽象类?
- 代码复用:当你想让多个子类共享一部分相同的具体代码时。
Animal抽象类中的sleep()方法可以直接被所有子类继承。 - 定义模板:当你想创建一个基类,为所有子类提供一个通用的结构和骨架时,子类在此基础上进行扩展和修改。
- “是一个”的关系:当子类和父类之间存在非常强的“是一个...”的继承关系时。
Dog是一个Animal。
经典场景:接口与抽象类结合使用
这是最强大、最灵活的设计模式。
场景:我们想设计一个 Bird(鸟)。
- 定义本质(抽象类):所有鸟都是“动物”,有名字,会睡觉,我们可以用
Animal抽象类来定义这个本质。 - 定义能力(接口):但不是所有鸟都会飞(比如鸵鸟),也不是所有鸟都会唱歌,我们可以用
Flyable和Singable接口来定义这些可选的能力。
代码实现:
// 抽象类:定义鸟的本质
public abstract class Bird extends Animal { // Bird 本质上是一种 Animal
public Bird(String name) {
super(name);
}
// 鸟有一个共有的行为,但不是所有动物都有
public void layEggs() {
System.out.println(name + " 下蛋了。");
}
}
// 接口:定义飞的能力
interface Flyable { void fly(); }
// 接口:定义唱歌的能力
interface Singable { void sing(); }
// 实现类1:麻雀,会飞,会唱歌
public class Sparrow extends Bird implements Flyable, Singable {
public Sparrow(String name) {
super(name);
}
@Override
public void eat() {
System.out.println(name + " 在吃虫子。");
}
@Override
public void fly() {
System.out.println(name + " 在天空自由飞翔。");
}
@Override
public void sing() {
System.out.println(name + " 在叽叽喳喳地唱歌。");
}
}
// 实现类2:鸵鸟,不会飞,但会唱歌(或者别的)
public class Ostrich extends Bird { // 鸵鸟不会飞,所以不实现 Flyable
public Ostrich(String name) {
super(name);
}
@Override
public void eat() {
System.out.println(name + " 在吃草。");
}
// 鸵鸟有自己的叫声,可以理解为一种特殊的“唱歌”
public void makeSound() {
System.out.println(name + " 发出低沉的叫声。");
}
}
// 使用
public class Main {
public static void main(String[] args) {
Sparrow sparrow = new Sparrow("小麻雀");
Ostrich ostrich = new Ostrich("鸵鸟先生");
sparrow.eat(); // 继承自 Animal
sparrow.sleep(); // 继承自 Animal
sparrow.layEggs(); // 继承自 Bird
sparrow.fly(); // 实现 Flyable
sparrow.sing(); // 实现 Singable
System.out.println("-----");
ostrich.eat(); // 继承自 Animal
ostrich.sleep(); // 继承自 Animal
ostrich.layEggs(); // 继承自 Bird
// ostrich.fly(); // 编译错误!Ostrich 没有 fly() 方法
ostrich.makeSound(); // 自己的方法
}
}
- 接口:定义“能做什么”,是行为的契约,一个类可以实现多个接口,非常灵活。
- 抽象类:定义“是什么”,是事物的模板,提供代码复用,一个类只能继承一个抽象类。
- 实现类:是最终的、具体的类,它“能做什么”和“是什么”都通过实现接口和继承抽象类来体现。
理解这三者的区别和联系,是迈向高级 Java 开发者的关键一步,蓝图与建筑”的比喻,能帮助你更好地在实际项目中做出正确的选择。

