接口是 Java 中定义对象行为规范的核心机制。它主要用于规定实现类必须具备哪些能力(方法),而不负责具体的逻辑实现。面向接口编程是降低系统耦合度、提升代码扩展性的重要设计原则。

接口的基础定义与演进

接口的声明修饰符只能是 public 或默认(包级可见)+ abstract(隐式),。随着 JDK 版本的更迭,接口内部能够包含的成员也在不断丰富:

public interface Vehicle {
    // 1. 属性(常量):必须且默认为 public static final
    int SPEED_LIMIT = 100; 

    // 2. 抽象方法:必须且默认为 public abstract
    void go(String loc); 

    // 3. 默认方法 (JDK 8+):允许有方法体,实现类可继承或重写
    default void stop() {       
        System.out.println("Stopping vehicle...");
    };   

    // 4. 静态方法 (JDK 8+):直接通过 接口名.方法名() 调用
    static void start() {
        System.out.println("Starting vehicle...");
    } 
}
  • 一个接口对应一个字节码文件

接口的实现(implements)

接口本身是一种抽象类型,不能被实例化。普通类或抽象类可以通过 implements 关键字来实现接口。

  1. 普通类实现接口: 必须重写(实现)接口中所有的抽象方法
  2. 抽象类实现接口: 可以不实现或只实现部分抽象方法,剩下的留给子类去完成。
  3. 多实现: 一个类可以同时实现多个接口(例如 class MyClass implements A, B)。

🔴 【核心注意点:访问权限陷阱】
重写接口中方法时,必须显式声明为 public,绝对不能使用默认的访问权限!
因为接口中的抽象方法隐式为 public,而 Java 规定重写方法时访问权限不能缩小。如果不写修饰符,就变成了默认(包访问)权限,编译器会直接报错。

interface Moveable {
    void run(); // 隐式为 public abstract void run();
}

class Car implements Moveable {
    // ❌ 错误写法:不写修饰符,变成了默认权限,缩小了原有的 public 权限
    // void run() { System.out.println("汽车跑"); } 

    // ✅ 正确写法:必须老老实实加上 public
    @Override
    public void run() { 
        System.out.println("汽车跑"); 
    }
}

a) 普通类实现接口

public interface Payment {
    // 接口中的方法默认是 public abstract
    void pay(double amount);     // 抽象方法:支付
    void refund(double amount);  // 抽象方法:退款
}
// 普通类 AliPay 实现了 Payment 接口
public class AliPay implements Payment {

    // 必须重写(实现)pay 方法
    @Override
    public void pay(double amount) {
        System.out.println("正在拉起支付宝收银台...");
        System.out.println("成功使用【支付宝】支付了:" + amount + " 元。");
    }

    // 必须重写(实现)refund 方法
    @Override
    public void refund(double amount) {
        System.out.println("【支付宝】退款处理中...");
        System.out.println("成功退款:" + amount + " 元,已原路退回余额宝。");
    }
    
    // 普通类当然也可以有自己独有的方法
    public void collectAntForestEnergy() {
        System.out.println("支付成功,收取了 10g 蚂蚁森林能量!");
    }
}

b) 抽象类实现接口

public interface SmartAppliance {
    void powerOn();   // 开机
    void powerOff();  // 关机
    void work();      // 具体工作(比如洗衣、制冷、扫地)
}
// 抽象类实现了 SmartAppliance 接口
public abstract class BaseAppliance implements SmartAppliance {
    
    // 1. 实现了接口的 powerOn 方法(通用逻辑)
    @Override
    public void powerOn() {
        System.out.println("系统通电,指示灯亮起,设备已开机。");
    }

    // 2. 实现了接口的 powerOff 方法(通用逻辑)
    @Override
    public void powerOff() {
        System.out.println("系统断电,设备已安全关机。");
    }

    // 3. 故意不实现 work() 方法!
    // 因为洗衣机和冰箱的工作方式完全不同,抽象类不知道怎么实现,
    // 所以把它留给具体的子类去完成。
    // (注意:这里甚至不需要把 work() 写出来,因为它本来就是接口里的抽象方法,
    // 只要 BaseAppliance 是抽象类,不实现也不会报错)
}
// 具体类继承了抽象类
public class WashingMachine extends BaseAppliance {

    // 只需要补全剩下的、未实现的抽象方法
    @Override
    public void work() {
        System.out.println("洗衣机正在注水,开始旋转清洗衣物...");
    }
}

public class Refrigerator extends BaseAppliance {

    // 同样只需要补全自己特有的工作逻辑
    @Override
    public void work() {
        System.out.println("冰箱压缩机启动,开始制冷保鲜...");
    }
}
public class Main {
    public static void main(String[] args) {
        // 接口的多态体现
        SmartAppliance washer = new WashingMachine();
        
        washer.powerOn();  // 调用的是 BaseAppliance 里的通用代码
        washer.work();     // 调用的是 WashingMachine 里特有的代码
        washer.powerOff(); // 调用的是 BaseAppliance 里的通用代码
    }
}

c) 多实现

// 接口 1:照相机功能
public interface Camera {
    void takePhoto(); // 抽象方法:拍照
}

// 接口 2:音乐播放器功能
public interface MusicPlayer {
    void playMusic(); // 抽象方法:播放音乐
}
// SmartPhone 类同时实现了 Camera 和 MusicPlayer 两个接口
public class SmartPhone implements Camera, MusicPlayer {

