java基礎中抽象類和接口的異同

一.概述

  • 一個軟件設計的好壞,我想很大程度上取決於它的整體結構,而這個整體架構其實就是你對整個宏觀商業業務的抽象框架,當代表業務邏輯的高層抽象層結構合理時,你底層的具體實現需要考慮的就僅僅是一些算法和一些具體的業務實現了。當你需要再開發另一個相近的項目時,你以前的抽象層說不定還可以再次利用。面向對象的設計,複用的重點其實應該是抽象層的複用,而不是具體某一個代碼塊的複用。
  • 既然面向對象設計的重點在於抽象,那java接口和java抽象類就有它存在的必然性了
  • Java接口(interface)和Java抽象類(abstract class)代表的就是抽象類型,就是我們需要提出的抽象層的具體表現。OOP面向對象的編程,如果要提高程序的複用率,增加程序的可維護性,可拓展性,就必須是面向接口的編程,面向抽象的編程,正確的使用接口、抽象類這些有用的抽象類型作爲你結構層次上的頂層。

二.抽象類

  • 我們都知道在面向對象的領域一切都是對象,同時所有的對象都是通過類來描述的,但是並不是所有的類都是來描述對象的。如果一個類沒有足夠的信息來描述一個具體的對象,而需要其他具體的類來支撐它,那麼這樣的類我們稱它爲抽象類。
  • 通俗來講:比如new Animal(),我們都知道這個是產生一個動物Animal對象,但是這個Animal具體長成什麼樣子我們並不知道,它沒有一個具體動物的概念,所以它是一個抽象類,需要一個具體的動物,如狗、貓、豬來對它進行特定的描述,我們才知道它長成啥樣。
  • 實例:
public class 抽象類的測試 {
    public static void main(String[] args) {
        //父類的引用指向子類的對象
        Animal c = new Cat();
        Animal p = new Pig();
        c.show();
        p.show();
    }

}

abstract class Animal{
    //普通方法
    public void play(){

    }
    //抽象方法 --- 父類只是做了定義,沒有具體代碼的實現
    //不同的子類,可以有不同的實現
    public abstract void show();
}

class Cat extends Animal{
    //必須重寫show方法
    @Override
    public void show() {
        System.out.println("貓在吃魚!");
    }
}

class Pig extends Animal{
    //必須重寫show方法
    @Override
    public void show() {
        System.out.println("豬在跑!");
    }
}

  • 輸出:
貓在吃魚!
豬在跑!
  • 上面代碼可以看到,定義了一個抽象動物類Animal,提供抽象方法叫show,貓、豬都是動物類的子類,由於show爲抽象方法,所以Cat、Pig必須要實現show方法。
  • main主方法中代碼,用到了多態(一種事物具有多種表現形態)裏面的知識,引用類型之間的轉換(自動轉換和強制轉換),這裏用到的是自動轉換,由小到大,向上轉爲自動轉換。
  • 這裏大概講一下自動轉換
    將子類對象轉爲父類 Cat、Pig — Animal
    將父類的引用指向子類對象
    特徵:
    • 通過父類保存的變量,調用的是子類重寫後的方法

抽象類總結:

  • 被abstract修飾的類,抽象類型
    • 定義:public abstract class 類名{}
    • 不能被實例化不能用new創建對象(實例化的工作應該交由它的子類來完成,它只需要有一個引用即可),只能被繼承並且只能單一繼承
  • 被abstract修飾的方法,抽象方法
    • 定義:public abstract 返回類型 方法名 (參數);
    • 抽象方法中只有聲明,沒有方法體,就是說()小括號外面不需要寫代碼
  • 特徵:
    • 子類在繼承抽象類後,必須實現抽象類中的抽象方法
    • 抽象類中可以有抽象的方法,也可以有普通方法
    • 如果類中含有抽象的方法,當前類必須爲抽象類

三.接口

  • 概念:
    • 接口就是特殊的抽象類,如果一個抽象類中全部都是抽象方法,就定義爲一個接口
    • 接口中只能有常量和抽象方法

1.定義接口:

  • 在java中,可以使用關鍵字定義一個接口,一個接口由變量的定義和方法定義兩部分組成。
  • 語法:
   [public] interface 接口名 {

          [public] [static] [final] 變量;

          [public] [abstract] 方法;

   }
  • []中括號是可以寫,可以不寫,一般省略不寫
  • 接口中的屬性,默認是public static final 修飾的,不能修改
  • 接口中的方法默認是public abstract 修飾的,沒有方法體不要寫大括號和裏面的代碼如:{代碼}
  • 接口中所有的方法不能有具體的實現,也就是說,接口中的方法必須是抽象方法。從這裏隱約看出接口和抽象類的區別,接口是一種極度抽象的類型,它比抽象類更加“抽象”,並且一般情況下不在接口中定義變量
  • 接口不能實例化

2.實現接口:

  • 語法:要讓一個類遵循某組特定的接口需要使用implements 關鍵字
[public] class 類名 implements Interface1,Interface2,...{
    //實現所有接口中聲明的方法
}
  • 實現接口的類,稱爲實現類
  • 實現類必須實現接口中所有的方法
  • 如果不實現接口的方法,那麼該實現類也要爲一個抽象類
  • 可以看出,允許一個類遵循多個特定的接口。如果一個非抽象類遵循了某個接口,就必須實現該接口中的所有方法。對於遵循某個接口的抽象類,可以不實現該接口中的抽象方法。

3.接口的特徵:

  • 類與類:
    • 一個類繼承另一個類:class 子類 extends 父類
  • 類與接口:
    • 一個可以同時繼承另一個類,並實現多個接口:class 類 extends 父類 implements 接口1,接口2
  • 接口與接口:
    • 一個接口可以繼承多個接口:interface 接口名 extends 接口1,接口2

