Java 接口与抽象类的区别

更新于 2025-12-27

Jakob Jenkov 2015-03-09

我经常被问到的问题是:Java 中接口(Interface)和抽象类(Abstract Class)有什么区别?什么时候该用哪一个?由于我已经多次通过电子邮件回答这个问题,因此决定撰写这篇关于 Java 接口与抽象类对比的教程。

接口的作用

Java 接口用于将某个组件的接口与其实现解耦。换句话说,使用该接口的类可以独立于实现该接口的类。这样,你就可以在不修改使用方代码的前提下,更换接口的具体实现。

抽象类的作用

抽象类通常被用作子类扩展的基础类。某些编程语言使用抽象类来实现多态性,并分离接口与实现;但在 Java 中,这项任务是由接口完成的。

请记住:一个 Java 类只能有一个直接父类,但可以实现多个接口。因此,如果一个类已经继承了另一个类(即已有超类),它就无法再继承另一个抽象类,但仍然可以实现接口。从这个角度看,接口是一种更灵活的机制,用于暴露公共行为

使用建议

  • 如果你需要将接口与其实现分离,请使用接口
  • 如果你还需要提供一个基础类或接口的默认实现,则可以添加一个抽象类(或普通类)来实现该接口。

下图展示了一个典型结构:

graph Class --> Interface AbstractClass --o Interface Subclass -.extends.-> AbstractClass style Class fill:#a0d3e8,stroke:#333,stroke-width:2px style Interface fill:#d5f5e3,stroke:#333,stroke-width:2px style AbstractClass fill:#ffe4b5,stroke:#f4a460,stroke-width:2px,stroke-dasharray: 5 5 style Subclass fill:#ffe4b5,stroke:#f4a460,stroke-width:2px

蓝色类引用接口。抽象类实现了该接口,而子类继承自抽象类。

代码示例

以下是来自 Java 抽象类 教程中的代码示例,但额外添加了一个由抽象基类实现的接口,使其与上图结构一致。

第一步:定义接口

public interface URLProcessor {
    public void process(URL url) throws IOException;
}

第二步:定义抽象基类(实现接口)

public abstract class URLProcessorBase implements URLProcessor {
    public void process(URL url) throws IOException {
        URLConnection urlConnection = url.openConnection();
        InputStream input = urlConnection.getInputStream();
        try {
            processURLData(input);
        } finally {
            input.close();
        }
    }

    protected abstract void processURLData(InputStream input) throws IOException;
}

第三步:定义具体子类(继承抽象类)

public class URLProcessorImpl extends URLProcessorBase {
    @Override
    protected void processURLData(InputStream input) throws IOException {
        int data = input.read();
        while(data != -1){
            System.out.println((char) data);
            data = input.read();
        }
    }
}

第四步:使用接口作为变量类型(实际创建的是子类实例)

URLProcessor urlProcessor = new URLProcessorImpl();
urlProcessor.process(new URL("https://mianshima.com"));

灵活性优势

同时使用接口和抽象基类可以让你的代码更加灵活:

  • 对于简单的 URL 处理器,可以直接继承 URLProcessorBase 抽象类;
  • 如果需要更复杂或不同的行为,也可以选择直接实现 URLProcessor 接口,而不继承 URLProcessorBase