接口
概述
接口(Interface)是 Java 中定义能力契约的一种类型:它只声明「能做什么」(方法签名),而不关心「怎么做」(不包含方法体,除非使用 default/static 实现)。类通过 implements 关键字实现接口,并必须提供接口中所有抽象方法的实现。一个类可以实现多个接口,从而具备多种能力,这是 Java 实现「多继承」效果的主要方式。接口类型的引用可以指向任意实现类对象,与 多态 结合,实现「面向接口编程」,提高可扩展性和解耦。
通俗理解:接口是一份「能力清单」——规定实现类必须提供哪些方法;谁实现了这份清单,谁就可以被当作该接口类型使用。接口与 抽象类 都能表达抽象与多态,但接口更侧重「契约」、无状态,且支持多实现。
为什么需要接口
- 定义能力契约:只声明方法签名,不关心实现细节,调用方只依赖「能调什么」,不依赖具体类。
- 实现多「能力」:Java 类只能单继承,但可以实现多个接口,从而同时具备多种角色(如「可比较」「可序列化」「可关闭」)。
- 解耦与可替换:依赖接口而非具体实现类,便于替换实现(如测试时注入 Mock、切换不同数据库驱动)。
- 与抽象类的分工:接口无实例字段、无构造方法,纯「行为契约」;抽象类可有状态和部分实现,适合「是一种」的层次。需要多继承能力或纯契约时优先用接口。
基本语法
声明接口
使用 interface 关键字,方法默认为 public abstract(可省略),不能有方法体(JDK 8 前);从 JDK 8 起可包含 default 和 static 方法并带实现。
// 接口中的方法默认就是 public abstract,无需写修饰符
public interface Drawable {
void draw(); // 等价于 public abstract void draw();
}实现接口
类使用 implements 实现接口,必须实现接口中所有抽象方法(未实现的 default 方法可选重写)。
public class Circle implements Drawable {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public void draw() {
System.out.println("Drawing a circle with radius " + radius);
}
}
// 接口引用指向实现类对象,多态
Drawable d = new Circle(3.0);
d.draw(); // Drawing a circle with radius 3.0注意
实现接口时,实现方法的访问权限不能比接口中的更严格。接口方法默认为 public,因此实现类中的实现也必须是 public。
多实现
一个类可以同时实现多个接口,用逗号分隔;必须实现每个接口中的全部抽象方法。
public interface Drawable {
void draw();
}
public interface Resizable {
void resize(double scale);
}
public class Rectangle implements Drawable, Resizable {
private double width, height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public void draw() {
System.out.println("Drawing a rectangle " + width + "x" + height);
}
@Override
public void resize(double scale) {
this.width *= scale;
this.height *= scale;
}
}
// 既可当作 Drawable,也可当作 Resizable
Drawable d = new Rectangle(2, 3);
Resizable r = new Rectangle(2, 3);
d.draw();
r.resize(1.5);接口的成员
| 成员类型 | 说明与语法 |
|---|---|
| 抽象方法 | 无方法体,实现类必须实现;默认 public abstract |
| 常量 | 用 public static final 声明,可省略修饰符(默认即如此) |
| default 方法 | JDK 8+,带方法体,实现类可不重写,用于接口演进 |
| static 方法 | JDK 8+,带方法体,通过接口名直接调用 |
常量
接口中的字段默认是 public static final,即常量,通常用大写命名。
public interface Config {
int MAX_SIZE = 100; // 等价于 public static final int MAX_SIZE = 100;
String DEFAULT_ENCODING = "UTF-8";
}
// 使用:Config.MAX_SIZE、Config.DEFAULT_ENCODINGdefault 方法(JDK 8+)
为接口增加 default 方法可提供默认实现,已有实现类不必强制重写,便于接口向后扩展。
public interface Greeter {
void sayHello(String name); // 抽象方法,实现类必须实现
// default 方法:有默认实现,实现类可选重写
default void sayBye() {
System.out.println("Goodbye!");
}
}
public class SimpleGreeter implements Greeter {
@Override
public void sayHello(String name) {
System.out.println("Hello, " + name);
}
// 未重写 sayBye(),将使用接口中的默认实现
}
SimpleGreeter g = new SimpleGreeter();
g.sayHello("Java"); // Hello, Java
g.sayBye(); // Goodbye!static 方法(JDK 8+)
接口中可以定义 static 方法,通过接口名调用,不能通过实现类实例调用(与类的静态方法一致)。
public interface MathUtil {
static int add(int a, int b) {
return a + b;
}
}
int sum = MathUtil.add(1, 2); // 3,通过接口名调用接口继承接口
接口可以继承多个其他接口(用 extends),从而组合多份契约;实现类只需实现最终接口中的全部抽象方法。
public interface Drawable {
void draw();
}
public interface Colorable {
void setColor(String color);
}
// 接口多继承:具备 draw + setColor 的契约
public interface DrawableAndColorable extends Drawable, Colorable {
}
public class ColoredCircle implements DrawableAndColorable {
private double radius;
private String color = "black";
public ColoredCircle(double radius) {
this.radius = radius;
}
@Override
public void draw() {
System.out.println("Drawing a " + color + " circle, radius " + radius);
}
@Override
public void setColor(String color) {
this.color = color;
}
}使用示例
示例 1:面向接口编程(多态)
方法参数和返回值使用接口类型,调用方可以传入任意实现类,便于扩展和测试。
public interface Logger {
void log(String message);
}
public class ConsoleLogger implements Logger {
@Override
public void log(String message) {
System.out.println("[CONSOLE] " + message);
}
}
public class FileLogger implements Logger {
@Override
public void log(String message) {
// 实际可写入文件,此处仅示例
System.out.println("[FILE] " + message);
}
}
// 面向接口编程:不依赖具体 Logger 实现
public class Service {
private final Logger logger;
public Service(Logger logger) {
this.logger = logger;
}
public void doWork() {
logger.log("Service started.");
// ... 业务逻辑
logger.log("Service finished.");
}
}
// 使用:可注入不同实现
Service s1 = new Service(new ConsoleLogger());
Service s2 = new Service(new FileLogger());
s1.doWork(); // 输出到控制台
s2.doWork(); // 输出到「文件」示例 2:多实现表达多种能力
一个类实现多个接口,在不同场景下被当作不同「角色」使用。
public interface Runnable {
void run();
}
public interface Swimmable {
void swim();
}
public class Duck implements Runnable, Swimmable {
@Override
public void run() {
System.out.println("Duck runs on land.");
}
@Override
public void swim() {
System.out.println("Duck swims in water.");
}
}
Duck duck = new Duck();
Runnable r = duck;
Swimmable s = duck;
r.run(); // Duck runs on land.
s.swim(); // Duck swims in water.示例 3:接口中的 default 方法(JDK 8+)
用 default 方法在接口中提供通用逻辑,减少实现类重复代码。
public interface ListFormatter {
String format(java.util.List<String> list); // 抽象方法:如何格式化由实现类决定
// default:提供通用「带标题」的格式,实现类可直接使用或重写
default String formatWithTitle(String title, java.util.List<String> list) {
return title + ":\n" + format(list);
}
}
public class CommaFormatter implements ListFormatter {
@Override
public String format(java.util.List<String> list) {
return String.join(", ", list);
}
}
ListFormatter f = new CommaFormatter();
String result = f.formatWithTitle("Items", java.util.List.of("A", "B", "C"));
// Items:
// A, B, C接口与抽象类的区别
| 特性 | 接口 | 抽象类 |
|---|---|---|
| 关键字 | interface | abstract class |
| 继承/实现数量 | 类可实现多个接口 | 类只能继承一个抽象类 |
| 实例字段 | 不能有(只能有常量) | 可以有 |
| 构造方法 | 不能有 | 可以有 |
| 方法 | 抽象方法 + default + static | 抽象方法 + 普通方法 |
| 主要用途 | 能力契约、多角色、解耦 | 「是一种」层次、模板、复用 |
提示
需要多继承能力或纯行为契约、无状态时用接口;需要共同状态(字段)或部分已实现逻辑作为模板时用抽象类。很多 API 同时提供接口(如 List)和抽象类(如 AbstractList),接口对外契约,抽象类提供默认实现。
注意事项
- 实现类必须实现接口中全部抽象方法:否则该类必须声明为
abstract。 - 实现方法必须为 public:接口方法默认为 public,实现类不能缩小为 package-private 或 private。
- 多实现时方法签名冲突:若两个接口中有同名同参的 default 方法,实现类必须重写该方法并明确选择调用哪个接口的 default(如
InterfaceA.super.method()),否则编译报错。 - 接口不能实例化:不能
new Drawable(),只能new实现类,再赋给接口引用。 - 接口与多态:接口引用指向实现类对象时,调用的是实现类的方法,与「父类引用指向子类对象」的多态规则一致。
注意
实现接口时建议对每个实现方法使用 @Override 注解,便于编译器检查方法签名是否与接口一致,避免误写重载而非实现。
相关链接
- 抽象类 — 接口与抽象类的区别与选择
- 多态 — 接口引用指向实现类,同样体现多态
- 继承 — 类可同时继承一个类并实现多个接口
- Oracle Java 教程 - 接口