4.接口的作用:

  • 可以間接實現多繼承
  • 通過接口定義規範和標準
  • 將標準的指定和實現分開
  • 面向接口編程,提高拓展性

5.實例:

public class 接口的測試 {
}

interface A{
    void a();
}

interface B{
    void b();
}

interface C extends A,B{
    void c();
}

class D extends E implements C{
    public void d(){
        
    }
    
    @Override
    public void a() {
        
    }

    @Override
    public void b() {

    }

    @Override
    public void c() {

    }
}

class E{
    public void e() {
        
    }
}

四.抽象類和接口的區別

  • 儘管抽象類和接口之間存在較大的相同點,甚至有時候還可以互換,但這樣並不能彌補他們之間的差異之處。下面將從語法層次和設計層次兩個方面對抽象類和接口進行闡述。

1.語法層次

  • 在語法層次,java語言對於抽象類和接口分別給出了不同的定義。下面用demo類來說明他們之間的不同之處。
  • 使用抽象類來實現:
abstract class Animal{
    //普通方法
    public void play(){

    }
    //抽象方法 --- 父類只是做了定義,沒有具體代碼的實現
    //不同的子類,可以有不同的實現
    public abstract void show();
}
  • 使用接口來實現:
interface Animal{
    void play();
    void show();
}
  • 可以看出:抽象類方式中,抽象類可以擁有任意範圍的成員數據,同時也可以擁有自己的非抽象方法。但是接口方式中,它僅能夠有靜態不能修改的成員數據(但是我們一般是不會在接口中使用成員數據),同時它所有的方法都必須是抽象的。在某種程序上來說,接口是抽象類的特殊化。
  • 對子類而言,它只能繼承一個抽象類這是java爲了數據安全而考慮的),但是卻可以實現多個接口

2.設計層次

  • 上面只是從語法層次和編程角度來區分它們之間的關係,這些都是低層次的,要真正使用好抽象類和接口,我們就必須要從較高層次來區分了。只有從設計理念的角度才能看出它們的本質所在。一般來說他們存在如下三個不同點:

1.抽象層次不同:

  • 抽象類是對類抽象,而接口是對行爲的抽象。抽象類是對整個類整體進行抽象,包括屬性、行爲,但是接口卻是對類局部(行爲)進行抽象。

2.跨域不同:

  • 抽象類所跨域的是具有相似特點的類,而接口卻可以跨域不同的類。我們知道抽象類是從子類中發現公共部分,然後泛化成抽象類,子類繼承該父類即可,但是接口不同。實現它的子類可以不存在任何關係,共同之處。例如貓、狗可以抽象成一個動物類抽象類,具備叫的方法。鳥、飛機可以實現飛fly接口,具備飛的行爲,這裏我們總不能將鳥、飛機共用一個父類吧 !所以說抽象類所體現的是一種繼承關係,要想使得繼承關係合理,父類和派生類之間必須存在"is-a"關係,即父類和派生類在概念本質上應該是相同的。對於接口則不然,並不要求接口的實現者和接口定義在概念本質上是一致的, 僅僅是實現了接口定義的契約而已,類似於“like a”

3.設計層次不同:

  • 對於抽象類而言,它是自下而上來設計的,我們要先知道子類才能抽象出父類,而接口則不同,它根本就不需要知道子類的存在,只需要定義一個規則即可,至於什麼子類、什麼時候怎麼實現它一概不知。比如我們只有一個貓類在這裏,如果你這是就抽象成一個動物類,是不是設計有點兒過度?我們起碼要有兩個動物類,貓、狗在這裏,我們在抽象他們的共同點形成動物抽象類吧!所以說抽象類往往都是通過重構而來的!但是接口就不同,比如說飛,我們根本就不知道會有什麼東西來實現這個飛接口,怎麼實現也不得而知,我們要做的就是事前定義好飛的行爲接口。所以說抽象類是自底向上抽象而來的,接口是自頂向下設計出來的。

3.實例:

  • 不同的門都具有本質特徵動作 open(), close()。那麼抽象類和接口都可以定義這兩個方法。現在要求它具有報警alarm功能。
    • 如果這3個功能都放在抽象類裏面,那麼所有的門都具備了這3個功能,無疑不妥,有的門不需要報警功能啊!
    • 如果這3個功能都放到接口裏面,需要用到報警功能的其他類,就需要實現門的open和close功能,這樣子也不對!
  • 所以,應該把門的open, close和alarm分離,讓所有的門都有open, close動作,繼承抽象類Door。而需要添加報警功能的門,則再繼承接口Alarm。
public class Test01 {
}

abstract class Door {
    abstract void open();

    abstract void close();
}

interface Alarm {
    void alarm();
}

class AlarmDoor extends Door implements Alarm {

    @Override
    void open() {

    }

    @Override
    void close() {

    }

    @Override
    public void alarm() {

    }
} 
  • 可以看出,因爲抽象類是用於單一繼承,接口是用於多重繼承,所以需要這樣安排。而同時看到,抽象類就是類的本質特徵,共同的。接口是個性化的,你想讓類更具個性化,則繼承其他相應個性特徵的接口。

4.抽象類和接口的異同總結:

  • 相同點:
    • 都不能實例化
    • 都可以包含抽象方法
  • 不同點:
    • 抽象類可以有普通方法,接口只能爲抽象方法
    • 接口支持多繼承,抽象類只能單一繼承
    • 接口中不能有構造方法,抽象類中可以有構造方法

如果對你有幫助,點個贊吧0.0
若有不正之處,請多多諒解並歡迎批評指正,不甚感激

參考資料:

發佈了24 篇原創文章 · 獲贊 12 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章