优先选抽象类当子类属同类事物且需共享状态或逻辑,选接口当不相关类需统一能力;抽象类支持状态、构造器和私有方法,接口侧重契约解耦与多实现。

抽象类和接口到底该选哪个?看继承关系和复用需求
选抽象类还是接口,第一眼就该看类的继承结构:如果子类天然属于同一类事物(比如 Animal 下的 Dog、Cat),且需要共享状态(如 name、age)或复用逻辑(如 sleep() 默认实现),优先用抽象类;如果只是想让不相关的类具备某种能力(比如 USBDevice 和 BluetoothSpeaker 都能 connect()),那就用接口。
- 一个类只能
extends一个抽象类,但可以implements多个接口——这是硬性限制,不是风格选择 - 抽象类能有
protected成员变量、private方法、构造器,接口里连private方法都只能从 JDK 9 起用private关键字显式声明(且仅限默认方法内部调用) - 别为了“看起来更抽象”而把本该是抽象类的基类强行改成接口——比如你发现子类都要持有一个
logger实例并共用logError(String),这时接口无法存实例变量,抽象类才是正解
JDK 8+ 后接口也能写实现?别被 default 方法带偏了
default 方法确实让接口能提供行为实现,但它不是用来替代抽象类的。它的设计初衷是「向后兼容」:给已有接口新增方法时,不强制所有实现类立刻改代码。
-
default方法不能访问实例变量(接口没实例变量),所以没法做状态管理;而抽象类里的具体方法可以直接操作this.name - 多个接口有同名
default方法时,实现类必须重写该方法,否则编译报错:class X inherits unrelated defaults for method Y from types A and B - 别在接口里堆
default方法来模拟模板逻辑——比如把process()拆成read()、validate()、save()全设为default,这会让实现类失去控制权,也违背接口“契约优先”的本意
什么时候非得用抽象类?三个典型信号
当你遇到以下任一情况,基本可以确定抽象类比接口更合适:
- 子类需要共享可变状态:比如所有
DatabaseConnection子类都需要一个ConnectionPool引用,抽象类里直接声明protected ConnectionPool pool,接口做不到 - 构造过程需统一初始化:比如抽象类
ReportGenerator的构造器里固定调用initTemplateEngine()和loadConfig(),子类通过super()继承这套流程;接口没有构造器 - 存在“模板方法模式”骨架:像
public final void execute() { before(); doWork(); after(); }这种流程不可变、步骤可扩展的场景,抽象类是唯一自然的选择
接口不是“更高级的抽象类”,它解决的是另一类问题
接口的核心价值从来不是“多实现”,而是解耦——它让调用方只依赖行为契约,不关心是谁实现的。比如 java.util.List 是接口,ArrayList 和 LinkedList 完全无关,但都能被 Collections.sort() 消费,因为它们都承诺了 get(int) 和 size() 行为。
- 别在接口里定义
id、createdAt这种实体字段——那是抽象类或基类干的事;接口只该出现save()、isValid()这类动词 - 一个类实现多个接口(如
Runnable+Serializable+Comparable)很常见,但若发现这些接口总是一起出现,可能暗示你漏掉了一个更高层的抽象类 - 最容易被忽略的一点:接口的演化成本远高于抽象类。加一个抽象方法到接口,所有实现类立刻编译失败;而抽象类加一个
protected方法,完全不影响子类编译
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











