文章目錄
抽象類是從多個類中抽象出來的模闆,如果将這種抽象進行得更徹底,則可以提煉出一種更加特殊的“抽象類”——接口(interface),接口裡不能包含普通方法,接口裡的所有方法都是抽象方法。Java 8 對接口進行了改進,允許在接口中定義預設方法,預設方法可以提供方法實作。
接口是從多個相似類中抽象出來的規範 , 接口不提供任何實作 。 接口展現的是規範和實作分離的設計哲學 。
讓規範和實作分離正是接口的好處 , 讓軟體系統的各元件之間面向接口稠合,是一種松藕合的設計 。例如主機闆上提供了 PCI 插槽,隻要一塊顯示卡遵守 PCI 接口規範,就可 以插入 PCI 插槽内 , 與該主機闆正常通信。至于這塊顯示卡是哪個廠家制造的 , 内部是如何實作的,主機闆無須關心 。
類似的,軟體系統的各子產品之間也應該采用這種面向接口的稿合,進而盡量降低各子產品之間 的耦合,為系統提供更好的可擴充性和可維護性。
是以,接口定義的是多個類共同的公共行為規範,這些行為是與外部交流的通道,這就意味着接口裡通常是定義一組公用方法。
使用interface可以聲明一個接口,
定義接口文法如下:
[修飾符] interface 接口名 extends 父接口 1 ,父接口 2. . .{
零個到多個常量定義 .. .
零個到多個抽象方法定義.. .
零個到多個内部類、接口、枚舉定義.. .
零個到多個私有方法、預設方法或類方法定義 .. .
}
由于接口定義的是一種規範,是以接口裡不能包含構造器和初始化塊定義 。 接口裡可以包含成員變量(隻能是靜态常量)、方法(隻能是抽象執行個體方法、類方法、預設方法或私有方法)、内部類 (包括内部接口、枚舉)定義。
隻有在Java 8以上的版本中才允許在接口中定義預設方法、類方法
詳細說明:
- 接口裡的所有成員,包括常量、 方法、内部類和内部枚舉都是public通路權限。定義接口成員時,可以省略通路控制修飾符,如果指定通路控制修飾符,則隻能使用public通路控制修飾符。
- 接口裡的成員變量隻能是靜态常量,它是接口相關的,是以系統會自動為這些成員變量增加static 和final兩個修飾符。而且接口裡沒有構造器和初始化塊,是以接口裡定義的成員變量隻能在定義時指定預設值。
//系統自動為接口裡定義的成員變量增加 public static final 修飾符,
//下面兩行代碼結果是一樣的
int MAX SIZE = 50;
public static final int MAX S 工 ZE = 50 ;
- 接口裡定義的方法隻能是抽象方法、類方法、預設方法或私有方法,是以如果不是定義預設方法、類方法或私有方法,系統将自動為普通方法增加 abstract 修飾符;定義接口裡的普通方法時不管是否使用 public abstract 修飾符,接口裡的普通方法總是使用 public abstract 來修飾 。接口裡的普通方法不能有方法實作(方法體) ;但類方法、預設方法都必須有方法實作(方法體〉 。
接口執行個體
public interface Output{
//接口裡定義的成員變量隻能是常量
int MAX_CACHE_LINE = 50;
//接口重凫義的曾通方法隻能是public的抽象方法
void out();
void getData(String msg);
//在接口中定義預設方法,需要使用default修飾
default void print(String... msgs)
for (String msg : msgs){
System.out.printin(msg);
}
}
//在接口中定義預設方法,需要使用default修飾
default void test(){
System. out. printin (預設的 test ()方法”);
}
//在接口中定義類方法,需要使用static修飾
static String staticTest(){
return ”接口裡的類方方法";
}
}
在 Java SE 8 中,允許在接口中增加靜态方法。理論上講,沒有任何理由認為這是不合法的。隻是這有違于将接口作為抽象規範的初衷。
接口的繼承和類繼承不一樣,接口完全支援多繼承,即一個接口可以有多個直接父接口。和類繼承相似,子接口擴充某個父接口,将會獲得父接口裡定義的所有抽象方法、常量。
一個接口繼承多個父接口時,多個父接口排在extends關鍵字之後,多個父接口之間以英文逗号(,) 隔開。下面程式定義了三個接口,第三個接口繼承了前面兩個接口。
繼承接口執行個體
interface interfaceA {
int PROP_A = 5;
void testA();
}
interface interfaceB {
int PROP_B = 6;
void testB();
}
// 接口C繼承接口A和接口B
interface interfaceC extends interfaceA, interfaceB {
int PROP_C = 7;
void testC();
}
public class InterfaceExtendsTest {
public static void main(String[] args) {
System.out.println(interfaceC.PROP_A);
System.out.println(interfaceC.PROP_B);
System.out.println(interfaceC.PROP_C);
}
}
接口不能用于建立執行個體,但接口可以用于聲明引用類型變量。當使用接口來聲明引用類型變量時, 這個引用類型變量必須引用到其實作類的對象。除此之外,接口的主要用途就是被實作類實作。歸納起來,接口主要有如下用途。
- 定義變量,也可用于進行強制類型轉換。
- 調用接口中定義的常量。
- 被其他類實作。
一個類可以實作一個或多個接口,繼承使用extends關鍵字,實作則使用implements關鍵字。因為 一個類可以實作多個接口,這也是Java為單繼承靈活性不足所做的補充。類實作接口的文法格式如下:
[修飾符] class 類名 extends 父類 implements 接口 1 , 接口 2. . .{
類體部分
}
- 一個類實作了一個或多個接口之後 , 這個類必須完全實作這些接口裡所定義的全部抽象方法(也就是重寫這些抽象方法) ; 否則,該類将保留從父接口那裡繼承到的抽象方法,該類也必須定義成抽象類 。
- 一個類實作某個接口時 , 該類将會獲得接口中定義的常量 (成員變量)、方法等 , 是以可以把實作接口了解為一種特殊的繼承 , 相當于實作類繼承了 一個徹底抽象的類(相當于除預設方法外,所有方法都是抽象方法的類)。
實作接口執行個體
public class LearningJava implements SlamDunk,Crossover,Shot{
public static void main(String arg[]) {
LearningJava sxai=new LearningJava();
sxai.crossover();
sxai.slamdunk();
sxai.shot();
}
public void slamdunk() {
System.out.println("宋小艾東方翔38式旋轉戰斧劈扣詹姆斯");
}
public void crossover() {
System.out.println("宋小艾瘋狂變向晃暈詹姆斯");
}
public void shot() {
System.out.println("宋小艾幹拔怒草詹姆斯打成三加一");
}
}
interface SlamDunk{
void slamdunk();
}
interface Crossover{
void crossover();
}
interface Shot{
void shot();
}
實作接口方法時 , 必須使用 public 通路 控制修飾符, 因為接口裡的方法都是 public的 , 而子類( 相當于 實作類 )重寫父類方法時通路權限隻能更大或者相等 , 是以實作類實作接口裡的方法時隻能使用 public 通路權限 。
接口不能顯式繼承任何類 , 但所有接口類型的引用變量都可以直接賦給 Object 類型的引用變量。這是利用向上轉型來實作 的,因為編譯器知道任何 Java 對象都必須是 Object 或其子類的執行個體 。
接口和抽象類具備相似的特征 :
- 接口和抽象類都不能被執行個體化,它們都位于繼承樹的頂端,用于被其他類實作和繼承。
- 接口和抽象類都可以包含抽象方法,實作接口或繼承抽象類的普通子類都必須實作這些抽象方法。
但實際上接口和抽象類之間的差别非常大,這種差别主要展現在二者設計目的上。
-
接口作為系統與外界互動的視窗 , 展現的是一種規範 。 對于接口的實作者而言,接口規定了實作者必須向外提供哪些服務(以方法的形式來提供);對于接口的調用者而言 , 接口規定了調用者可以調用哪些服務,以及如何調用這些服務(就是如何來調用方法)。當在一個程式 中使用接口時,接口是多個子產品間的耦合标準 : 當在多個應用程式之間使用接口時 , 接口是多個程式之間的通信标準。
從某種程度上來看,接口類似于整個系統的"總綱",它制定了系統各子產品應該遵循的标準,是以一個系統中的接口不應該經常改變 。一旦接口被改變 , 對整個系統甚至其他系統的影響将是輻射式的,導緻系統中大部分類都需要改寫 。
- 抽象類則不一樣,抽象類作為系統中多個子類的共同父類 , 它所展現的是一種模闆式設計 。 抽象類作為多個子類的抽象父類,可以被當成系統實作過程中的中間産品 , 這個中間産品己經實作了系統的部分功能(那些己經提供實作的方法) ,但這個産品依然不能當成最終産品,必須有更進一步的完善 ,這種完善可能有幾種不同方式 。
接口和抽象類在用法上也存在如下差别 :
- 接口裡隻能包含抽象方法、靜态方法、預設方法和私有方法,不能為普通方法提供方法實作;
- 抽象類則完全可以包含普通方法。
- 接口裡隻能定義靜态常量,不能定義普通成員變量
- 抽象類裡則既可以定義普通成員變量,也可以定義靜态常量。
- 接口裡不包含構造器;
-
抽象類裡可以包含構造器,抽象類裡的構造器并不是用于建立對象,而
是讓其子類調用這些構造器來完成屬于抽象類的初始化操作 。
- 接口裡不能包含初始化塊;
- 抽象類則完全可以包含初始化塊 。
- 一個類最多隻能有一個直接父類,包括抽象類;
- 一個類可以直接實作多個接口,通過實作多個接口可以彌補 Java 單繼承的不足。
- 接口中的方法預設都是 public,abstract 類型的。
- 抽象類可以定義私有化方法。(雖然沒有什麼實際意義)
參考:
【1】:《瘋狂Java講義》
【2】:《Java核心技術 卷一》
【3】:
https://www.liaoxuefeng.com/wiki/1252599548343744/1260456371027744【4】:
http://blog.itmyhome.com/2015/08/difference-in-java-abstract-classes-and-interfaces【5】:
https://www.2cto.com/database/201807/762348.html