Java学习笔记之接口

前言

Java 只允许单继承------一个类只能有一个父类,这在很多场景下不够用。

鸟既要"飞"又要"游泳",但 Java 不允许同时继承 FlyableSwimable 两个抽象类。飞机也能飞,但它不是"动物",不应该继承 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 检查来赋予类某种特殊能力。

相关推荐
莫寒清1 小时前
@AliasFor 注解
java·spring
MaCa .BaKa1 小时前
56-非遗手工艺品定制平台系统
java·vue.js·spring boot·mysql·maven·非遗手工制作平台系统·非遗制作
吃好睡好便好1 小时前
矩阵的左除和右除
人工智能·学习·线性代数·算法·矩阵
OBiO20131 小时前
从血清型到启动子升级——如何规避心肌 AAV 肝脏泄露?
学习
lili00121 小时前
AI编程三件套CI集成与质量门禁:从“看起来对“到“证据确凿“
java·人工智能·python·ci/cd·ai编程
雪之下雪乃的代码日记1 小时前
认识Java中集合框架
java·开发语言·笔记
道亦无名1 小时前
windows下杀死僵尸进程命令
笔记
江屿风1 小时前
C++图的基本概念流食般投喂-竞赛编
开发语言·数据结构·c++·笔记·算法·图论
独自破碎E1 小时前
SLKJ笔试题解析
java·开发语言