【Java SE】接口 (Interface)
接口
接口是 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 关键字来实现接口。
- 普通类实现接口: 必须重写(实现)接口中所有的抽象方法。
- 抽象类实现接口: 可以不实现或只实现部分抽象方法,剩下的留给子类去完成。
- 多实现: 一个类可以同时实现多个接口(例如
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关键字,编译器也会自动加上。 -
接口不能是
private、protected,因为接口设计的目的是被其他类实现,顶层接口要么是public,要么是默认(包级私有),不能是private或protected。 -
C .final:接口不能被
final修饰,因为接口需要被实现,final会阻止继承/实现。
- 接口中方法默认是
public abstract。- 接口中变量默认是
public static final。- 接口本身的修饰符只有
public、默认(包级私有)、abstract(隐式)。
更多推荐




所有评论(0)