1. 什么是接口?
如果抽象类——是房子的蓝图,那么接口——更像是一份“合同”,约定要完成的工作。例如,你叫来一位水管工,你期望他能“修好水龙头”和“消除漏水”。至于他具体怎么做——你并不关心,重要的是结果。在 Java 中,接口正是这样的契约:它说明必须实现哪些方法,但不说明,如何实现。
要点速览:
接口 是 Java 中的一种特殊类型,它定义了一组方法,签署该接口的类必须实现这些方法。
- 接口只描述“做什么”,而不描述“怎么做”。
- 在 Java 8 之前,接口不包含方法的实现。
- 接口是纯契约:类一旦实现接口,就必须实现其所有方法。
为什么它很有用?
- 可以给一个类“挂载”多个接口——也就是赋予它多种“角色”。
- 有助于构建灵活且可扩展的架构,使类能够实现不同的能力。
- 接口在 Java 标准库中被广泛使用(例如,Comparable、Serializable、Runnable 等)。
2. 接口的声明语法
在 Java 中声明接口非常简单。使用关键字 interface 即可。直到 Java 8,接口中的方法默认被视为 public abstract,即使没有显式写出。这意味着:实现该接口的类必须实现这些方法。
接口示例
public interface Movable {
void move(int x, int y);
}
- 这里我们声明了接口 Movable(可理解为“可移动/会移动”)。
- 内部声明了方法 move(int x, int y)。没有方法体——只有签名。这就是“契约”:“如果你实现了 Movable,就请实现 move”。
语法要点
- 接口中的方法(在 Java 8 之前)不包含方法体。
- 接口中的所有方法默认都是 public abstract(可以不显式写出)。
- 接口只能包含常量(public static final),不能有普通字段。
包含常量的示例
public interface Constants {
int MAX_SPEED = 100; // 默认是 public static final
}
3. 接口与类的区别
接口不是类!我们来看看它们的区别。
| 类(包括抽象类) | 接口 |
|---|---|
| 可以包含字段(状态) | 不能包含字段(只能有常量) |
| 可以包含方法的实现 | 在 Java 8 之前接口不能包含实现,只能有方法签名 |
| 可以通过 new 创建(如果不是抽象类) | 不能直接创建 |
| 只允许单继承(extends) | 类可以实现多个接口(implements) |
| 用于描述“它是什么” | 用于描述“能做什么” |
类通过 implements 来实现接口
public class Robot implements Movable {
@Override
public void move(int x, int y) {
System.out.println("机器人移动到点 (" + x + ", " + y + ")");
}
}
- 类 Robot 实现了接口 Movable。
- 关键字 implements 表示“实现”。
- 必须实现接口的全部方法(否则会编译错误)。
- 别忘了注解 @Override——它不是必须的,但能帮助编译器和你避免出错。
4. 接口的使用示例
我们写一个简单的例子,看看它在实际中如何工作。假设我们有接口 Movable,并希望不同的类都能“移动”:机器人、汽车、动物……
步骤 1:声明接口
public interface Movable {
void move(int x, int y);
}
步骤 2:在类中实现接口
public class Robot implements Movable {
@Override
public void move(int x, int y) {
System.out.println("机器人移动到点 (" + x + ", " + y + ")");
}
}
步骤 3:使用接口
public class Main {
public static void main(String[] args) {
Movable m = new Robot(); // 接口类型的变量!
m.move(10, 20); // 输出:机器人移动到点 (10, 20)
}
}
请注意
- 我们可以声明一个接口类型的变量(Movable m),并将实现该接口的类的对象(new Robot())赋给它。
- 这使我们能编写泛化的代码:无需知道具体的类,只要它是“可移动”的对象即可。
未实现方法时的编译错误
Error: Class 'Robot' must either be declared abstract or implement abstract method 'move(int, int)' in 'Movable'
这正是接口的力量——它保证所有实现类都具备所需的方法。
5. Java 标准库中的接口
接口并不只是“课本上的概念”。它们在整个 Java 标准库中都被广泛使用。以下是几个示例:
- Comparable<T>——用于比较对象的接口(例如在排序时)。
- Runnable——用于启动线程的接口。
- Serializable——标记接口,表示对象可以被序列化。
- List、Set、Map——集合接口。
示例:Comparable
public class Person implements Comparable<Person> {
String name;
int age;
// 构造函数及其他方法...
@Override
public int compareTo(Person other) {
return this.age - other.age;
}
}
现在 Person 对象可以被排序,因为它实现了 Comparable 接口。
6. 可视化示意:接口如何工作
+-------------------+ +-------------------+
| interface | | class |
| Movable |<--------| Robot |
|-------------------| |-------------------|
| +move(int, int) | | +move(int, int) |
+-------------------+ +-------------------+
- 箭头表示类 Robot 实现了接口 Movable。
- 接口只规定“应该有什么”,类负责“怎样实现”。
生活中的类比
Java 中的接口就像驾驶证。如果你有驾驶证(你实现了“司机”这个接口),说明你会驾驶汽车。你具体如何驾驶——这是实施细节:有人开得稳,有人开得快,但关键是你承诺你会做这件事。
7. 实战示例:继续完善我们的应用
public interface Movable {
void move(int x, int y);
}
public class Animal implements Movable {
protected String name;
public Animal(String name) {
this.name = name;
}
@Override
public void move(int x, int y) {
System.out.println(name + " 移动到点 (" + x + ", " + y + ")");
}
}
public class Robot implements Movable {
private String model;
public Robot(String model) {
this.model = model;
}
@Override
public void move(int x, int y) {
System.out.println("机器人 " + model + " 前往坐标 (" + x + ", " + y + ")");
}
}
public class Main {
public static void main(String[] args) {
Movable[] movables = {
new Animal("Spot"),
new Robot("R2D2")
};
for (Movable m : movables) {
m.move(5, 10);
}
}
}
运行结果:
Spot 移动到点 (5, 10)
机器人 R2D2 前往坐标 (5, 10)
我们创建了一个 Movable 类型对象的数组。里面可以放任何实现该接口的对象!循环中的代码并不知道它面对的是动物还是机器人——但知道它们都会 move。
8. 使用接口时的常见错误
错误 1:试图直接创建接口的对象。 接口是契约,而不是具体对象。不能写 new Movable()——这会导致编译错误。应该创建实现该接口的类的对象。
错误 2:没有实现接口的所有方法。 如果类实现了接口却没有实现其所有方法,编译器会报错:“Class must either be declared abstract or implement abstract method ...”。如果你不想实现所有方法——就把类声明为 abstract。
错误 3:忽略访问修饰符。 接口方法始终是 public(即使没有显式写出)。在实现类中不能降低可见性级别,因此方法也必须是 public。
错误 4:在接口中添加普通字段。 接口中只能声明常量(public static final)。不能添加普通(非静态)字段。
GO TO FULL VERSION