Java 接口
本小節我們將學習 Java 接口(interface),通過本小節的學習,你將了解到什么是接口、為什么需要接口、如何定義和實現接口,以及接口的特點等內容。最后我們也將對比抽象類和接口的區別。
1. 概念
Java 接口是一系列方法的聲明,是一些方法特征的集合,一個接口只有方法的特征沒有方法的實現。
在 Java 中,被關鍵字 interface
修飾的 class 就是一個接口。接口定義了一個行為協議,可以由類層次結構中任何位置的任何類實現。接口中定義了一組抽象方法,都沒有具體實現,實現該接口的類必須實現該接口中定義的所有抽象方法。
2. 為什么需要接口
我們知道 Java 僅支持單繼承,也就是說一個類只允許有一個直接父類,這樣保證了數據的安全。Java 不支持下圖所示的多繼承:

接口就是為了解決 Java 單繼承這個弊端而產生的,雖然一個類只能有一個直接父類,但是它可以實現多個接口,沒有繼承關系的類也可以實現相同的接口。繼承和接口的雙重設計既保持了類的數據安全也變相實現了多繼承。
3. 接口的定義和實現
3.1 定義接口
3.1.1 接口聲明
使用 interface
關鍵字聲明一個接口:
public interface Person {
...
}
接口聲明需要兩個元素:interface
關鍵字和接口名稱,public
修飾符表示該接口可以在任何包的任何類中使用,如果為顯示指定訪問修飾符,則該接口只能被在同包中的類使用。
3.1.2 接口主體
接口主體中,可以定義常量和方法聲明:
public interface Person {
final String NAME = "我是Person接口中的常量";
void walk();
void run();
}
上面的 Person
就是一個接口,這個接口定義了一個常量 NAME
和兩個抽象方法 walk()
、run()
。
接口比抽象類更加 “抽象”,它下面不能擁有具體實現的方法,必須全部都是抽象方法,所有的方法默認都是 public abstract
的,所以在接口主體中的方法,這兩個修飾符無需顯示指定。
接口除了方法聲明外,還可以包含常量聲明。在接口中定義的所有的常量默認都是 public
,static
,和 final
的。
接口中的成員聲明不允許使用 private
和 protected
修飾符。
3.2 實現接口
接口定義了一些行為協議,而實現接口的類要遵循這些協議。implements
關鍵字用于實現接口,一個類可以實現一個或多個接口,當要實現多個接口時,implements
關鍵字后面是該類要實現的以逗號分割的接口名列表。其語法為:
public class MyClass implements MyInterface1, MyInterface2 {
...
}
下面是實現了 Person
接口的 Student
類的示例代碼:
public class Student implements Person {
@Override
public void walk() {
// 打印接口中的常量
System.out.println(Person.NAME);
System.out.println("學生可以走路");
}
@Override
public void run() {
System.out.println("學生可以跑步");
}
}
上述代碼中,Student
類實現了 Person
接口。值得注意的是,可以使用接口名。常量名的方式調用接口中所聲明的常量:
String name = Person.NAME;
4. 接口繼承
接口也是存在繼承關系的。接口繼承使用 extends
關鍵字。例如,聲明兩個接口 MyInterface1
和 MyInterface2
,MyInterface2
繼承自 MyInterface1
:
// MyInterface1.java
public interface MyInterface1 {
void abstractMethod1();
}
// MyInterface2.java
public interface MyInterface2 extends MyInterface1 {
void abstractMethod2();
}
當一個類實現 MyInterface2
接口,將會實現該接口所繼承的所有抽象方法:
// MyClass.java
public class MyClass implements MyInterface2 {
@Override
public void abstractMethod2() {
...
}
@Override
public void abstractMethod1() {
...
}
}
值得注意的是,一個接口可以繼承多個父接口,接口名放在 extends
后面,以逗號分割,例如:
// MyInterface1.java
public interface MyInterface1 {
void abstractMethod1();
}
// MyInterface2.java
public interface MyInterface2 {
void abstractMethod2();
}
// MyInterface3.java
public interface MyInterface3 extends MyInterface1, MyInterface2 {
void abstractMethod3();
}
補充一點,當一個實現類存在 extends
關鍵字,那么 implements
關鍵字應該放在其后:
public class MyClass extends SuperClass implements MyInterface {
...
}
5. 默認方法和靜態方法
從 JDK 1.8 開始,接口中可以定義默認方法和靜態方法。與抽象方法不同,實現類可以不實現默認方法和類方法。
5.1 默認方法
5.1.1 聲明
我們可以使用 default
關鍵字,在接口主題中實現帶方法體的方法,例如:
public interface Person {
void run();
default void eat() {
System.out.println("我是默認的吃方法");
}
}
5.1.2 調用和重寫
在實現類中,可以不實現默認方法:
public class Student implements Person {
@Override
public void run() {
System.out.println("學生可以跑步");
}
}
我們也可以在實現類中重寫默認方法,重寫不需要 default
關鍵字:
public class Student implements Person {
@Override
public void run() {
System.out.println("學生可以跑步");
}
// 重寫默認方法
@Override
public void eat() {
// 使用 接口名.super.方法名() 的方式調用接口中默認方法
Person.super.eat();
System.out.println("學生吃東西");
}
}
如果想要在實現類中調用接口的默認方法,可以使用接口名.super. 方法名 () 的方式調用。這里的 接口名.super 就是接口的引用。
5.1.3 使用場景
當一個方法不需要所有實現類都進行實現,可以在接口中聲明該方法為默認方法;使用默認方法還有一個好處,當接口新增方法時,將方法設定為默認方法,只在需要實現該方法的類中重寫它,而不需要在所有實現類中實現。
5.2 靜態方法
5.2.1 聲明
使用 static
關鍵字在接口中聲明靜態方法,例如:
public interface Person {
void walk();
// 聲明靜態方法
static void sayHello() {
System.out.println("Hello imooc!");
}
}
5.2.2 調用
類中的靜態方法只能被子類繼承而不能被重寫,同樣在實現類中,靜態方法不能被重寫。如果想要調用接口中的靜態方法,只需使用 接口名。類方法名 的方式即可調用:
public class Student implements Person {
@Override
public void walk() {
// 調用接口中的類方法
Person.sayHello();
System.out.println("學生會走路");
}
}
6. 接口和抽象類的區別
- 接口的方法默認是 public ,所有方法在接口中不能有實現(Java 8 開始接口方法可以有默認實現),而抽象類可以有非抽象的方法;
- 接口中除了 static 、final 變量,不能有其他變量,而抽象類可以;
- 一個類可以實現多個接口,但只能實現一個抽象類。接口自己本身可以通過 extends 關鍵字擴展多個接口;
- 接口方法默認修飾符是 public ,抽象方法可以有 public 、protected 和 default 這些修飾符(抽象方法就是為了被重寫所以不能使用 private 關鍵字修飾?。?;
- 從設計層面來說,抽象是對類的抽象,是一種模板設計,而接口是對行為的抽象,是一種行為的規范。
7. 多個接口中的重名成員解決方法
7.1 多個接口存在重名默認方法
例如有兩個接口 MyInteface1.java
和 MyInterface2.java
,存在相同簽名的默認方法:
public interface MyInterface1 {
default void defaultMethod() {
System.out.println("我是MyInterface1接口中的默認方法");
}
}
public interface MyInterface2 {
default void defaultMethod() {
System.out.println("我是MyInterface2接口中的默認方法");
}
}
當實現類實現兩個接口時,同名的默認方法將會發生沖突,解決辦法是在實現類中重寫這個默認方法:
public class MyClass implements MyInterface1, MyInterface2 {
public void defaultMethod() {
System.out.println("我是重寫的默認方法");
}
}
還有一種情況:實現類所繼承的父類中也存在與默認方法的同名方法,此時存在三個同名方法:
// 聲明父類,并在父類中也定義同名方法
public class SuperClass {
public void defaultMethod() {
System.out.println("我是SuperClass中的defaultMethod()方法");
}
}
// 實現類繼承父類,并實現兩個接口
public class MyClass extends SuperClass implements MyInterface1, MyInterface2 {
}
實例化 MyClass
類,調用其 defaultMethod()
方法:
MyClass myClass = new MyClass();
myClass.defaultMethod();
此時編譯執行,不會報錯:
我是SuperClass中的defaultMethod()方法
實際上,在沒有重寫的情況下,它執行了實現類的父類 SuperClass
的 defaultMethod()
方法。
7.2 多個接口中存在重名常量
例如有兩個接口,存在重名的常量:
public interface MyInterface1 {
final int NUM = 100;
}
public interface MyInterface2 {
final int NUM = 200;
}
此時在實現類中,我們可以使用接口名。常量名的方式分別調用:
public MyClass implements MyInterface1, MyInterface2 {
System.out.println(MyInterface1.NUM);
System.out.println(MyInterface2.NUM);
}
當實現類將入一個繼承關系時:
class SuperClass {
static int NUM = 300;
}
public MyClass extends SuperClass implements MyInterface1, MyInterface2 {
System.out.println(NUM);
}
當父類中的屬性或常量與接口中的常量同名時,子類無法分辨同名的 NUM
是哪一個。編譯程序將會報錯:
MyClass.java:4: 錯誤: 對NUM的引用不明確
System.out.println(NUM);
^
SuperClass 中的變量 NUM 和 MyInterface1 中的變量 NUM 都匹配
1 個錯誤
此時只有在子類中聲明 NUM
,才可以通過編譯:
public MyClass extends SuperClass implements MyInterface1, MyInterface2 {
int NUM = 3;
System.out.println(NUM);
}
8. 小結
通過本小節的學習,我們知道了 Java 的接口是為了解決其單繼承的弊端而產生的,可以使用 interface
關鍵字來聲明一個接口,接口內部不能有具體的方法實現。可以使用 implements
關鍵字來實現接口,一個接口可以繼承多個父接口,接口名放在 extends
后面,以逗號分割。從 Java 8 開始,接口中可以定義默認方法和靜態方法。