Java接口与实现类关系的解析

接口不能直接new,但可通过匿名类或Lambda实例化;实现类必须implements并覆盖全部抽象方法;接口支持多继承,运行时引用类型不影响实际对象行为。

接口不能直接 new,但可以用匿名类或 Lambda 实例化

Java 中 interface 是纯抽象类型,编译器禁止写 new MyInterface()。但这不意味着它完全不能“变成对象”——只要提供所有抽象方法的实现,就能构造实例。

常见做法有两种:

  • 用匿名内部类:适合方法较多、逻辑较重的场景,例如测试回调或临时策略
  • 用 Lambda 表达式:仅限函数式接口(即有且仅有一个抽象方法),如 RunnableComparator、自定义的 @FunctionalInterface
Runnable r = () -> System.out.println("hello");
Comparator c = (a, b) -> a.length() - b.length();

注意:Lambda 本质是编译器生成的私有静态方法 + 内部类桥接,不是语法糖那么简单;过度嵌套或捕获大对象可能影响 GC。

实现类必须 implements 接口,且覆盖全部抽象方法

一个类要成为某接口的实现者,必须显式声明 imple

ments InterfaceName,并在编译期满足契约:所有 public abstract 方法都有可访问的 public 实现(默认方法和静态方法不用覆写)。

容易忽略的细节:

  • 如果实现类是 abstract,可以不实现部分抽象方法
  • 接口中 default 方法可被重写,但子类若想调用父接口的默认实现,需用 InterfaceName.super.method()
  • 多个接口含同签名 default 方法时,实现类必须显式重写,否则编译报错:class inherits unrelated defaults for method from types

接口之间可用 extends,但只能单继承、可多继承

接口支持用 extends 继承其他接口,语法上允许一次写多个父接口,例如:interface A extends B, C, D。这和类的 extends(只允许一个)完全不同。

这种设计让接口能组合能力,比如:

interface Readable extends AutoCloseable {
    String readLine();
}
interface Writable {
    void write(String s);
}
interface TextFile extends Readable, Writable { }

但要注意:若 B 和 C 都定义了同名同参的 default 方法,TextFile 编译不过,必须自己提供实现。

运行时无法区分「通过接口引用」还是「通过实现类引用」指向同一对象

Java 的多态发生在运行时,JVM 只看实际对象类型,不看变量声明类型。也就是说:MyInterface obj = new MyClass();MyClass obj = new MyClass(); 创建的是同一个对象,只是引用类型不同。

关键影响:

  • 字段访问始终按引用类型决定(接口不能有实例字段,所以接口引用只能看到接口里定义的 public static final 常量)
  • 方法调用走动态绑定:哪怕通过接口引用调用,最终执行的仍是实现类里重写的方法
  • 强制转型((MyClass) obj)必须确保实际类型匹配,否则抛 ClassCastException

这个机制是 Spring AOP、Mockito 动态代理等工具的基础——它们返回的往往是一个实现了目标接口的代理对象,你拿它当接口用完全没问题,但底层根本不是原实现类。