java 抽象類(abstract)與介面(interface)
1 抽象類(abstract class)
1.1 基本概念
父類中的方法,被它的子類們重寫,子類各自的實現都不盡相同。那麼父類的方法宣告和方法主體,只有宣告還有意義,而方法主體則沒有存在的意義了(因為子類物件會呼叫自己重寫的方法)。換句話說,父類可能知道子類應該有哪個功能,但是功能具體怎麼實現父類是不清楚的(由子類自己決定),父類完全只需要提供一個沒有方法體的方法簽名即可,具體實現交給子類自己去實現。我們把沒有方法體的方法稱為抽象方法。Java語法規定,包含抽象方法的類就是抽象類。
- 抽象方法 : 使用
abstract
關鍵字修飾方法,該方法就成了抽象方法,抽象方法只包含一個方法名,而沒有方法體。
//抽象方法定義格式:
修飾符 abstract 返回值型別 方法名 (引數列表);
- 抽象類:如果一個類包含抽象方法,那麼該類必須是抽象類。注意:抽象類不一定有抽象方法,但是有抽象方法的類必須定義成抽象類。
//抽象類定義格式:
abstract class 類名字 {
}
1.2 注意事項:
-
抽象類不能建立物件,如果建立,編譯無法通過而報錯。只能建立其非抽象子類的物件。
理解:假設建立了抽象類的物件,呼叫抽象的方法,而抽象方法沒有具體的方法體,沒有意義。
-
抽象類中,是有構造器的,其是供子類建立物件時,初始化父類成員使用的。
理解:子類的構造方法中,有預設的
super()
private
關鍵字修飾在語法層面上不會報錯,但這樣的話該抽象類就不能被繼承了,自然就失去了它的意義。 -
抽象類中,不一定包含抽象方法,但是有抽象方法的類必定是抽象類。
理解:未包含抽象方法的抽象類,目的就是不想讓呼叫者建立該類物件,通常用於某些特殊的類結構設計(比如說工具類的封裝)。
-
抽象類的子類,必須重寫抽象父類中所有的抽象方法,否則子類也必須定義成抽象類,編譯無法通過而報錯。
理解:假設不重寫所有抽象方法,則類中可能包含抽象方法。那麼建立物件後,呼叫抽象的方法,沒有意義。
-
抽象類存在的意義是為了被子類繼承,抽象類體現的是模板思想。
理解:抽象類中已經實現的是模板中確定的成員,抽象類不確定如何實現的定義成抽象方法,交給具體的子類去實現。
總結:
抽象類和普通類的區別就是抽象類獲得了擁有抽象方法的能力,失去了建立物件例項的能力。除此之外,普通類擁有的其他成員(構造器,例項方法,靜態方法等)抽象類都是具備的。
1.3 抽象類存在的意義
抽象類存在的意義是為了被子類繼承,否則抽象類將毫無意義,抽象類體現的是模板思想,模板具體來說就是通用的東西在抽象類中已經有具體的實現(抽象類中可以有成員變數和實現方法),而模板中不能決定的東西則定義成抽象方法,讓使用模板(繼承抽象類的類)的類去重寫抽象方法實現需求,這是典型的模板思想。
從而可以衍生出模板設計模式。在模板模式(Template Pattern)中,一個抽象類公開定義了執行它的方法的方式/模板。它的子類可以按需要重寫方法實現,但呼叫將以抽象類中定義的方式進行。這種型別的設計模式屬於行為型模式。
具體見:模板模式|菜鳥教程
2 介面(interface)
2.1 基本概念
介面是更加徹底的抽象,介面中全部是抽象方法。介面中沒有構造器,不能建立物件的。 在JDK8
之前,介面中的成分包含:抽象方法和常量。
- 介面中的抽象方法:預設會自動加上
public abstract
修飾。建議不要多此一舉寫上public abstract
。 - 介面中的常量:在介面中定義的成員變數預設會加上:
public static final
修飾。也就是說在介面中定義的成員變數實際上是一個常量。這裡是使用public static final
修飾後,變數值就不可被修改,並且是靜態化的變數可以直接用介面名訪問,所以也叫常量。常量必須要給初始值。常量命名規範建議字母全部大寫,多個單詞用下劃線連線。
/**介面的實現:
在Java中類與類是繼承關係,而類與介面是實現關係,即類實現介面
實現類的格式:*/
[修飾符] class 類名 implements 介面1,介面2,介面3...{
}
2.1 注意事項
- 類與類是單繼承關係、類與介面是多實現關係、介面與介面是多繼承關係。
- 必須重寫實現的全部介面中所有抽象方法。如果沒有重寫完全部介面的全部抽象方法,這個類也必須定義成抽象類。
- 介面體現的是一種規範,介面對實現類是一種強制性的約束,要麼全部完成介面申明的功能,要麼自己也定義成抽象類。這正是一種強制性的規範。
- java 是不允許使用
extends
去繼承多個類的。java如果出現多繼承、父類中都有相同的屬性和方法,子類如果使用父類的屬性,無法確定是哪一個父類的屬性。父類中如果有相同的方法,並且子類並沒有覆蓋該方法。子類呼叫父類的時候 無法判斷是那個父類的方法。但是java中介面是可以多繼承的。介面(jdk 1.8
以下版本)裡面的方法沒有具體實現,即使在多繼承時出現父類中都有相同方法的情況也並無大礙。而且介面的成員變數都是static final
的,有自己靜態域,只能供自己使用。
tips:可以將類理解為親爹,介面理解為乾爹!!!
親爹只能有一個,乾爹可以有多個(單繼承,多實現);
先有親爹後有乾爹(先extends
後implements
);
親爹的優先順序高於乾爹(類既繼承一個父類,又實現若干個介面時,父類中的成員方法與介面中的預設方法重名,子類就近選擇執行父類的成員方法。)
2.2 JDK1.8開始之後介面的變化
在JDK8
之前,介面中的成分包含:抽象方法和常量。
從JDK 8
開始之後,介面不再純潔了,介面中不再只是抽象方法,介面還可以有預設方法(也就是例項方法),和靜態方法了,還包含了私有例項方法和私有靜態方法。
- 預設方法:使用
default
修飾,不可省略,供子類呼叫或者子類重寫。 - 靜態方法:使用
static
修飾,供介面直接呼叫。而其實現類不能呼叫該靜態方法,這點要注意與繼承區分開來。此外,子介面重寫預設方法時,default
關鍵字可以保留。實現類重寫預設方法時,default
關鍵字不可以保留。
程式碼如下:
public interface InterFaceName {
// 預設方法(public可以省略,預設會加上)
public default void method() {
// 執行語句
}
// 靜態方法(public可以省略,預設會加上)
public static void method2() {
// 執行語句
}
}
- 私有方法(其實是從
JDK1.9
開始才支援的):使用private
修飾,供介面中的預設方法、靜態方法和私有方法呼叫。
程式碼如下:
public interface InterFaceName {
private void method() {
// 執行語句
}
}
2.2.1 JDK1.8介面的注意事項
- 如果實現了多個介面,多個介面中存在同名的靜態方法並不會衝突,原因是隻能通過各自介面名訪問靜態方法。
- 當一個類,既繼承一個父類,又實現若干個介面時,父類中的成員方法與介面中的預設方法重名,子類就近選擇執行父類的成員方法。(親爹的優先順序高於乾爹)
- 當一個類實現多個介面,且多個介面中存在同名預設方法時,實現類必須重寫這個方法。
3 抽象類與介面的區別
注: 這裡僅針對JDK1.8
之前的介面
- 抽象類和介面都不能例項化。
- 抽象類中有構造器(其子類需要通過
super()
呼叫),而介面中沒有。 - 抽象類要被子類繼承,介面要被類實現。
- 抽象類只能被單繼承,而介面可以被多實現。
- 介面只能做方法宣告,抽象類中可以作方法宣告,也可以做方法實現。
- 接口裡定義的變數只能是公共的靜態的常量,抽象類中的變數是普通變數。
- 抽象類可以有具體的方法和屬性,介面只能有抽象方法和不可變常量。
- 抽象類主要用來抽象類別,介面主要用來抽象功能。