Skip to content

抽象类 vs 接口

刚学 Java 的时候,很多人都会被这个问题困扰:抽象类和接口到底用哪个?

其实只要记住一个原则:抽象类是「是什么」,接口是「能做什么」

本质区别

对比项抽象类接口
关键字abstract classinterface
继承单继承多实现
构造方法可以有不能有
方法抽象 + 普通都可以JDK 7 前只能是抽象方法
字段无限制只能是常量 (static final)
访问修饰符任意默认 public
静态方法可以有JDK 8+ 可以有
用途「是什么」「能做什么」

什么时候用抽象类

建立 is-a 关系

当你在描述「X 是一种 Y」时,用抽象类:

java
// Dog 是一种 Animal
abstract class Animal {
    String name;
    abstract void eat();
}

class Dog extends Animal {
    @Override
    void eat() {
        System.out.println("狗在吃狗粮");
    }
}

需要共享代码

当多个子类需要共享一些通用实现时,用抽象类:

java
abstract class Employee {
    protected String name;
    protected double baseSalary;

    // 所有员工都要打卡
    public void clockIn() {
        System.out.println(name + "打卡上班");
    }

    // 计算工资:子类必须实现
    public abstract double calculateSalary();
}

class FullTimeEmployee extends Employee {
    @Override
    public double calculateSalary() {
        return baseSalary + 1000;
    }
}

需要非 public 成员

如果需要 protectedprivate 或包级访问的成员,只能用抽象类:

java
abstract class Base {
    protected void helperMethod() { }  // 只有子类能用
    private void privateHelper() { }   // 内部辅助方法
}

什么时候用接口

建立 can-do 关系

当你在描述「X 能做什么」时,用接口:

java
// Bird 能飞,Airplane 也能飞
interface Flyable {
    void fly();
}

class Bird implements Flyable {
    @Override
    public void fly() {
        System.out.println("鸟儿飞翔");
    }
}

class Airplane implements Flyable {
    @Override
    public void fly() {
        System.out.println("飞机飞翔");
    }
}

需要多继承

当一个类需要「继承」多个不相关的行为时,用接口:

java
interface Runnable {
    void run();
}

interface Swimmable {
    void swim();
}

// Duck 能跑又能游
class Duck implements Runnable, Swimmable {
    @Override
    public void run() { }

    @Override
    public void swim() { }
}

声明能力,不关心实现

接口只关心「能做什么」,不关心「怎么做」:

java
// 只关心:能比较
interface Comparable<T> {
    int compareTo(T other);
}

// 具体怎么比,让实现类决定
class Student implements Comparable<Student> {
    int score;

    @Override
    public int compareTo(Student other) {
        return this.score - other.score;
    }
}

实际案例对比

用抽象类:动物系统

java
// 所有动物有共同属性和行为
abstract class Animal {
    protected String name;
    protected int age;

    // 通用实现:睡觉
    public void sleep() {
        System.out.println(name + "在睡觉");
    }

    // 抽象方法:吃的方式不同
    public abstract void eat();
}

// Dog is an Animal
class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗在吃狗粮");
    }
}

// Cat is an Animal
class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫在吃猫粮");
    }
}

用接口:能力系统

java
// 可以飞
interface Flyable {
    void fly();
}

// 可以游泳
interface Swimmable {
    void swim();
}

// 可以被驯养
interface Domesticable {
    void beDomesticated();
}

// 企鹅:不会飞,能游泳,能被驯养
class Penguin implements Swimmable, Domesticable {
    @Override
    public void swim() { }

    @Override
    public void beDomesticated() { }
}

// 鹦鹉:会飞,能被驯养
class Parrot implements Flyable, Domesticable {
    @Override
    public void fly() { }

    @Override
    public void beDomesticated() { }
}

选型决策树

需要继承多个不相关的契约?
    是 → 用接口
    否 ↓
有明确的「是」关系吗?
    是 → 用抽象类
    否 ↓
需要共享代码吗?
    是 → 用抽象类
    否 → 用接口

进阶:两者结合

实际开发中,经常会看到两者结合使用:

java
// 抽象类:定义骨架
abstract class Animal {
    protected String name;

    public void sleep() { }  // 通用行为

    public abstract void eat();  // 子类实现
}

// 接口:定义扩展能力
interface Flyable {
    void fly();
}

interface Swimmable {
    void swim();
}

// 具体实现
class Dog extends Animal {
    @Override
    public void eat() { }
}

class Duck extends Animal implements Flyable, Swimmable {
    @Override
    public void eat() { }

    @Override
    public void fly() { }

    @Override
    public void swim() { }
}

总结

抽象类 = 父亲:给你一些东西,也规定你必须做什么
接口   = 合同:我不在乎你怎么实现,只在乎你能做到
场景选择
建立类层次抽象类
多重能力组合接口

基于 VitePress 构建