1. 程式人生 > 其它 >java 抽象類(abstract)與介面(interface)

java 抽象類(abstract)與介面(interface)

1 抽象類(abstract class)

1.1 基本概念

父類中的方法,被它的子類們重寫,子類各自的實現都不盡相同。那麼父類的方法宣告和方法主體,只有宣告還有意義,而方法主體則沒有存在的意義了(因為子類物件會呼叫自己重寫的方法)。換句話說,父類可能知道子類應該有哪個功能,但是功能具體怎麼實現父類是不清楚的(由子類自己決定),父類完全只需要提供一個沒有方法體的方法簽名即可,具體實現交給子類自己去實現。我們把沒有方法體的方法稱為抽象方法。Java語法規定,包含抽象方法的類就是抽象類

  • 抽象方法 : 使用abstract 關鍵字修飾方法,該方法就成了抽象方法,抽象方法只包含一個方法名,而沒有方法體。
//抽象方法定義格式:
修飾符 abstract 返回值型別 方法名 (引數列表);
  • 抽象類:如果一個類包含抽象方法,那麼該類必須是抽象類。注意:抽象類不一定有抽象方法,但是有抽象方法的類必須定義成抽象類。
//抽象類定義格式:
abstract class 類名字 { 

}

1.2 注意事項:

  1. 抽象類不能建立物件,如果建立,編譯無法通過而報錯。只能建立其非抽象子類的物件。

    理解:假設建立了抽象類的物件,呼叫抽象的方法,而抽象方法沒有具體的方法體,沒有意義。

  2. 抽象類中,是有構造器的,其是供子類建立物件時,初始化父類成員使用的。

    理解:子類的構造方法中,有預設的super()

    ,需要訪問父類構造方法。此外,抽象類的構造器用private關鍵字修飾在語法層面上不會報錯,但這樣的話該抽象類就不能被繼承了,自然就失去了它的意義。

  3. 抽象類中,不一定包含抽象方法,但是有抽象方法的類必定是抽象類。

    理解:未包含抽象方法的抽象類,目的就是不想讓呼叫者建立該類物件,通常用於某些特殊的類結構設計(比如說工具類的封裝)。

  4. 抽象類的子類,必須重寫抽象父類中所有的抽象方法,否則子類也必須定義成抽象類,編譯無法通過而報錯。

    理解:假設不重寫所有抽象方法,則類中可能包含抽象方法。那麼建立物件後,呼叫抽象的方法,沒有意義。

  5. 抽象類存在的意義是為了被子類繼承,抽象類體現的是模板思想。

    理解:抽象類中已經實現的是模板中確定的成員,抽象類不確定如何實現的定義成抽象方法,交給具體的子類去實現。

總結:
抽象類和普通類的區別就是抽象類獲得了擁有抽象方法的能力,失去了建立物件例項的能力。除此之外,普通類擁有的其他成員(構造器,例項方法,靜態方法等)抽象類都是具備的。

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 注意事項

  1. 類與類是單繼承關係、類與介面是多實現關係、介面與介面是多繼承關係。
  2. 必須重寫實現的全部介面中所有抽象方法。如果沒有重寫完全部介面的全部抽象方法,這個類也必須定義成抽象類。
  3. 介面體現的是一種規範,介面對實現類是一種強制性的約束,要麼全部完成介面申明的功能,要麼自己也定義成抽象類。這正是一種強制性的規範。
  4. java 是不允許使用 extends 去繼承多個類的。java如果出現多繼承、父類中都有相同的屬性和方法,子類如果使用父類的屬性,無法確定是哪一個父類的屬性。父類中如果有相同的方法,並且子類並沒有覆蓋該方法。子類呼叫父類的時候 無法判斷是那個父類的方法。但是java中介面是可以多繼承的。介面(jdk 1.8 以下版本)裡面的方法沒有具體實現,即使在多繼承時出現父類中都有相同方法的情況也並無大礙。而且介面的成員變數都是static final的,有自己靜態域,只能供自己使用。

tips:可以將類理解為親爹,介面理解為乾爹!!!
親爹只能有一個,乾爹可以有多個(單繼承,多實現);
先有親爹後有乾爹(先extendsimplements);
親爹的優先順序高於乾爹(類既繼承一個父類,又實現若干個介面時,父類中的成員方法與介面中的預設方法重名,子類就近選擇執行父類的成員方法。)

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介面的注意事項

  1. 如果實現了多個介面,多個介面中存在同名的靜態方法並不會衝突,原因是隻能通過各自介面名訪問靜態方法。
  2. 當一個類,既繼承一個父類,又實現若干個介面時,父類中的成員方法與介面中的預設方法重名,子類就近選擇執行父類的成員方法。(親爹的優先順序高於乾爹)
  3. 當一個類實現多個介面,且多個介面中存在同名預設方法時,實現類必須重寫這個方法。

3 抽象類與介面的區別

注: 這裡僅針對JDK1.8之前的介面

  1. 抽象類和介面都不能例項化。
  2. 抽象類中有構造器(其子類需要通過super()呼叫),而介面中沒有。
  3. 抽象類要被子類繼承,介面要被類實現。
  4. 抽象類只能被單繼承,而介面可以被多實現。
  5. 介面只能做方法宣告,抽象類中可以作方法宣告,也可以做方法實現。
  6. 接口裡定義的變數只能是公共的靜態的常量,抽象類中的變數是普通變數。
  7. 抽象類可以有具體的方法和屬性,介面只能有抽象方法和不可變常量。
  8. 抽象類主要用來抽象類別,介面主要用來抽象功能。