前面《Java介面》一節中提到介面是一種特殊的抽象類,介面和抽象類的淵源頗深,有很大的相似之處,所以在選擇使用誰的問題上很容易迷糊。本節我們先整理一下 Java 中抽象類和介面的特點,再分析它們具有的相同點、不同點和使用場景。
1)抽象類
在 Java 中,被關鍵字 abstract 修飾的類稱為抽象類;被 abstract 修飾的方法稱為抽象方法,抽象方法只有方法宣告沒有方法體。
抽象類有以下幾個特點:
-
抽象類不能被範例化,只能被繼承。
-
包含抽象方法的類一定是抽象類,但抽象類不一定包含抽象方法(抽象類可以包含普通方法)。
-
抽象方法的許可權修飾符只能為 public、protected 或 default,預設情況下為 public。
-
一個類繼承於一個抽象類,則子類必須實現抽象類的抽象方法,如果子類沒有實現父類別的抽象方法,那子類必須定義為抽象類。
-
抽象類可以包含屬性、方法、構造方法,但構造方法不能用來範例化物件,只能被子類呼叫。
2)介面
介面可以看成是一種特殊的類,只能用 interface 關鍵字修飾。
Java 中的介面具有以下幾個特點:
-
介面中可以包含變數和方法,變數被隱式指定為 public static final,方法被隱式指定為 public abstract(JDK 1.8 之前)。
-
介面支援多繼承,即一個介面可以繼承(extends)多個介面,間接解決了 Java 中類不能多繼承的問題。
-
一個類可以同時實現多個介面,一個類實現某個介面則必須實現該介面中的抽象方法,否則該類必須被定義為抽象類。
3)抽象類和介面的區別
介面和抽象類很像,它們都具有如下特徵。
-
介面和抽象類都不能被範例化,主要用於被其他類實現和繼承。
-
介面和抽象類都可以包含抽象方法,實現介面或繼承抽象類的普通子類都必須實現這些抽象方法。
但介面和抽象類之間的差別非常大,這種差別主要體現在二者設計目的上。下面具體分析二者的差別。
介面作為系統與外界互動的視窗,介面體現的是一種規範。對於介面的實現者而言,介面規定了實現者必須向外提供哪些服務(以方法的形式來提供);對於介面的呼叫者而言,介面規定了呼叫者可以呼叫哪些服務,以及如何呼叫這些服務(就是如何來呼叫方法)。當在一個程式中使用介面時,介面是多個模組間的耦合標準;當在多個應用程式之間使用介面時,介面是多個程式之間的通訊標準。
從某種程度上來看,介面類似於整個系統的“總綱”,它制定了系統各模組應該遵循的標準,因此一個系統中的介面不應該經常改變。一旦介面被改變,對整個系統甚至其他系統的影響將是輻射式的,會導致系統中大部分類都需要改寫。
抽象類則不一樣,抽象類作為系統中多個子類的共同父類別,它所體現的是一種模板式設計。抽象類作為多個子類的抽象父類別,可以被當成系統實現過程中的中間產品,這個中間產品已經實現了系統的部分功能(那些已經提供實現的方法),但這個產品依然不能當成最終產品,必須有更進一步的完善,這種完善可能有幾種不同方式。
除此之外,介面和抽象類在用法上也存在差別,如下表所示:
引數 |
抽象類 |
介面 |
實現 |
子類使用 extends 關鍵字來繼承抽象類,如果子類不是抽象類,則需要提供抽象類中所有宣告的方法的實現。 |
子類使用 implements 關鍵字來實現介面,需要提供介面中所有宣告的方法的實現。 |
存取修飾符 |
可以用 public、protected 和 default 修飾 |
預設修飾符是 public,不能使用其它修飾符 |
方法 |
完全可以包含普通方法 |
只能包含抽象方法、靜態方法、預設方法和私有方法,不能為普通方法提供方法實現 |
變數 |
既可以定義普通成員變數,也可以定義靜態常數 |
只能定義靜態常數,不能定義普通成員變數 |
構造方法 |
抽象類裡的構造方法並不是用於建立物件,而是讓其子類呼叫這些構造方法來完成屬於抽象類的初始化操作 |
沒有構造方法 |
初始化塊 |
可以包含初始化塊 |
不能包含初始化塊 |
main 方法 |
可以有 main 方法,並且能執行 |
沒有 main 方法 |
與普通Java類的區別 |
抽象類不能範例化,除此之外和普通 Java 類沒有任何區別 |
是完全不同的型別 |
執行速度 |
比介面執行速度要快 |
需要時間去尋找在類種實現的方法,所以執行速度稍微有點慢 |
一個類最多只能有一個直接父類別,包括抽象類,但一個類可以直接實現多個介面,通過實現多個介面可以彌補 Java 單繼承的不足。
4)抽象類和介面的應用場景
抽象類的應用場景:
-
父類別只知道其子類應該包含怎樣的方法,不能準確知道這些子類如何實現這些方法的情況下,使用抽象類。
-
從多個具有相同特徵的類中抽象出一個抽象類,以這個抽象類作為子類的模板,從而避免了子類設計的隨意性。
介面的應用場景:
-
一般情況下,實現類和它的抽象類之前具有 "is-a" 的關係,但是如果我們想達到同樣的目的,但是又不存在這種關係時,使用介面。
-
由於 Java 中單繼承的特性,導致一個類只能繼承一個類,但是可以實現一個或多個介面,此時可以使用介面。
什麼時候使用抽象類和介面:
-
如果擁有一些方法並且想讓它們有預設實現,則使用抽象類。
-
如果想實現多重繼承,那麼必須使用介面。因為 Java 不支援多繼承,子類不能繼承多個類,但可以實現多個介面,因此可以使用介面。
-
如果基本功能在不斷改變,那麼就需要使用抽象類。如果使用介面並不斷需要改變基本功能,那麼就需要改變所有實現了該介面的類。