前言
Java 只允许单继承------一个类只能有一个父类,这在很多场景下不够用。
鸟既要"飞"又要"游泳",但 Java 不允许同时继承 Flyable 和 Swimable 两个抽象类。飞机也能飞,但它不是"动物",不应该继承 Animal 抽象类。"飞"是一种能力 ,不是一个种类。
这些需求引出了 Java 接口(interface)------它不关心"你是什么",只关心"你能做什么"。
一、概念:什么是接口
1.1 为什么需要接口
想象一下这些需求:
- 鸟既要"飞"又要"游泳",但 Java 只允许继承一个类
- 飞机也能飞,但它不是"动物",不应该继承
Animal - "可序列化"这个能力,线程类需要,集合类也需要,但它们不在同一棵继承树上
这些需求的共同点:"飞"、"游泳"、"可序列化"是一种能力,不是一种种类。 我们需要一种机制来定义"能做什么",而不是"是什么"------这就是接口。
1.2 接口的定义
一句话理解:接口是一份"能力契约"------它只规定"你能做什么",不关心"你是谁"以及"你怎么做"。
java
// 接口:定义"飞"这种能力
public interface Flyable {
void fly(); // 不写 public abstract,编译器也会自动加上------这叫"隐式"
}
java
// 接口:定义"游泳"这种能力
public interface Swimable {
void swim(); // 等价于:public abstract void swim();
}
1.3 接口的核心特点
接口有三个核心特点,让它和类/抽象类截然不同:
1. 只定义"能做什么",不关心"是什么"
java
// 接口定义"飞"这种能力------不管你是鸟还是飞机
public interface Flyable {
void fly();
}
java
// 接口定义"游泳"这种能力------不管你是鸭子还是潜艇
public interface Swimable {
void swim();
}
2. 一个类可以实现多个接口
java
// 鸭子能飞也能游泳------同时实现两个接口
public class Duck implements Flyable, Swimable {
@Override
public void fly() { System.out.println("扑棱扑棱飞起来了"); }
@Override
public void swim() { System.out.println("在水里游"); }
}
3. 接口不能持有实例状态
什么是"状态"? 状态就是一个对象持有的数据,也就是成员变量(字段)。比如一个数据库连接 connection,多个方法都要用它,它就是状态------状态是多个方法共享的数据。
为什么接口不能持有状态? 因为接口只能有 public static final 常量,不能有实例变量。这意味着接口只定义"行为契约",不存储数据。
判断标准:如果接口里的方法之间需要共享数据(成员变量),说明你需要的是抽象类,不是接口。
二、性质:接口的完整特性
2.1 逐条说明
下面以"游泳"接口为例,展示接口从 Java 7 到 Java 9 的全部特性:
java
// 接口:定义"游泳"能力,包含各版本特性
public interface Swimable {
// -------- Java 7 及以前就有的特性 --------
// 1. 常量:隐式 public static final,必须初始化
int MAX_DEPTH = 200;
// 等价于:public static final int MAX_DEPTH = 200;
// 2. 抽象方法:隐式 public abstract,没有方法体
void swim();
// 等价于:public abstract void swim();
// -------- Java 8 新增 --------
// 3. 默认方法:有方法体,实现类可以选择重写,也可以不重写
default void dive() {
System.out.println("下潜中...");
log("dive called"); // Java 9+ 可以调用私有方法
}
// 4. 静态方法:通过接口名直接调用
static double checkPressure(int depth) {
return depth * 0.1;
}
// -------- Java 9 新增 --------
// 5. 私有方法:用于在接口内部复用 default 方法的代码
private void log(String message) {
System.out.println("[LOG] " + message);
}
// 6. 私有静态方法:用于复用静态方法的代码
private static void validateDepth(int depth) {
if (depth > MAX_DEPTH) {
throw new IllegalArgumentException("深度超过限制");
}
}
}
2.2 特性一览表
| 性质 | 接口 | 说明 |
|---|---|---|
| 能否被实例化 | 不能 | 和抽象类一样 |
| 能否有构造方法 | 不能 | 接口不参与对象构造 |
| 能否有实例成员变量 | 不能 | 只有 public static final 常量 |
| 能否有抽象方法 | 能 | 隐式 public abstract |
| 能否有普通方法 | Java 8+(default) |
有方法体,实现类可以不重写 |
| 能否有静态方法 | Java 8+ | 通过接口名直接调用 |
| 能否有私有方法 | Java 9+ | 用于接口内部复用 default 方法的代码 |
| 实现/继承 | 多实现 / 接口多继承 | 这是接口存在的核心意义 |
2.3 接口的多实现
这是接口区别于抽象类最核心的能力------一个类可以实现多个接口:
java
// 接口:定义"飞"的能力(含 default 方法)
public interface Flyable {
void fly();
default void land() { System.out.println("降落"); }
}
java
// 接口:定义"游泳"的能力(含 default 方法)
public interface Swimable {
void swim();
default void dive() { System.out.println("下潜"); }
}
java
// 接口:定义"跑"的能力
public interface Runnable {
void run();
}
java
// 一个类同时实现三个接口------三种能力兼得
public class Duck implements Flyable, Swimable, Runnable {
private String name;
public Duck(String name) {
this.name = name;
}
@Override
public void fly() {
System.out.println(name + " 扑棱扑棱飞起来了");
}
@Override
public void swim() {
System.out.println(name + " 在水里游");
}
@Override
public void run() {
System.out.println(name + " 摇摇晃晃跑起来");
}
// land() 和 dive() 可以不重写,使用 default 实现
}
使用:
java
// 同一个对象,不同接口类型的引用看到不同的"能力"
Duck duck = new Duck("唐老鸭");
Flyable flyable = duck;
flyable.fly(); // 唐老鸭 扑棱扑棱飞起来了
flyable.land(); // 降落
Swimable swimable = duck;
swimable.swim(); // 唐老鸭 在水里游
swimable.dive(); // 下潜
Runnable runnable = duck;
runnable.run(); // 唐老鸭 摇摇晃晃跑起来
2.4 接口之间的继承
接口可以继承多个接口(注意:类只能单继承,但接口可以多继承):
java
// 接口:定义"读"的能力
public interface Reader {
void read();
}
java
// 接口:定义"写"的能力
public interface Writer {
void write();
}
java
// 接口:多继承------同时具备"读"和"写"的能力
public interface ReadWritable extends Reader, Writer {
void flush();
}
java
// 实现类:文件流,同时具备读、写、刷新能力
public class FileStream implements ReadWritable {
@Override
public void read() { System.out.println("读取文件"); }
@Override
public void write() { System.out.println("写入文件"); }
@Override
public void flush() { System.out.println("刷新缓冲区"); }
}
三、作用:为什么要用接口
3.1 定义契约------面向接口编程
先定义一个支付接口和两个实现类:
java
// 接口:定义支付能力
public interface Payment {
void pay(BigDecimal amount); // BigDecimal 是 Java 中表示精确小数的类,比 double 更适合表示金额
}
java
// 具体实现:支付宝
public class AlipayPayment implements Payment {
@Override
public void pay(BigDecimal amount) {
System.out.println("支付宝支付:" + amount);
}
}
java
// 具体实现:微信
public class WechatPayment implements Payment {
@Override
public void pay(BigDecimal amount) {
System.out.println("微信支付:" + amount);
}
}
对比两种用法:
java
// 不好的做法:依赖具体实现
public class OrderService {
private AlipayPayment payment = new AlipayPayment();
// 结算,即完成支付
public void checkout() {
payment.pay(new BigDecimal("100"));
}
// 换成微信支付就要改这里
}
java
// 好的做法:依赖接口,不依赖具体类
public class OrderService {
private Payment payment; // 类型是接口,不是具体类
public void setPayment(Payment payment) {
this.payment = payment;
}
// 结算,即完成支付
public void checkout() {
payment.pay(new BigDecimal("100"));
}
// 任何实现了 Payment 接口的类都能用,不用改这里的代码
}
使用:
java
// 运行时切换支付方式
OrderService service = new OrderService();
service.setPayment(new AlipayPayment());
service.checkout(); // 支付宝支付:100
service.setPayment(new WechatPayment());
service.checkout(); // 微信支付:100
3.2 实现多态------同一接口,不同行为
**"多态"**就是"同一个指令,不同的行为"。比如同样是
pay(),支付宝和微信的执行方式完全不同------调用方不关心具体怎么执行,只知道"你能 pay 就行"。
用同一个接口类型引用不同的实现,运行时自动调用对应的行为:
java
// 用接口类型 Payment 引用不同的实现类
Payment p1 = new AlipayPayment();
Payment p2 = new WechatPayment();
p1.pay(new BigDecimal("100")); // 支付宝支付:100
p2.pay(new BigDecimal("200")); // 微信支付:200
3.3 解耦------调用方和实现方互不依赖
调用方(OrderService) ──依赖──▶ 接口(Payment)
▲ implements
AlipayPayment / WechatPayment
调用方只知道接口的存在,完全不知道具体实现是谁。未来替换实现类,调用方代码零修改。
3.4 弥补单继承------多能力组合
java
// ArrayList 同时具备 4 种能力
public class ArrayList<E> extends AbstractList<E>
implements List<E>, // 列表能力
RandomAccess, // 随机访问能力
Cloneable, // 可克隆能力
java.io.Serializable { // 可序列化能力
// ...
}
四、场景:接口的典型应用
4.1 策略模式(最经典的场景)
场景:多种算法/策略可以互相替换,客户端在运行时选择。
java
// 1. 定义策略接口
public interface SortStrategy {
void sort(int[] array);
}
java
// 2. 具体策略
public class BubbleSortStrategy implements SortStrategy {
@Override
public void sort(int[] array) {
System.out.println("使用冒泡排序");
// 冒泡排序实现...
}
}
java
// 具体策略:快速排序
public class QuickSortStrategy implements SortStrategy {
@Override
public void sort(int[] array) {
System.out.println("使用快速排序");
// 快速排序实现...
}
}
java
// 具体策略:归并排序
public class MergeSortStrategy implements SortStrategy {
@Override
public void sort(int[] array) {
System.out.println("使用归并排序");
// 归并排序实现...
}
}
java
// 上下文:持有策略接口的引用,运行时切换策略
public class Sorter {
private SortStrategy strategy;
public void setStrategy(SortStrategy strategy) {
this.strategy = strategy;
}
public void sort(int[] array) {
strategy.sort(array);
}
}
使用:
java
// 运行时动态切换排序策略
Sorter sorter = new Sorter();
int[] data = {5, 2, 8, 1, 9};
sorter.setStrategy(new QuickSortStrategy());
sorter.sort(data); // 使用快速排序
sorter.setStrategy(new MergeSortStrategy());
sorter.sort(data); // 使用归并排序
设计思想 :策略模式把"做什么"(接口)和"怎么做"(具体策略)分离。新增排序算法只需新增一个类,不用修改
Sorter。
4.2 观察者模式
场景:一个对象状态变化时,自动通知所有关注它的对象。
java
// 观察者接口:定义"订阅者"的行为
// 任何想要接收通知的类,都实现这个接口
public interface Observer {
// 当被观察者有新消息时,会调用这个方法
// message 就是被观察者推送过来的内容
void update(String message);
}
java
// 被观察者接口:定义"发布者"的行为
// 任何想要发布通知的类,都实现这个接口
public interface Subject {
// attach:注册一个观察者(订阅)------"我要关注你"
void attach(Observer observer);
// detach:移除一个观察者(取消订阅)------"我不再关注你了"
void detach(Observer observer);
// notifyObservers:通知所有已注册的观察者------"有新消息了,通知所有人"
void notifyObservers();
}
java
// 被观察者的具体实现:新闻发布者,发布新闻时通知所有观察者
public class NewsPublisher implements Subject {
private List<Observer> observers = new ArrayList<>();
private String latestNews;
@Override
public void attach(Observer observer) {
observers.add(observer);
}
@Override
public void detach(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer o : observers) {
o.update(latestNews);
}
}
public void publish(String news) {
this.latestNews = news;
notifyObservers();
}
}
java
// 观察者的具体实现:收到通知后发邮件
public class EmailNotifier implements Observer {
@Override
public void update(String message) {
System.out.println("发送邮件:" + message);
}
}
java
// 观察者的具体实现:收到通知后发短信
public class SmsNotifier implements Observer {
@Override
public void update(String message) {
System.out.println("发送短信:" + message);
}
}
使用:
java
// 发布新闻,所有订阅者自动收到通知
NewsPublisher publisher = new NewsPublisher();
publisher.attach(new EmailNotifier());
publisher.attach(new SmsNotifier());
publisher.publish("Java 21 正式发布!");
// 发送邮件:Java 21 正式发布!
// 发送短信:Java 21 正式发布!
4.3 标记接口
场景:给类打上"标签",表示它具备某种能力,不需要定义方法。
java
// JDK 中的标记接口
public interface Serializable { } // 可序列化
java
// 标记接口:表示对象可以被克隆
public interface Cloneable { } // 可克隆
java
// 标记接口:表示列表支持随机访问(如 ArrayList)
public interface RandomAccess { } // 支持随机访问
java
// 使用:JVM 或 API 通过 instanceof 检查
public class User implements Serializable {
// 可以被 ObjectOutputStream 序列化
// 如果没有 implements Serializable,序列化时会抛异常
}
java
// 标记接口的检查
// instanceof 用来判断"这个对象是否实现了某个接口"
if (obj instanceof Serializable) {
// 安全地进行序列化
} else {
throw new NotSerializableException();
}
4.4 函数式接口(Java 8+)
场景:接口只有一个抽象方法,可以用 Lambda 表达式简化代码。
java
// 自定义函数式接口
@FunctionalInterface
public interface StringProcessor {
String process(String input);
}
java
// 传统写法
StringProcessor toUpper = new StringProcessor() {
@Override
public String process(String input) {
return input.toUpperCase();
}
};
// Lambda 写法(Java 8 简写:左边是参数,右边是返回值)
StringProcessor toUpper = s -> s.toUpperCase();
StringProcessor trim = s -> s.trim();
StringProcessor addPrefix = s -> "PREFIX_" + s;
System.out.println(toUpper.process("hello")); // HELLO
System.out.println(trim.process(" hello ")); // hello
System.out.println(addPrefix.process("data")); // PREFIX_data
JDK 内置的常用函数式接口:
java
// Predicate<T>:判断
Predicate<String> isEmpty = s -> s.isEmpty();
isEmpty.test(""); // true
// Consumer<T>:消费
Consumer<String> print = s -> System.out.println(s);
print.accept("hello"); // 输出 hello
// Function<T, R>:转换
Function<String, Integer> length = s -> s.length();
length.apply("hello"); // 5
// Supplier<T>:提供
Supplier<Double> random = () -> Math.random();
random.get(); // 随机数
4.5 工厂方法模式
场景:创建对象时不暴露创建逻辑,通过接口引用返回具体产品。
java
// 产品接口
public interface DatabaseConnection {
// 连接
void connect();
// 执行
void execute(String sql);
// 关闭
void close();
}
java
// 具体产品
public class MySQLConnection implements DatabaseConnection {
@Override
public void connect() { System.out.println("连接 MySQL"); }
@Override
public void execute(String sql) { System.out.println("MySQL 执行:" + sql); }
@Override
public void close() { System.out.println("关闭 MySQL 连接"); }
}
java
// 具体产品:PostgreSQL 连接
public class PostgreSQLConnection implements DatabaseConnection {
@Override
public void connect() { System.out.println("连接 PostgreSQL"); }
@Override
public void execute(String sql) { System.out.println("PostgreSQL 执行:" + sql); }
@Override
public void close() { System.out.println("关闭 PostgreSQL 连接"); }
}
java
// 工厂
public class ConnectionFactory {
public static DatabaseConnection create(String type) {
switch (type) {
case "mysql": return new MySQLConnection();
case "postgres": return new PostgreSQLConnection();
default: throw new IllegalArgumentException("不支持的类型:" + type);
}
}
}
使用:
java
// 调用方只依赖接口,不知道具体实现
DatabaseConnection conn = ConnectionFactory.create("mysql");
conn.connect();
conn.execute("SELECT * FROM users");
conn.close();
五、举例:完整的代码示例
5.1 插件式通知系统
java
// 通知接口
public interface Notifier {
void send(String recipient, String message);
}
java
// 邮件通知
public class EmailNotifier implements Notifier {
@Override
public void send(String recipient, String message) {
System.out.println("发送邮件给 " + recipient + ":" + message);
}
}
java
// 短信通知
public class SmsNotifier implements Notifier {
@Override
public void send(String recipient, String message) {
System.out.println("发送短信给 " + recipient + ":" + message);
}
}
java
// 钉钉通知
public class DingTalkNotifier implements Notifier {
@Override
public void send(String recipient, String message) {
System.out.println("发送钉钉消息给 " + recipient + ":" + message);
}
}
java
// 通知服务:面向接口编程
public class NotificationService {
private List<Notifier> notifiers = new ArrayList<>();
public void addNotifier(Notifier notifier) {
notifiers.add(notifier);
}
public void removeNotifier(Notifier notifier) {
notifiers.remove(notifier);
}
public void notifyAll(String recipient, String message) {
for (Notifier notifier : notifiers) {
notifier.send(recipient, message);
}
}
}
使用:
java
// 注册多个通知渠道,一次性通知所有渠道
NotificationService service = new NotificationService();
service.addNotifier(new EmailNotifier());
service.addNotifier(new SmsNotifier());
service.addNotifier(new DingTalkNotifier());
service.notifyAll("admin@example.com", "系统告警:CPU 使用率超过 90%");
// 发送邮件给 admin@example.com:系统告警:CPU 使用率超过 90%
// 发送短信给 admin@example.com:系统告警:CPU 使用率超过 90%
// 发送钉钉消息给 admin@example.com:系统告警:CPU 使用率超过 90%
5.2 数据转换管道
java
// 数据处理器接口
public interface DataProcessor {
String process(String input);
}
java
// 多个处理器组合成管道
public class DataPipeline {
private List<DataProcessor> processors = new ArrayList<>();
public DataPipeline add(DataProcessor processor) {
processors.add(processor);
return this;
}
public String execute(String input) {
String result = input;
for (DataProcessor processor : processors) {
result = processor.process(result);
}
return result;
}
}
java
// 去除首尾空白字符的处理器
public class TrimProcessor implements DataProcessor {
@Override
public String process(String input) {
// trim() 去掉字符串开头和结尾的空格、制表符、换行等空白字符
return input.trim();
}
}
java
// 转小写的处理器
public class LowerCaseProcessor implements DataProcessor {
@Override
public String process(String input) {
// toLowerCase() 把所有大写字母转成小写
return input.toLowerCase();
}
}
java
// 去除 HTML 标签的处理器
public class RemoveHtmlProcessor implements DataProcessor {
@Override
public String process(String input) {
// 正则 <[^>]*> 匹配所有 HTML 标签(如 <b>、</b>、<div> 等)
// [^>]* 表示"除了 > 之外的任意字符,重复 0 次或多次"
// 整体含义:从 < 开始,到第一个 > 结束,中间可以是任意内容
// replaceAll 把匹配到的标签全部替换为空字符串(即删除)
return input.replaceAll("<[^>]*>", "");
}
}
java
// 截断超长文本的处理器
public class LimitLengthProcessor implements DataProcessor {
private int maxLength; // 允许的最大长度
public LimitLengthProcessor(int maxLength) {
this.maxLength = maxLength;
}
@Override
public String process(String input) {
// 如果字符串长度超过 maxLength,截取前 maxLength 个字符并加上 "..."
// 否则原样返回
return input.length() > maxLength
? input.substring(0, maxLength) + "..."
: input;
}
}
使用:
java
// 组合多个处理器,按顺序依次处理数据
String result = new DataPipeline()
.add(new TrimProcessor())
.add(new RemoveHtmlProcessor())
.add(new LowerCaseProcessor())
.add(new LimitLengthProcessor(20))
.execute(" <b>Hello World</b> This is a very long text ");
System.out.println(result); // hello world this is ...
六、反例:接口的常见误用
6.1 接口中写太多 default 方法(应该用抽象类)
java
// 反例:接口变成"伪抽象类"
public interface UserRepository {
default User findById(Long id) {
// 需要数据库连接------但接口不能持有状态!
// 只能依赖其他方法,设计非常别扭
return getConnection().query("SELECT * FROM users WHERE id = " + id);
}
default void save(User user) { /* ... */ }
default void delete(Long id) { /* ... */ }
// 十几个 default 方法,全都需要共享状态
Connection getConnection(); // 被迫加一个抽象方法来获取连接
}
正确做法:有状态和共享逻辑时,用抽象类:
java
// 正确做法:用抽象类持有状态和共享逻辑
public abstract class AbstractUserRepository {
protected Connection connection; // 可以持有状态
public User findById(Long id) {
return connection.query("SELECT * FROM users WHERE id = " + id);
}
public void save(User user) { /* 可以直接使用 connection */ }
}
6.2 "胖接口"(违反接口隔离原则)
java
// 反例:一个接口包含了不相关的行为
public interface Worker {
void work();
void eat(); // 机器人不需要吃饭!
void sleep(); // 机器人不需要睡觉!
}
java
// 机器人被迫实现不需要的方法
public class Robot implements Worker {
@Override
public void work() { System.out.println("工作中"); }
@Override
public void eat() { /* 空实现,被迫写的 */ }
@Override
public void sleep() { /* 空实现,被迫写的 */ }
}
正确做法:按职责拆分为多个小接口:
java
// 拆分后的接口:只定义"工作"能力
public interface Workable { void work(); }
java
// 拆分后的接口:只定义"吃饭"能力
public interface Eatable { void eat(); }
java
// 拆分后的接口:只定义"睡觉"能力
public interface Sleepable { void sleep(); }
java
// 机器人只实现需要的接口
public class Robot implements Workable {
@Override
public void work() { System.out.println("工作中"); }
}
java
// 人类实现所有接口
public class Human implements Workable, Eatable, Sleepable {
@Override public void work() { System.out.println("工作中"); }
@Override public void eat() { System.out.println("吃饭"); }
@Override public void sleep() { System.out.println("睡觉"); }
}
6.3 接口用来传递数据
java
// 反例:用接口定义数据结构
public interface UserData {
String getName();
int getAge();
// 接口是定义"行为契约"的,不是装数据的
}
正确做法:用类(或 record,Java 16+ 的简化数据类)做数据载体:
java
// 用 record(Java 16+)作为纯数据载体
public record User(String name, int age) { }
6.4 为了用接口而用接口
java
// 反例:只有一个实现,且永远不会有第二个
public interface HelloWorldService {
String sayHello(String name);
}
java
// 唯一的实现类
public class HelloWorldServiceImpl implements HelloWorldService {
@Override
public String sayHello(String name) { return "Hello, " + name; }
}
这个接口毫无意义------只有一个实现,且不需要多态。正确做法:直接用类,等到确实需要多实现时再提取接口:
java
// 正确做法:只有一个实现时,直接用普通类
public class HelloWorldService {
public String sayHello(String name) { return "Hello, " + name; }
}
七、速查清单
| 问题 | 答案 |
|---|---|
| 接口用什么关键字? | interface |
| 接口能实例化吗? | 不能 |
| 接口能有构造方法吗? | 不能 |
| 接口能有实例成员变量吗? | 不能,只有 public static final 常量 |
| 接口能有抽象方法吗? | 能,隐式 public abstract |
| 接口能有普通方法吗? | Java 8+ 有 default 方法 |
| 接口能有静态方法吗? | Java 8+ 可以 |
| 接口能有私有方法吗? | Java 9+ 可以 |
| 一个类能实现几个接口? | 多个 |
| 接口能继承几个接口? | 多个 |
| 接口能继承类吗? | 不能 |
| default 方法能被重写吗? | 能,也可以不重写 |
| 什么是函数式接口? | 只有一个抽象方法的接口,可以用 Lambda |
| 什么是标记接口? | 没有任何方法的接口,如 Serializable |
| 典型设计模式? | 策略模式、观察者模式、工厂方法模式 |
八、面试口述:什么是接口
接口是 Java 中定义行为契约的机制,用 interface 关键字声明。它和抽象类的定位不同------抽象类定义"是什么",接口定义"能做什么"。接口不能有构造方法和实例变量,只能有常量和抽象方法,这让接口非常"轻量",换来的是核心优势:一个类可以实现多个接口,弥补了 Java 单继承的限制。
从 Java 版本演进来看,接口的能力在逐步增强:Java 7 只有常量和抽象方法;Java 8 新增了 default 方法和静态方法,default 方法让接口可以有默认实现,不用强制实现类重写;Java 9 又新增了私有方法,用于在接口内部复用 default 方法的代码。但即使有了这些增强,接口仍然不能持有实例状态,所以不能替代抽象类。
接口最常见的应用场景有四个:一是策略模式,把不同算法封装成同一接口的实现类,运行时动态切换;二是面向接口编程实现解耦,调用方只依赖接口,不依赖具体实现;三是函数式接口,只有一个抽象方法的接口可以用 Lambda 表达式简化代码;四是标记接口,没有任何方法的接口(如 Serializable),通过 instanceof 检查来赋予类某种特殊能力。