Java中的接口(Interface)与抽象类(Abstract Class)

前言

接口与抽象,Java中绕不开的主题,但是又总感觉二者之间有些东西模模糊糊没有理清,今天来认真整理一下。

一、基本原理

接口

纯行为契约,定义方法的签名 (不包含实现)。Java 8+支持默认方法(default)和静态方法。

实现多继承行为:类可同时实现多个接口

强制统一规范 :不同类的相同行为标准化(如Comparable)

解耦:声明与实现分离(依赖倒置原则)

抽象类

可包含部分实现的模板,允许定义抽象方法(无实现)、具体方法、成员变量和构造器。

代码复用:为继承体系提供公共实现(如模板方法模式)

状态共享:可在基类中封装共享状态(字段)

控制扩展:定义必须由子类实现的约束

核心差异

特性

接口

抽象类

实例字段

❌(Java 17仅支持常量)

构造方法

方法实现

Java 8+支持默认/静态方法

多重继承

✅(多个接口)

❌(单继承)

设计目的

"能做什么"(行为)

"是什么"(层次结构)

二、普遍用法

1. 定义行为契约(接口)

java

复制代码

// 接口定义支付行为

interface PaymentProcessor {

// 抽象方法

void processPayment(double amount);

// Java 8 默认方法(可选实现)

default void validatePayment(double amount) {

if (amount <= 0) throw new IllegalArgumentException("无效金额");

}

}

// 具体实现类

class CreditCardProcessor implements PaymentProcessor {

@Override

public void processPayment(double amount) {

validatePayment(amount); // 调用默认方法

System.out.println("信用卡支付: " + amount);

}

}

2. 复用公共逻辑(抽象类)

java

复制代码

// 抽象类提供模板方法

abstract class AbstractDatabaseClient {

// 共享状态

private final String connectionUrl;

// 构造器注入

public AbstractDatabaseClient(String url) {

this.connectionUrl = url;

initializeConnection();

}

// 具体方法(复用)

private void initializeConnection() {

System.out.println("初始化DB连接: " + connectionUrl);

}

// 抽象方法(子类必须实现)

public abstract void executeQuery(String sql);

}

// 具体子类

class MySQLClient extends AbstractDatabaseClient {

// 通过构造方法注入参数

public MySQLClient(String url) { super(url); }

@Override

public void executeQuery(String sql) {

System.out.println("执行MySQL查询: " + sql);

}

}

⚠️ 版本差异说明

Java 8+ :接口支持default/static方法

Java 9+ :接口支持private方法(代码复用)

Java 17+ :sealed接口/抽象类控制继承

java

复制代码

// Java 17 sealed接口示例

public sealed interface PaymentProcessor

permits CreditCardProcessor, CryptoProcessor { ... }

三、设计原则

优先选择接口(符合ISP原则)

java

复制代码

// 细粒度接口设计(避免God Interface)

interface Loggable { void log(); }

interface Cacheable { void cache(); }

抽象类用于模板方法

java

复制代码

abstract class TemplateService {

// 固定算法骨架

public final void execute() {

preProcess();

doBusinessLogic(); // 抽象钩子

postProcess();

}

protected abstract void doBusinessLogic();

}

✅ 总结决策树

flowchart TD

A[需要多继承?] -- Yes --> B(选接口)

A -- No --> C{需要共享状态或公共实现?}

C -- Yes --> D(选抽象类)

C -- No --> E{需要定义行为契约?}

E -- Yes --> B

E -- No --> F(可能不需要接口/抽象类)四、避坑

⚠️ 坑1:接口默认方法冲突

问题:多个接口的同名默认方法引发冲突

java

复制代码

interface A { default void foo() {} }

interface B { default void foo() {} }

class C implements A, B {} // 编译错误!

解决:在实现类中强制重写:

java

复制代码

class C implements A, B {

@Override

public void foo() {

A.super.foo(); // 显式选择实现

}

}

⚠️ 坑2:抽象类构造器陷阱

问题:子类忘记调用超类构造器导致状态未初始化

java

复制代码

abstract class AbstractResource {

private String resourceId;

protected AbstractResource(String id) { this.resourceId = id; }

}

class SubResource extends AbstractResource {

// 未调用super(id) -> NullPointerException!

}

解决:

IDE配置@RequiredArgsConstructor(Lombok)

代码审查强制验证

⚠️ 坑3:扩展性破坏

问题:向接口添加新方法导致遗留实现类报错

规避:

Java 8+:优先用default方法(向后兼容)

发布新接口V2(如PaymentProcessorV2)

⚠️ 坑4:状态泄漏(抽象类特有)

问题:子类错误修改抽象类共享状态:

java

复制代码

abstract class AbstractSession {

protected List sessions = new ArrayList<>();

}

class UserSession extends AbstractSession {

public void clear() { sessions.clear(); } // 影响所有子类实例!

}

规避:

字段设为private + 提供受保护访问器

文档明确状态所有权

结语

黄金法则:

行为定义必选接口(特别是公共API)

纵向复用代码选抽象类(如领域模型基类)

Java 17+优先使用sealed限制继承(安全控制)