抽象类用于定义类的骨架并共享状态和逻辑,接口用于声明行为契约;抽象类支持可变状态、构造函数和非final方法,接口仅支持静态常量和无状态的default方法。

抽象类和接口根本不是同一类东西
抽象类是“类”的变体,本质仍是类;接口是“行为契约”,本质是能力声明。别被语法相似性骗了——你写 abstract class 是在定义一类事物的骨架,而写 interface 是在说“谁想干这事,就得按这个签名来”。选错就等于设计跑偏:用接口强行塞状态,或用抽象类去拼凑不相关的功能,后期改起来全是坑。
什么时候必须用抽象类,而不是接口
当你需要共享可变状态、提供带逻辑的默认实现、或控制子类初始化流程时,只能选抽象类。接口无法持有实例变量,也不能定义 protected 或 private 方法,更没有构造函数。
- 要复用字段(比如
protected String name)或非 final 的常量 → 只能放抽象类里 - 子类必须走统一初始化路径(比如强制校验 ID 格式)→ 抽象类有构造方法,接口没有
- 想给子类留一个“可覆盖但非强制”的默认行为(如
logOperation())→ 抽象类里写普通方法就行;接口得用default,但该方法不能访问实例状态 - 框架基类(如 Spring 的
InitializingBean是接口,但AbstractController是抽象类)→ 有状态管理需求时,抽象类更自然
什么时候必须用接口,而不是抽象类
当你需要让不相关的类具备同一组能力、要求多继承语义、或对外暴露稳定契约时,接口是唯一选择。Java 不支持多继承,但一个类可以 implements 多个接口。
- 策略切换(如
PaymentStrategy)、事件回调(如OnClickListener)→ 接口天然支持运行时替换,抽象类做不到 - 多个维度正交扩展(比如一个类既要
Serializable,又要Comparable,还要Cloneable)→ 只能靠接口组合 - API 设计对外暴露行为,不暴露实现细节(如 JDK 的
Collection、Stream)→ 接口更轻量、更稳定;抽象类一旦加字段,就是二进制不兼容 - JDK 8+ 虽然允许接口有
default和static方法,但它们不能访问this,无法替代抽象类中依赖实例状态的方法
最容易踩的三个坑
很多团队线上出问题,不是语法不会,而是没想清“抽象类管‘是什么’,接口管‘能做什么’”这层语义边界。
立即学习“Java免费学习笔记(深入)”;
- 在接口里塞
public static final int MAX_RETRY = 3看似方便,但一旦值要变,所有实现类都得重新编译 → 改成抽象类里的protected static final更可控 - 为图省事,把本该拆成多个小接口的功能(如
read()、write()、delete())全塞进一个大接口 → 违反接口隔离原则,强迫实现类实现无用方法 - 抽象类里写了
public abstract void init(),又在构造函数里调它 → 子类对象还没构造完,this不完整,极易引发 NPE 或未定义行为
真正难的从来不是语法,而是判断某个功能属于“身份继承”还是“能力授权”。多问一句“这个东西是它的本质,还是它能干的一件事”,比死记区别有用得多。










