曾裕宗
摘要:抽象是Java語言的一種很重要的特性,可以通過兩種形式來體現:abstract class(抽象類) 和interface(接口)。兩者之間有很大的相似性,但也存在不一樣的地方,該文結合筆者多年Java開發和教學經驗,先分別闡述抽象類、接口,接著剖析兩者之間的區別,然后給出一個案例來說明,最后做出總結。
關鍵詞:抽象類;接口;繼承;實現;區別
中圖分類號:TP311 文獻標識碼:A 文章編號:1009-3044(2017)36-0202-02
抽象是任何一門面向對象編程語言的一種相當重要的特性,Java亦如此。Java OOP的抽象包含兩種:abstract class(抽象類) 和interface(接口)。從某種意義上講,正是因為有了這兩種抽象機制,Java才具備強大的 面向對象能力。它們兩者之間有很大的相似性,在編程的時候,有時可以相互替換實現,但肯定也存在不一樣的地方,否則Java就沒必要搞兩個概念機制了。本文結合筆者多年Java開發和教學經驗,來剖析、闡述兩者之間的區別,試圖給眾多Java初學者提供一些借鑒和參考。
首先,我們來講抽象類。簡單來說,包含抽象方法的類就叫抽象類。這里所說的抽象方法,是指只有方法頭部,即方法名稱,但沒有最重要的方法實現的語句體,其定義格式為:
從定義中,我們看出:抽象類所包含的方法,可以是抽象方法,也可以是普通的實現方法,另外,抽象類也可以擁有成員變量(屬性)。當然,它跟普通類還是有以下的區別:
1) 抽象類,其修飾符必須為public或者protected,不能是private,因為創建抽象類,就是要被其他類繼承,用private修飾了,則不能被子類繼承,子類便無法實現該方法。
2) 抽象類不能用來創建對象,即抽象類不能被直接實例化,要通過其普通子類進行實例化。
3) 如果一個普通子類繼承于一個抽象父類,則該類一定要重寫實現該父類的抽象方法。如果該子類仍然是一個抽象類,這也是允許的,就不必重寫實現該父類的抽象方法,但必須用abstract修飾。
接著,我們來講接口。接口,也可以認為是一種特殊的抽象類,當然,它本質上不是類,它是一些方法特征的集合,但不可以有方法的實現。接口全部是由全局變量和公共的抽象方法組成,接口中的所有方法都是抽象方法,而且其修飾符必須是public類型,其定義格式為:
從定義中,我們看出:接口可以有全局成員變量,是一種超級抽象的類型,它比抽象類更加“抽象”。顯然,我們不能直接通過接口來創建對象,而是要創建普通類,先實現接口,再通過普通類來創建對象,其語法定義:
從定義中,我們看出:普通類可以實現多個接口,再就是,如果是普通類實現接口,那么必須重寫實現接口中的所有方法;如果實現接口的還是抽象類,則可以不必重寫實現接口中的所有方法,還可以有自己的方法。
講完了抽象類和接口,我們重點來論述兩者之間的區別,總體來說,有兩大方面的區別:
1 定義的語法不同
從定義中,我們看出:兩者的關鍵詞不同,抽象類是abstract class,而接口是Interface;抽象類可以有成員變量數據,也可以具有非抽象方法,但是接口則不同,它方法外只能定義的是常量(一般情況下不定義),而且它所有的方法都必須是抽象的??梢赃@么理解,接口是特殊的抽象類。子類只能繼承一個父類或抽象類,但是它卻可以實現多個接口,這叫子類的單繼承、多實現。
2 設計理念層次不一樣
1) 抽象級別不一樣,抽象類是對整體類的抽象,包含屬性和方法;而接口是對其中方法的抽象,即一般只是對其方法進行抽象。
2) 實現類的范圍不同,抽象類,其實是從子類中提取共性部分,然后抽象出來,反之,子類繼承該父類就可以了,也就是說里面的抽象方法一般來自同一個類別,而接口卻可以跨越不同的類,實現它的子類可以不存在任何關系和共同之處,即接口中定義的抽象方法,在被不同的普通類實現時,即重寫其抽象方法時,可以具有完全不同的行為,即語句體可以完全不同。舉個例子,老虎、牛這些動物,來自同一類別,可以抽象成一個動物類,都有睡的行為;鳥、飛機都能飛,可以有Fly接口,但是,顯然它們是沒有共同父類的?所以,只能用接口寫。也就是說,抽象類反映的是一種繼承關系,父類和子類之間必須存在"is-a" 關系,而接口則不同,其同樣的方法,在不同的地方,可以實現完全不一樣的行為,體現的是"like-a"關系。
3) 設計方式不同,抽象類是要先有子類,然后才抽象出父類,是一種從下往上的構建法則;而接口不需要先有子類,它只需要定義一些抽象方法就行。比如前面提到的例子,假如我們只有一個老虎類,你肯定不能馬上抽象成一個動物類,最起碼還要有牛類,然后才可以找它們的共同點,形成一個動物抽象類,它是倒過來實現的。但是接口不一樣,比如前面提到的飛,我們只需要事前定義好飛的行為接口,其后面的實現不管它,可以有完全不同的行為,也就是說,接口是從上向下設計出來的。
問題來了,假如再增加一個fly( )的功能,該怎么實現?
可以這么解決,將這個方法,連同之前的兩個方法,一起放在抽象類里,但這樣做有個問題,繼承于這個抽象類的子類都帶上了fly( )行為,但現實中,有些動物是不會飛的;也可以將這三個方法都放在接口里,但這樣一來,有些只有飛功能的類就又不得不去實現這個接口中的eat( )和sleep( )方法,比如飛機,它沒有eat( )和sleep( )方法。所以,這個解決辦法不妥,最合理的解決方案是,應該將飛創建為一個接口,具有fly()行為,而把Animal創建為一個抽象類,它具有eat和sleep兩種方法。最后,創建一個類繼承Animal抽象類和實現Fly接口,這樣問題就科學的解決了,改進后的代碼如下:
綜上所述,Java抽象類與接口的區別,主要有以下5個方面:
1) 抽象類是特殊的類,它只能被繼承一次,而接口可以被多次實現,這就是單繼承、多實現。
2) 在抽象類中,其方法可以是抽象方法,也可以是實現方法,可以有全局成員變量;接口只能有抽象方法,只能有常量,且一般情況下不定義它。
3) 抽象類與接口,它們的設計理念不同。抽象類反映的是"is-a"關系,接口反映的是"like-a"關系。
4) 接口中定義的變量是public static final 型,且一定要賦初始值,它相當于一個常量,故實現類中不能再次定義,也就不能改變它的值。
5) 抽象類中定義的變量是普通的,它就是一個真正的變量,它的值可以在子類中再次定義,也可以重新賦值。
參考文獻:
[1] 徐紅.java程序設計.高等教育出版社,2013.
[2] 廖大強.面向多目標的云計算資源調度算法,計算機系統應用,2016,25(2):180-189.