什么是接口?
在 Java 中,接口(Interface) 是一种引用数据类型,它和 class(类)处于同一层次,你可以把它理解为一份 “契约” 或 “规范”。

这份契约定义了一组方法(方法签名),但没有提供具体实现,任何类 implements(实现)了这个接口,就必须遵守契约,提供所有这些方法的具体实现。
核心思想:
- 定义“能做什么” (What):接口只声明方法,不关心“怎么做”(How)。
- 实现“具体怎么做”:具体的类来实现接口,提供方法体。
这完美地体现了面向对象编程中的 “面向接口编程” 思想,它极大地提高了代码的灵活性、可扩展性和解耦性。
接口的语法定义
1 基本语法
// [public] interface 接口名 [extends 父接口1, 父接口2, ...] {
// // 零个或多个常量定义
// // 零个或多个抽象方法定义
// }
2 一个简单的例子
我们先来看一个最基础的接口定义。

Flyable.java - 定义一个“飞行”的能力接口
/**
* 定义一个飞行能力的接口
* 这是一个规范,任何能飞的物体都必须实现这个接口。
*/
public interface Flyable {
// 1. 接口中可以定义常量
// 注意:常量默认是 public static final 的,所以可以省略修饰符
double MAX_SPEED = 1000.0; // 最大速度
double MIN_SPEED = 0.0; // 最小速度
// 2. 接口中可以定义抽象方法
// 注意:抽象方法默认是 public abstract 的,所以可以省略修饰符
void fly(); // 飞行的方法
// 在 Java 8 之前,接口中只能有抽象方法和常量
}
接口的核心特性
在 Java 8 之前,接口的特性非常严格,但从 Java 8 开始,接口的能力得到了极大的增强。
1 Java 8 之前的特性
-
public static final常量:- 接口中定义的变量,默认被
public,static,final修饰。 - 这意味着它们是 公共的、静态的(属于接口本身,而不是实例)、最终的(常量,不可修改)。
- 通常我们直接写
int AGE = 18;,编译器会自动加上这三个修饰符。
- 接口中定义的变量,默认被
-
public abstract抽象方法:
(图片来源网络,侵删)- 接口中定义的方法,默认被
public,abstract修饰。 - 这意味着它们是 公共的、抽象的(没有方法体,由实现类提供具体逻辑)。
- 通常我们直接写
void doSomething();。
- 接口中定义的方法,默认被
-
不能有构造方法:
接口不能被实例化,因此它没有构造方法。
-
一个类可以实现多个接口:
- 这是 Java 实现多重继承能力的方式,一个类可以同时遵守多个“契约”。
- 语法:
class ClassName implements InterfaceA, InterfaceB, ...
2 Java 8 引入的新特性(重要更新)
为了支持 函数式编程 和 Lambda 表达式,Java 8 为接口引入了新的成员。
-
默认方法:
- 使用
default关键字修饰,可以拥有方法体。 - 作用:允许接口提供方法的默认实现,这样,实现类可以不必重写这个方法,直接使用接口提供的默认实现。
- 解决了接口演化问题:可以在不破坏现有实现类代码的情况下,向接口中添加新方法。
public interface Flyable { void fly(); // 默认方法 default void takeOff() { System.out.println("准备起飞..."); } } - 使用
-
静态方法:
- 使用
static关键字修饰,可以拥有方法体。 - 作用:提供与接口相关的工具性方法,它们属于接口本身,不能被实现类继承或重写。
- 调用方式:
InterfaceName.staticMethodName();
public interface Flyable { // 静态方法 static void logFlightInfo() { System.out.println("记录飞行日志..."); } } - 使用
3 Java 9 引入的新特性
-
私有方法:
- 使用
private关键字修饰,可以拥有方法体。 - 作用:提高代码复用性,当一个接口中有多个默认方法或静态方法,并且它们包含一些共同的逻辑代码时,可以将这些公共代码提取到私有方法中,供其他接口方法调用。
- 私有方法分为两类:
private:供接口中的默认方法和私有方法调用。private static:供接口中的静态方法和私有静态方法调用。
public interface Flyable { default void flyAndLand() { System.out.println("开始飞行..."); commonFlightLogic(); // 调用私有方法 System.out.println("安全降落。"); } // 私有方法 private void commonFlightLogic() { System.out.println("执行通用的飞行逻辑..."); } } - 使用
接口的实现
一个类使用 implements 关键字来实现一个或多个接口。
Bird.java - 实现飞行接口
// Bird 类实现了 Flyable 接口
public class Bird implements Flyable {
@Override
public void fly() {
System.out.println("小鸟用翅膀飞翔,速度很快。");
}
// Bird 类可以选择不重写 takeOff() 方法,直接使用 Flyable 接口提供的默认实现
}
Airplane.java - 实现飞行接口
// Airplane 类也实现了 Flyable 接口
public class Airplane implements Flyable {
@Override
public void fly() {
System.out.println("飞机通过引擎和机翼高速飞行。");
}
// Airplane 类也可以选择重写默认方法,提供自己的实现
@Override
public void takeOff() {
System.out.println("飞机在跑道上加速,然后起飞。");
}
}
Test.java - 测试
public class Test {
public static void main(String[] args) {
Flyable bird = new Bird();
bird.fly(); // 输出: 小鸟用翅膀飞翔,速度很快。
bird.takeOff(); // 输出: 准备起飞... (使用默认实现)
Flyable airplane = new Airplane();
airplane.fly(); // 输出: 飞机通过引擎和机翼高速飞行。
airplane.takeOff(); // 输出: 飞机在跑道上加速,然后起飞。 (重写后的实现)
// 调用接口的静态方法
Flyable.logFlightInfo(); // 输出: 记录飞行日志...
}
}
接口与抽象类的区别
这是一个非常经典和重要的问题,它们既有相似之处,又有本质区别。
| 特性 | 接口 | 抽象类 |
|---|---|---|
| 继承/实现 | implements,一个类可以实现多个接口 |
extends,一个类只能继承一个抽象类(单继承) |
| 构造方法 | 没有构造方法 | 有构造方法,子类在创建时会先调用父类的构造方法 |
| 方法 | - Java 8 之前:只有抽象方法 - Java 8+:默认方法、静态方法、私有方法 |
可以包含抽象方法和具体方法(有方法体的普通方法) |
| 成员变量 | 只能是 public static final 常量 |
可以是各种类型的变量(普通成员变量、静态变量等) |
| 设计目的 | 规范,定义一种能力或行为,强调“是什么”(What),如 Runnable, Serializable |
模板,定义一个事物的基础结构,强调“是什么”以及“部分怎么做”(How),如 Animal |
| 访问修饰符 | 方法和常量默认是 public |
方法和变量可以是 public, protected, private |
简单总结:
- 当你需要定义一种能力或行为规范,并且希望任何类都能自由地实现这种能力时,使用接口。
- 当你定义一个“事物”的基类,这个基类有一些共同的状态(成员变量)和部分共同的行为(方法实现),并且希望子类继承和扩展时,使用抽象类。
Java 接口是一个强大且灵活的特性,它的定义和功能随着 Java 版本的更新而不断演进:
- Java 7 及以前:纯粹的“契约”,包含常量和抽象方法。
- Java 8:引入了 默认方法 和 静态方法,增强了接口的功能,使其更易于扩展和提供工具方法。
- Java 9:引入了 私有方法,提高了接口内部代码的复用性。
理解接口的定义和特性是掌握 Java 面向对象编程和现代 Java 开发(如 Spring 框架)的关键,它为实现 解耦、多态 和 面向接口编程 提供了坚实的基础。
