第19篇:Java接口的作用和意义


📌 系列导航《Java 100 天进阶之路》完整目录 |
⬅️ 上一篇:第18篇:Java接口和抽象类的异同,default关键字 |
➡️ 下一篇:第20篇:Java初始化、构造器、对象创建的过程


一、核心知识点

  • 接口的定义:一组方法签名的集合,表示一种契约或能力
  • 接口的核心作用:
    • 解耦:调用者只依赖接口,不依赖具体实现,便于替换和测试
    • 多态:通过接口引用指向不同实现,实现运行时行为变化
    • 扩展性:易于增加新的实现类,符合开闭原则
    • 标准化:定义模块之间的通信协议(如 JDBC、Servlet 规范)
  • 接口在框架设计中的应用(Spring 的 ApplicationContextBeanPostProcessor
  • 接口与设计模式:策略模式、工厂模式、代理模式、适配器模式等

二、通俗讲解(1分钟开心学)

1. 接口像一个“USB 插口”

想象你的电脑有一个 USB 接口(规范):它规定了插头形状、电压、数据传输协议。任何符合这个规范的设备(U盘、鼠标、键盘)都能插上去用。电脑不需要知道插入的是什么具体设备,只需要按照 USB 协议通信。

接口在 Java 中就是这样的“规范”或“合同”。它定义了“能做什么”,但不规定“怎么做”。

2. 接口的核心价值

  • 解耦:上层业务不依赖底层实现细节。比如支付系统定义 Payment 接口,支付宝、微信各自实现。上层调用 Payment.pay(),换支付方式时只需换一个实现类,其他代码不用改。
  • 多态:一个接口引用可以指向任意实现类对象。例如 List<String> list = new ArrayList<>(); 后面可以换成 new LinkedList<>()
  • 扩展性:增加新功能只需新增实现类,原有代码不修改(符合开闭原则)。
  • 可测试性:单元测试时可以用 Mock 对象代替真实实现(如数据库操作)。

3. 接口在框架中无处不在

Spring 框架大量使用接口:ApplicationContextBeanFactoryAwareInitializingBean 等。你只需要实现这些接口,框架就会在合适的时机调用你的代码。

三、实操代码案例 + 场景说明

场景:开发一个通知服务,支持多种通知方式(邮件、短信、微信),并方便后续增加新方式。

1. 定义接口(契约)

public interface Notifier {
    void send(String to, String message);
}

2. 多种实现(解耦与多态)

public class EmailNotifier implements Notifier {
    @Override
    public void send(String to, String message) {
        System.out.println("发送邮件到 " + to + ":" + message);
    }
}

public class SmsNotifier implements Notifier {
    @Override
    public void send(String to, String message) {
        System.out.println("发送短信到 " + to + ":" + message);
    }
}

3. 业务代码依赖接口(不依赖具体类)

public class NotificationService {
    private final Notifier notifier;
    
    // 构造器注入接口(解耦)
    public NotificationService(Notifier notifier) {
        this.notifier = notifier;
    }
    
    public void notifyUser(String userId, String msg) {
        // 实际中 userId 可能有对应的邮箱/手机号
        notifier.send(userId, msg);
    }
}

// 使用
public class Main {
    public static void main(String[] args) {
        // 很容易切换实现
        Notifier notifier = new EmailNotifier();
        // Notifier notifier = new SmsNotifier();  // 换短信只需改这一行
        NotificationService service = new NotificationService(notifier);
        service.notifyUser("user@example.com", "您的订单已发货");
    }
}

4. 接口作为回调(策略模式)

public interface SortStrategy {
    void sort(int[] arr);
}

public class QuickSort implements SortStrategy {
    @Override public void sort(int[] arr) { /* 快排实现 */ }
}

public class BubbleSort implements SortStrategy {
    @Override public void sort(int[] arr) { /* 冒泡实现 */ }
}

// 使用策略
public class DataProcessor {
    public void process(int[] data, SortStrategy strategy) {
        strategy.sort(data);
        // 后续处理...
    }
}

四、避坑要点

错误/误区 后果 正确做法
接口设计得过于臃肿(几十个方法) 实现类负担过重,违反接口隔离原则 拆分成多个小接口
接口中定义常量(常量接口反模式) 污染命名空间,实现类无意义地继承常量 常量放在枚举或工具类中
空接口(标记接口)滥用 没有实际语义,增加混乱 使用注解替代
方法返回类型用具体实现类而非接口 降低了灵活性和可替换性 返回接口类型(如 List 而非 ArrayList

五、面试高频考点

Q1:什么是面向接口编程?有什么好处?

面向接口编程是指依赖抽象(接口)而非具体实现。好处:解耦、易扩展、易测试、支持多态。

Q2:接口和抽象类如何选择?

优先使用接口,因为可多实现。需要共享状态、构造逻辑或非公共方法时,用抽象类。很多框架提供“接口 + 抽象基类”的模式。

Q3:接口中能否包含静态方法?从哪个版本开始支持?

可以。Java 8 允许接口定义 static 方法,通过接口名调用,常用于提供工具方法,如 Comparator 接口有静态方法 comparing()

六、练习题

  1. 设计:定义一个 Cache 接口,包含 put(key, value)get(key)remove(key) 方法,分别实现 RedisCacheLocalCache(模拟)。
  2. 代码阅读:下面代码有什么问题?
    public interface UserService {
        void addUser(User user);
    }
    public class UserServiceImpl implements UserService {
        //...
    }
    // 在控制器中
    UserServiceImpl service = new UserServiceImpl();  // 直接依赖具体类
    
  3. 动手:用接口+策略模式实现一个计算器,支持加减乘除四种策略,并可以动态切换。

📊 你的学习进度

  • 当前:第19篇 / 共44篇 · 第二阶段:核心语法与面向对象(第5~20篇)
  • ✅ 已完成:第1~18篇
  • 📖 正在学:第19篇
  • ⏳ 待学习:第20~44篇

👉 📚 完整目录 & 学习指南 | 🔥 订阅本专栏,不错过每一篇

💡 本专栏每篇都包含:避坑表 + 面试高频考点 + 练习题。每天30分钟,100天拿offer!


👉 下一篇预告

《Java初始化、构造器、对象创建的过程》

内容简介:成员初始化顺序(静态→实例→构造),对象创建的JVM流程,父子类初始化顺序口诀。

💡 学完这篇,你将彻底搞清楚“对象到底是怎么创建的”,面试必考。

📌 《Java 100 天进阶之路 | 从入门到上岗就业》 每天一篇,建议收藏 + 关注,一起100天拿offer!
👉 点击关注我,更新后第一时间收到推送!

更多推荐