跳转至

接口和抽象类的区别,如何选择?

接口和抽象类这个问题,设计模式里其实常用一句话概括:接口定义“能做什么”,抽象类定义“是什么,并帮你做一部分”。我习惯用“日志框架”这个场景来解释,因为很多开源库(如SLF4J)就是这么设计的。

下面这张图展示了一个典型的配合方式:接口制定日志规范,抽象类提供通用的格式化模板,具体实现只关注写到哪里。

deepseek_mermaid_20260612_32178c.png

最核心的区别

  • 方法实现:接口里只能有抽象方法(Java 8后default不算),告诉你必须做什么;抽象类可以有普通方法、成员变量,能把你“喂”到只剩最后一公里。

  • 构造函数与状态:抽象类可以有构造器、成员变量,代表一个对象的内在状态;接口不能有实例字段。

  • 继承限制:一个类能实现多个接口(“可以扮演多种角色”),但只能继承一个抽象类(“你只能有一个亲爹”)。

  • 设计意图:接口是 “行为契约” ,强调跨层级的能力(比如可序列化、可比较);抽象类是 “代码复用” ,把子类的公共部分上提(比如模板方法模式)。


怎么选?我一般按这个优先级

  1. 需要多态又不共享代码 → 用接口 比如策略模式、回调函数,只是约定一个行为,具体实现八仙过海。

  2. 既有公共行为,又想强制子类差异 → 抽象类实现接口 就像上图,接口保证外部调用统一,抽象类挖好流程框架(模板方法),子类只填关键步骤。

  3. 只是一些无关紧要的默认实现 → 接口的default方法 Java 8 之后可以直接在接口里写default方法,但它不能有状态,只是纯逻辑。如果默认实现需要访问成员变量,立刻转抽象类。

  4. 想限制继承链 → 抽象类 如果你明确这是一个BaseControllerBaseEntity,所有子类天生就该是这个类型,用抽象类来表达is-a关系。

实际开发中我很少纠结,因为经常是“接口先上,发现多个实现有重复代码时,抽一个抽象类出来实现接口”。重构的成本很低,没必要一开始就把继承体系设计得过深。