Java 接口(Interface)
1.接口是什么
在面向对象编程中,抽象类可以定义抽象方法,但 Java 提供的 接口(Interface) 进一步强化了“规范与实现分离”的思想。它定义了一组方法签名,但不包含方法的具体实现。
接口不是类,而是一组方法规范的集合。编写接口的方式和类很相似,但是它们属于不同的概念。
2.接口的实现
接口和类之间是实现关系,一个类使用 implements 关键字可以实现一个(单实现)或多个(多实现)接口。
2.1 单实现(Single Interface Implementation)
-
规则:类可以实现一个接口。
-
语法:
// 单实现语法 public class [类名] implements [接口名] { // 必须实现接口中定义的所有抽象方法 } -
说明:实现类如果是普通类(非抽象类),必须 重写接口中的所有抽象方法;如果实现类本身是抽象类,则可以选择性实现部分方法,剩下的留给子类实现。
2.2 多实现(Multiple Interface Implementation)
-
规则:类可以实现 多个接口,接口之间用逗号分隔。
-
语法:
// 多实现语法 public class [类名] implements [接口名1], [接口名2]{ // 必须实现所有接口中未实现的抽象方法 } -
示例:
public interface Flyable { void fly(); } public interface Swimmable { void swim(); } public class Duck implements Flyable, Swimmable { public void fly() { System.out.println("Duck flies"); } public void swim() { System.out.println("Duck swims"); } } -
说明:这种方式可以让一个类同时拥有多个接口的功能。
2.3 继承与实现结合
-
规则:一个类可以继承另一个类的同时实现一个或多个接口。
-
语法:
public class [子类名] extends [父类名] implements [接口名1], [接口名2] { // 必须实现所有接口的抽象方法(若类非抽象) } -
说明:在这种情况下,类继承得到父类的行为,同时通过接口获得额外的功能约束。
3.接口的成员及其特点(Java 8 及以后)
1.接口中可以包含常量和方法
- 变量默认是
public static final 常量。 - 方法默认是
public abstract(即使省略修饰符)。
2.接口不能声明构造方法,因为接口不能被直接实例化。
在早期(JDK8 之前),接口里只能放常量和抽象方法,这导致一个问题:如果接口需要增加新功能,那么所有实现类都必须去修改代码,否则编译会报错。为了解决这种“接口扩展困难”的问题:
JDK 8 引入默认方法和静态方法
默认方法可以让实现类选择性覆盖;静态方法则用来放置和接口相关的工具方法。
这两个特性主要解决了向后兼容性和接口演进的问题:接口带上方法实现,避免因为接口扩展而破坏已有实现类。
默认方法
默认方法使用关键字 default修饰,允许在接口中提供一个带方法的实现。这样在接口中添加新功能时,已有的实现类可以直接继承这个默认实现,无需修改;也可以选择性地覆盖(Override)这个方法。
最典型的应用: Java 8 中对 Collection 接口的增强(例如加入了 forEach 方法)就是通过默认方法实现的。
接口中默认方法的定义格式:
public default [返回值类型] [方法名](参数列表){ }
示例:
public default void show(){ }
接口中默认方法的注意事项:
- 默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉
default关键字 - public可以省略,default不能省略
- 如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
在这里插入代码片// 定义接口 A
interface A {
default void show() {
System.out.println("A: show()");
}
}
// 定义接口 B
interface B {
default void show() {
System.out.println("B: show()");
}
}
// 实现类同时实现 A 和 B
public class Demo implements A, B {
// 注意:
// 1. 必须重写 show(),因为 A 和 B 的默认方法冲突
// 2. 重写时去掉 default 关键字
@Override
public void show() {
// 可以选择调用其中一个接口的默认实现
A.super.show();
// 也可以加上自己的逻辑
System.out.println("Demo: show()");
}
public static void main(String[] args) {
Demo d = new Demo();
d.show(); // 输出:A: show() \n Demo: show()
}
}
默认方法解决了在不破坏现有实现类的情况下,向接口中添加新方法的难题:想象一个已被成百上千个类实现的大型接口。如果直接在其中添加一个抽象方法,那么所有现有的实现类都会立即编译报错,因为它们没有实现这个新方法。
静态方法
静态方法使用关键字static修饰,调用时,必须通过 接口名 直接调用。
接口中静态方法的定义格式:
public static [返回值类型] [方法名](参数列表){ }
示例:
public static void show(){ }
接口中静态方法的注意事项:
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
- public可以省略,
static不能省略 - 静态方法不能被重写,非要写的话属于是重新定义。因为
static、private、final修饰的不在虚方法表中,不能被重写。
// 定义接口
interface Animal {
static void info() {
System.out.println("Animal interface static method");
}
}
// 实现类
class Dog implements Animal {
// 注意:这并不是“重写”,而是重新定义了一个静态方法
static void info() {
System.out.println("Dog class static method");
}
}
public class Main {
public static void main(String[] args) {
// 正确调用接口的静态方法:只能用接口名
Animal.info();
// 不能通过对象调用接口的静态方法
Animal a = new Dog();
// a.info(); // 编译错误:静态方法不能通过实例调用
// 不能通过实现类调用接口的静态方法(调用到的其实是类自己的方法)
Dog.info(); // 调用的是 Dog 自己定义的静态方法
}
}
输出:
Animal interface static method
Dog class static method
JDK 9 引入私有方法
这个特性主要是为了提高接口内部的代码复用,当接口中有多个默认方法或静态方法需要共享逻辑时,就可以把公共部分抽取到私有方法里,实现代码复用。方法对外不可见。
接口中私有方法的定义格式:
- 普通私有方法,为默认方法服务
private [返回值类型] [方法名](参数列表){ } - 静态私有方法,为静态方法服务
private static [返回值类型] [方法名](参数列表){ }
示例:
private void show(){ }private static void method(){ }
public interface DataProcessor {
// 默认方法 1:处理一个字符串
default void processString(String data) {
logStart("processString");
String processedData = sanitize(data);
System.out.println("String Result: " + processedData.toUpperCase());
}
// 默认方法 2:处理一个数字
default void processNumber(int number) {
logStart("processNumber");
String data = "Number:" + number;
String processedData = sanitize(data);
System.out.println("Number Result: " + processedData.length());
}
// --- 私有普通方法 (为默认方法复用逻辑) ---
private String sanitize(String input) {
if (input == null || input.trim().isEmpty()) {
return "DEFAULT_DATA";
}
return input.trim();
}
// --- 私有静态方法 (为默认方法和静态方法复用通用逻辑) ---
private static void logStart(String methodName) {
System.out.println("--- Starting " + methodName + " ---");
}
// --- 静态方法 1 ---
public static boolean isValidKey(String key) {
return isCommonValid(key, 5);
}
// --- 静态方法 2 ---
public static boolean isStrongKey(String key) {
return isCommonValid(key, 10);
}
// --- 私有静态方法 (为静态方法复用逻辑) ---
private static boolean isCommonValid(String value, int minLength) {
if (value == null || value.length() < minLength) {
System.out.println("Key too short (Min: " + minLength + ")");
return false;
}
return true;
}
}
私有普通方法 sanitize(String input):它被两个 默认方法 (processString 和 processNumber) 调用,实现了对输入数据统一清洗逻辑的复用。
私有静态方法 isCommonValid(String value, int minLength):它被两个 静态方法 (isValidKey 和 isStrongKey) 调用,实现了统一的关键码长度校验逻辑的复用。
总结
| 成员类型 | 默认修饰符 | 特点 | 示例 |
|---|---|---|---|
| 常量 | public static final |
必须在声明时初始化。 | int MAX_COUNT = 10; |
| 抽象方法 | public abstract |
没有方法体,由实现类提供具体实现。 | void doSomething(); |
| 默认方法 (Java 8以后) | public default |
有方法体,实现类可以选择性覆盖。 | default void log() { ... } |
| 静态方法 (Java 8以后) | public static |
有方法体,只能通过接口名直接调用。 | static void utility() { ... } |
| 私有方法 (Java 9以后) | private |
有方法体,用于接口内部默认方法和静态方法的代码重用。 | private void helper() { ... } |
4.接口的应用
4.1 将多个类会用到的规则定义为接口
接口代表规则,是行为的抽象。想要让哪个类拥有一个行为,就让这个类实现对应的接口就可以了
// 规则定义:可飞行的接口
public interface Flyable {
// 规则:所有可飞行的东西都必须能执行 fly() 这个动作
void fly();
}
// 实现类 1: 飞机类遵循 Flyable 规则
public class Airplane implements Flyable {
@Override
public void fly() {
System.out.println("飞机在喷气动力的驱动下飞行。");
}
}
// 实现类 2: 鸟类遵循 Flyable 规则
public class Bird implements Flyable {
@Override
public void fly() {
System.out.println("鸟在扇动翅膀的驱动下飞行。");
}
}
4.2 接口作为方法参数
当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称之为接口多态。
public class Observer {
/**
* 方法参数是接口类型 (Flyable)。
* 它可以接受任何实现了 Flyable 接口的类的对象。
* @param target 任何一个可飞行的对象
*/
public void watch(Flyable target) {
System.out.println("观察者开始观察一个可飞行的目标...");
// 尽管不知道传入的是飞机还是鸟,但可以确定它有 fly() 行为
target.fly();
System.out.println("观察结束。");
}
}
// 测试类
public class TestApplication {
public static void main(String[] args) {
Observer observer = new Observer();
// 传入 Airplane 对象
Airplane myPlane = new Airplane();
observer.watch(myPlane);
// 输出:
// 观察者开始观察一个可飞行的目标...
// 飞机在喷气动力的驱动下飞行。
// 观察结束。
System.out.println("--------------------");
// 传入 Bird 对象
Bird myBird = new Bird();
observer.watch(myBird);
// 输出:
// 观察者开始观察一个可飞行的目标...
// 鸟在扇动翅膀的驱动下飞行。
// 观察结束。
}
}
补充: 接口与抽象类的对比
接口和抽象类都是实现抽象(Abstraction) 的重要手段,但它们在定义、设计意图和使用上存在关键区别。
相同点
-
不能直接实例化: 两者都不能被直接创建对象(new)。它们都必须由子类或实现类来完善和使用。
-
抽象性: 它们都可以包含抽象方法(没有方法体的方法),要求子类/实现类去实现。
-
多态性: 都可以作为类型引用,指向其子类/实现类的实例,从而实现多态。
不同点(关键区别)
| 特性 | 接口(Interface) | 抽象类(Abstract Class) |
|---|---|---|
| 关键字 | interface |
abstract class |
| 继承关系 | 类可以实现(implements)多个接口。 |
类只能单继承(extends)一个抽象类。 |
| 构造器 | 不能有构造器。 | 可以有构造器(但不能直接调用 new,仅供子类调用)。 |
| 成员变量 | 只能有 public static final 常量。 |
可以有各种访问修饰符的变量。 |
| 方法 | JDK 7:只能有抽象方法 JDK 8+:可有 default、static 方法 JDK 9+:支持私有方法 | 可以包含 抽象方法 和 普通方法(有方法体的方法)。 |
| 设计意图 | 定义能力/行为规范(做什么),适用于不相关的类共享同一行为。 | 定义类族共同的基类(是什么),适用于有密切关系的类。 |
| 实现细节 | 在 Java 8 以前,不能包含任何实现细节。 | 可以包含部分实现细节(有方法体的方法)。 |
更多推荐



所有评论(0)