    // 必须实现 Camera 接口里的 takePhoto 方法
    @Override
    public void takePhoto() {
        System.out.println("咔嚓!智能手机正在使用高清摄像头拍照...");
    }

    // 必须实现 MusicPlayer 接口里的 playMusic 方法
    @Override
    public void playMusic() {
        System.out.println("播放音乐:正在通过立体声扬声器播放歌曲...");
    }
    
    // 手机当然也可以有自己独有的功能
    public void makeCall() {
        System.out.println("正在拨打电话...");
    }
}

接口继承:“多继承”机制 (extends)

虽然 Java 的类只支持单继承,但 接口之间支持多继承。一个接口可以使用 extends 关键字继承一个或多个父接口,这使得接口的组合变得非常灵活。

接口多继承与常量继承

子接口不仅能继承父接口的方法,还会原封不动地继承父接口中的常量(public static final)。

interface Mammal { void run(); }
interface Bird { void fly(); }

// 接口的多继承
interface Bat extends Mammal, Bird { 
    void echoLocate(); 
}

class BatImpl implements Bat {
    // 必须实现 run(), fly(), echoLocate() 三个方法
    @Override public void run() {}
    @Override public void fly() {}
    @Override public void echoLocate() {}
}

方法冲突与“菱形问题” (Diamond Problem)

当遇到多继承或多实现时,经典的“菱形问题”就会浮现:如果多个父接口拥有相同签名的方法,子接口或实现类该怎么处理?Java 制定了明确的冲突解决规则:

a) 抽象方法冲突

如果多个父接口中有完全相同签名的抽象方法,子接口只需继承一次,实现类也只需重写一次,没有冲突

interface X {
    void test();
}

interface Y {
    void test();
}

interface Z extends X, Y {
    // test()方法只继承一次,没有冲突
    void otherMethod();
}

class Implementor implements Z {
    @Override
    public void test() {
        System.out.println("实现了test方法");
    }
    
    @Override
    public void otherMethod() {
        System.out.println("实现了otherMethod");
    }
}

b) 默认方法冲突 (JDK 8+)

如果多个父接口有相同的默认方法,编译器会报错(二义性)。子类或子接口必须显式重写该方法来解决冲突。可以选择提供全新的实现,或者通过 父接口名.super.方法名() 指定调用某一个父接口的实现。

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 的默认方法
        A.super.hello(); 
    }
}

c) 抽象方法和默认方法的组合

如果父接口中既有抽象方法又有默认方法(签名相同),子接口继承的是抽象方法。

interface Parent1 {
    void show(); // 抽象方法
}

interface Parent2 {
    default void show() {
        System.out.println("Parent2's show");
    }
}

// 子接口继承的是Parent1的抽象方法show()
interface Child extends Parent1, Parent2 {
    // show()在这里是抽象方法
}

class Implementation implements Child {
    @Override
    public void show() {
        System.out.println("必须实现show方法");
    }
}

接口与类继承的核心对比

特性 类继承 (extends) 接口实现 (implements) / 接口继承
关系语义 is-a (是什么,血统关系) can-do (能做什么,能力契约)
数量限制 单继承 (避免状态和行为的严重冲突) 多实现 / 多继承
代码复用 直接复用父类代码及状态 主要定义规范,JDK 8+ 仅能通过 default 复用少量行为
内部属性 可以有各种类型的变量 只能是 public static final 常量
设计目的 类的层次化设计与基础代码复用 定义跨系统的行为规范,解耦,提升灵活性

面试笔试题

考点 1:变量的二义性冲突

interface G { int x = 0; }
class H { int x = 1; }

class I extends H implements G {
    void printX() {
        // System.out.println(x); // 编译报错:对 x 的引用不明确
        
        // 正确做法:必须指明出处
        System.out.println(G.x);     // 通过接口名调用常量
        System.out.println(super.x); // 通过 super 调用父类成员
    }
}

解析:当继承的父类和实现的接口中存在同名属性时,直接访问会造成编译器无法识别。接口常量用 接口名.属性,父类属性用 super.属性

考点 2:接口常量的访问方式

interface A { int a = 1; }
class B implements A {}

// 访问方式:
System.out.println(A.a); // 正确:推荐,直接通过接口访问静态常量
System.out.println(B.a); // 正确:B 实现了 A,继承了该常量
System.out.println(new B().a); // 正确但极不推荐:通过实例访问静态变量

解析:接口中的属性隐式带有 public static final 修饰,属于类级别常量,不依附于具体的对象实例。

考点 3:接口的修饰符

java 接口的修饰符可以为()
A .private
B .protected
C .final
D .abstract

答案:D .abstract

  • 接口默认是 abstract 的,即使不写 abstract 关键字,编译器也会自动加上。

  • 接口不能是 privateprotected,因为接口设计的目的是被其他类实现,顶层接口要么是 public,要么是默认(包级私有),不能是 privateprotected

  • C .final:接口不能被 final 修饰,因为接口需要被实现,final 会阻止继承/实现。

  • 接口中方法默认是 public abstract
  • 接口中变量默认是 public static final
  • 接口本身的修饰符只有 public、默认(包级私有)、abstract(隐式)。
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