RHODES ISLAND // PERSONAL ARCHIVE

关之水瑶 · 作战记录

记录技术、生活与正在进行的思考。欢迎来到我的个人情报终端。

SYSTEM ONLINE / 2026

2. 接口和抽象类的区别是什么?Java 8 之后接口发生了哪些变化(默认方法/静态方法)?(面试官考核目标:考察抽象能力与设计取舍,以及是否跟进了语言演进)

接口和抽象类的区别,以及 Java 8 之后接口的变化

一句话回答

  • 抽象类:表达 is-a,用于代码复用 + 模板设计,可以有构造器、成员变量、各种访问修饰符的方法。
  • 接口:表达 can-do / 能力契约,用于解耦 + 多实现,Java 8 后可以有默认方法和静态方法。
  • 取舍:优先接口定义能力,需要复用状态/代码、表达强分类时用抽象类。

1. 接口和抽象类的核心区别

维度 抽象类 接口
关系语义 is-a,是什么 can-do / has-a capability,能做什么
继承/实现 单继承 extends 多实现 implements
构造器 有 无
成员变量 普通变量、静态变量都可以 只能是 public static final 常量
方法 抽象方法 + 具体方法 Java 8 前只能抽象方法;Java 8 后可默认方法、静态方法;Java 9 后可私有方法
访问修饰符 任意 方法默认 public,Java 9 私有方法除外
设计目的 代码复用、模板方法、强分类 定义契约、能力扩展、解耦
状态 可以有实例状态 不能有实例状态
扩展性 单继承限制大 多实现,扩展灵活

记忆:抽象类是“是什么 + 复用”,接口是“能做什么 + 契约”。


2. 抽象类解决什么问题

  1. 多个子类有共性代码,可以抽到父类复用。
  2. 用抽象方法定义模板,子类实现变化部分。
  3. 表达稳定的分类体系,例如 Animal、Shape。
abstract class AbstractExporter {
    // 模板方法:定义流程
    public final void export() {
        fetchData();
        format();
        write();
    }

    protected abstract void fetchData();
    protected abstract void format();

    protected void write() {
        System.out.println("默认写出逻辑");
    }
}

抽象类适合模板方法模式:父类定流程,子类填步骤。


3. 接口解决什么问题

  1. 定义一组能力契约,不关心谁实现、怎么实现。
  2. 一个类可以实现多个接口,突破单继承限制。
  3. 调用方依赖接口,便于替换实现、测试 mock、扩展。
interface Payable {
    void pay(BigDecimal amount);
}

class Alipay implements Payable {
    public void pay(BigDecimal amount) { /* ... */ }
}

class WechatPay implements Payable {
    public void pay(BigDecimal amount) { /* ... */ }
}

接口适合策略模式、依赖倒置、SPI、回调等场景。


4. Java 8 之后接口的变化

4.1 Java 8:默认方法 default method

接口可以有带实现的方法,用 default 修饰。

interface Flyable {
    void fly();

    default void land() {
        System.out.println("默认降落");
    }
}

目的:

  1. 接口演进:给老接口加新方法时,不强制所有实现类都改代码。
    典型例子:Collection 新增 stream()、forEach()。
  2. 行为复用:多个实现类可共享默认实现。

注意:

  • 默认方法不是抽象方法,实现类可以不重写。
  • 如果类实现的多个接口有同名默认方法,必须重写解决冲突:
interface A { default void hello() { System.out.println("A"); } }
interface B { default void hello() { System.out.println("B"); } }

class C implements A, B {
    @Override
    public void hello() {
        A.super.hello(); // 显式选择
    }
}
  • 类优先原则:如果父类已有同名方法,父类方法优先于接口默认方法。

4.2 Java 8:静态方法 static method

接口可以有静态方法,只能通过接口名调用。

interface Validator {
    static boolean isBlank(String s) {
        return s == null || s.trim().isEmpty();
    }
}

目的:

  • 把和接口相关的工具方法放在接口内,避免额外的工具类。
  • 典型例子:Comparator.comparing()、Stream.of()。

注意:

  • 接口静态方法不会被实现类继承。
  • 不能通过实现类或实例调用。

4.3 Java 9:私有方法 private method

接口可以有私有方法和私有静态方法,供默认方法/静态方法内部复用。

interface Logger {
    default void logInfo(String msg) {
        log("INFO", msg);
    }

    default void logError(String msg) {
        log("ERROR", msg);
    }

    private void log(String level, String msg) {
        System.out.println(level + ": " + msg);
    }
}

目的:

  • 抽取多个默认方法之间的公共逻辑。
  • 避免把内部实现细节暴露给实现类。

4.4 Java 8+ 接口能力总结

版本 新增能力
Java 8 默认方法、静态方法
Java 9 私有方法、私有静态方法

接口越来越像“抽象类”,但仍然不能有实例字段、不能有构造器、不能被实例化。


5. 设计取舍:什么时候用接口,什么时候用抽象类

优先用接口

  • 需要定义能力契约,如 Comparable、Runnable。
  • 需要多实现,突破单继承。
  • 调用方只关心行为,不关心状态和实现。
  • 需要 SPI、策略、回调、依赖倒置。
  • 需要跨继承体系复用能力。

用抽象类

  • 多个子类有大量共性代码或状态需要复用。
  • 需要模板方法,控制流程骨架。
  • 需要定义非 public 方法、构造器、实例字段。
  • 语义上是明确的 is-a 分类,且分类稳定。

组合使用

常见做法:接口定义契约 + 抽象类提供骨架实现。

interface Task {
    void execute();
    String name();
}

abstract class AbstractTask implements Task {
    @Override
    public void execute() {
        System.out.println("开始: " + name());
        doExecute();
        System.out.println("结束: " + name());
    }

    protected abstract void doExecute();
}

JDK 中典型例子:List 是接口,AbstractList 是骨架实现。


6. 面试口述版

接口和抽象类的核心区别在于语义和设计目的。

抽象类表达 is-a,用于代码复用和模板设计,可以有构造器、实例字段、普通方法和各种访问修饰符,但只能单继承。

接口表达 can-do,用于定义能力契约和解耦,可以多实现,但不能有实例状态和构造器。Java 8 之前接口只能有抽象方法。

Java 8 之后接口增加了默认方法和静态方法。默认方法让接口可以在不破坏已有实现类的前提下演进,比如 Collection 新增 stream();静态方法让接口可以放相关工具方法,比如 Comparator.comparing()。Java 9 又增加了私有方法,用于抽取默认方法之间的公共逻辑。

设计取舍上,优先用接口定义契约,需要复用状态和代码、表达强分类时用抽象类。两者也可以组合:接口定契约,抽象类给骨架实现。


7. 常见追问

  • 默认方法冲突怎么办?
    类必须重写;可用 InterfaceName.super.method() 显式调用某个接口实现;父类方法优先于接口默认方法。

  • 接口为什么不能有实例字段?
    接口是能力契约,不承载对象状态;状态应由实现类管理。

  • 抽象类能有构造器吗?
    能,供子类 super() 调用,但不能直接 new。

  • 接口能继承接口吗?
    能,且可以多继承,如 interface C extends A, B。

  • 默认方法算多态吗?
    算,实现类可重写默认方法,运行时仍按实际对象动态分派。

  • 什么时候用抽象类而不是接口?
    需要共享状态、构造逻辑、非 public 方法,或需要模板方法控制流程时。

posted @ 2026-09-24 17:12  关之水瑶  阅读(18)  评论(0)    收藏  举报