
意义
1. 规范行为:定义契约
接口只定义方法的规范(方法名、参数、返回值) ,不提供实现。
类在实现接口时必须提供这些方法的实现,因此接口起到约束和规范的作用。
2. 多态性
接口是 Java 实现多态的重要机制之一。
通过接口类型引用对象,可以在不修改代码的情况下替换实际实现,提升代码的灵活性与扩展性。
3. 多重继承的补充
Java 类不能多继承,但可以实现多个接口 。
接口弥补了单继承的不足,让类可以同时具备多种能力。
4. 解耦与模块化
接口将"做什么"与"怎么做"分离,使代码结构更松散耦合,方便替换实现、单元测试、维护与重构。
5. API 设计的基础
Java 中许多框架(如 Servlet、JDBC、Spring)广泛使用接口来统一标准,让不同厂商或开发者提供不同实现而不影响使用。
定义格式
java
[public/缺省] interface 接口名 [extends 其他接口...] {
// 常量(隐式的 public static final 可省略)
// 抽象方法(隐式的 public abstract 可省略,不能有{})
// 默认方法(default Java 8+ 隐式的 public 可省略)
// 静态方法(static Java 8+ 隐式的 public 可省略)
// 私有方法(private Java 9+ 必须有函数体,可为空)
}
// 接口中没有构造函数、初始化块,因为没有成员变量需要动态初始化
java
public interface Flyable {
// int MAX_HEIGHT; 编译错误
// private/protected ...; 编译错误
int MAX_HEIGHT = 1000;
void fly(); // 抽象方法
void fly2() {}; // 编译错误
// private/protected default ... 编译错误
default void start() {
System.out.println("Start flying...");
}
static void check() {
System.out.println("Checking...");
}
private static void checks() {
System.out.println("Checkings...");
}
private void helper() { // Java 9+
System.out.println("Helper");
}
}
!NOTE
接口也会被编译成
.class文件,但一定要明确它并不是类,也是一种引用数据类型。
接口的实现
类实现接口与继承类似,使用 implements关键字。
Java 不支持多继承类,但支持多实现接口。
java
[修饰符] class 实现类 implements 接口1, 接口2, 接口3, ... {
// 必须重写接口中所有抽象方法。如果实现类是抽象类,可以不重写。
// 可以重写接口中默认方法,要去掉default。不重写时,直接继承 default 方法。
}
实现类可以继承父类同时实现接口,必须继承在前。
java
[修饰符] class 实现类 extends 父类 implements 接口1, 接口2, 接口3, ... {
...
}
- 当一个类实现接口时,该类必须实现接口中的所有抽象方法。否则这个类必须声明为
abstract。 - 接口里的静态方法/常量必须用接口名(
接口名.xxx)调用,不能被实现类继承或重写,不能通过实现类调用。
接口的继承
- 接口之间可以继承,使用
extends关键字。 - 一个接口可以继承多个接口(接口多继承)。
- 接口继承的是抽象方法、默认方法、静态方法和常量。
java
interface A {
void methodA();
}
interface B extends A {
void methodB();
}
interface C extends A, B {
void methodC();
}
// 实现 C 的类
class MyClass implements C {
@Override
public void methodA() {
System.out.println("A 方法实现");
}
@Override
public void methodB() {
System.out.println("B 方法实现");
}
@Override
public void methodC() {
System.out.println("C 方法实现");
}
}
public class Test {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.methodA();
obj.methodB();
obj.methodC();
}
}
- 类实现接口时,需要实现接口继承链中所有的抽象方法。
- 子接口可重写父接口的默认方法,不要去掉
default。
接口与实现类
接口本身不能直接创建对象,但可以通过实现类、匿名内部类、lambda 表达式(函数式接口)来创建接口的实例。
java
InterfaceA obj = new InterfaceA(); // ❌ 错误:接口不能直接实例化
java
interface A {
void run();
}
class B implements A {
public void run() {
System.out.println("running");
}
}
A obj = new B(); // ✔️ OK
obj.run();
java
A obj = new A() {
@Override
public void run() {
System.out.println("running");
}
};
java
@FunctionalInterface
interface A {
void run();
}
A obj = () -> System.out.println("running"); // ✔️ OK
🌠类实现接口,类似于继承。因此,接口类型的变量与实现类对象之间可构成多态引用。
java
interface Animal {
void cry();
}
class Dog implements Animal {
public void cry() {
System.out.println("Dog barks");
}
}
Animal a = new Dog(); // 多态引用
a.cry(); // 调用 Dog 的实现
相关冲突问题
0、接口之间抽象方法重名
- 两个或多个接口包含相同签名的抽象方法时,子类只需实现一次。
java
interface A {
void run();
}
interface B {
void run();
}
class C implements A, B {
@Override
public void run() {
System.out.println("run from C");
}
}
public class Demo3 {
public static void main(String[] args) {
new C().run();
}
}
1、父类方法 vs. 接口抽象方法冲突
- 如果父类中已经提供了某个方法的实现,而接口中声明了同名同参数的抽象方法甚至是提供了默认方法,则父类方法优先。子类不需要重新实现接口中的该抽象方法或覆盖接口的默认方法。
java
interface A {
void hello(); // 抽象方法
// or
/*
default void hello() { // 默认方法
System.out.println("Hello from A");
}
*/
}
class Parent {
public void hello() {
System.out.println("Hello from Parent");
}
}
class Child extends Parent implements A {
// 不需要实现 A.hello(),因为 Parent 已提供实现
// 或覆盖接口的默认方法
}
public class Demo {
public static void main(String[] args) {
new Child().hello(); // Hello from Parent
}
}
2、多个接口间默认方法重名冲突
- 当多个接口具有相同签名的默认方法 时,子类必须显式地覆盖方法,否则编译错误;子接口本身不会编译错误,但实现这个子接口的类必须解决方法冲突,或在子接口中重新定义默认实现。
- 子类可以选择调用某个接口的默认实现,写法
InterfaceName.super.methodName();
java
interface A {
default void hello() {
System.out.println("Hello from A");
}
}
interface B {
default void hello() {
System.out.println("Hello from B");
}
}
class C implements A, B {
@Override
public void hello() {
// 解决冲突:必须明确选择一个父接口的默认方法
A.super.hello();
// 或 B.super.hello();
}
}
java
interface C extends A, B {
// 编译通过:不强制重写 hello()
// 但实现该接口的类必须解决冲突
}
class D implements C {
// 编译错误:hello() 有冲突,必须解决
}
class D implements C {
@Override
public void hello() {
// 显示调用哪个接口的默认实现
A.super.hello(); // 或者 B.super.greet();
}
}
// or
interface C extends A, B {
@Override
default void hello() {
A.super.hello(); // 或者 B.super.greet(),或自己写
}
}
class D implements C {
// 不需要重写 hello()
}
3、常量冲突
- 如果多个接口中存在同名常量,或父类与父接口中存在同名常量,将会发生字段隐藏冲突。
java
public class Father {
int x = 1;
}
java
public interface A {
int x = 2;
int y = 2;
}
java
public interface B {
int x = 3;
}
java
public class Child extends Father implements A, B {
public void method() {
// System.out.println("x = " + x); // 模糊不清
System.out.println("super.x = " + super.x);
System.out.println("A.x = " + A.x);
System.out.println("B.x = " + B.x);
System.out.println("y = " + y); // 没有重名问题,可以直接访问
}
}
java
interface A {
int VALUE = 10;
}
interface B {
int VALUE = 20;
}
class C implements A, B {
public void print() {
// 必须用接口名区分
System.out.println(A.VALUE);
System.out.println(B.VALUE);
}
}