JAVA的介面和抽象類別,JAVA介面抽象類別
對於物件導向編程來說,抽象是它的一大特徵之一。在Java中,可以通過兩種形式來體現OOP的抽象:介面和抽象類別。
一:抽象類別
如果一個類含有抽象方法,則稱這個類為抽象類別,抽象類別必須在類前用abstract關鍵字修飾。因為抽象類別中含有無具體實現的方法,所以不能用抽象類別建立對象。
1 package abstract_interface;2 3 public abstract class Abstract_one {4 public abstract void one();//抽象方法是一種特殊的方法:它只有聲明,而沒有具體的實現。5 }
在《JAVA編程思想》一書中,將抽象類別定義為“包含抽象方法的類”,但是後面發現如果一個類不包含抽象方法,只是用abstract修飾的話也是抽象類別。也就是說抽象類別不一定必須含有抽象方法。個人覺得這個屬於鑽牛角尖的問題吧,因為如果一個抽象類別不包含任何抽象方法,為何還要設計為抽象類別?
從這裡可以看出,抽象類別就是為了繼承而存在的,如果你定義了一個抽象類別,卻不去繼承它,那麼等於白白建立了這個抽象類別,因為你不能用它來做任何事情。對於一個父類,如果它的某個方法在父類中實現出來沒有任何意義,必鬚根據子類的實際需求來進行不同的實現,那麼就可以將這個方法聲明為abstract方法,此時這個類也就成為abstract類了。
包含抽象方法的類稱為抽象類別,但並不意味著抽象類別中只能有抽象方法,它和普通類一樣,同樣可以擁有成員變數和普通的成員方法。注意,抽象類別和普通類的主要有三點區別:
1)抽象方法必須為public或者protected(因為如果為private,則不能被子類繼承,子類便無法實現該方法),預設情況下預設為public。
2)抽象類別不能用來建立對象;
3)如果一個類繼承於一個抽象類別,則子類必須實現父類的抽象方法。如果子類沒有實現父類的抽象方法,則必須將子類也定義為為abstract類。
在其他方面,抽象類別和普通的類並沒有區別。
二:介面
介面,英文稱作interface,在軟體工程中,介面泛指供別人調用的方法或者函數。從這裡,我們可以體會到Java語言設計者的初衷,它是對行為的抽象
1 package abstract_interface;2 3 public interface Interface_one {4 public void one();5 }
我們在裡面定義一個 int a=1; 查看a的狀態;
發現a變成了 static final 類型的了;
介面中可以含有 變數和方法。但是要注意,介面中的變數會被隱式地指定為public static final變數(並且只能是public static final變數,用private修飾會報編譯錯誤),而方法會被隱式地指定為public abstract方法且只能是public abstract方法(用其他關鍵字,比如private、protected、static、 final等修飾會報編譯錯誤),並且介面中所有的方法不能有具體的實現,也就是說,介面中的方法必須都是抽象方法。從這裡可以隱約看出介面和抽象類別的區別,介面是一種極度抽象的類型,它比抽象類別更加“抽象”,並且一般情況下不在介面中定義變數。
1 package abstract_interface;2 3 public abstract class Test implements Interface_one{4 5 }
可以看出,允許一個類遵循多個特定的介面。如果一個非抽象類別遵循了某個介面,就必須實現該介面中的所有方法。對於遵循某個介面的抽象類別,可以不實現該介面中的抽象方法。
三:抽象類別和介面的區別
1.文法層面上的區別
- 抽象類別和介面都不能直接執行個體化,如果要執行個體化,抽象類別變數必須指向實現所有抽象方法的子類對象,介面變數必須指向實現所有介面方法的類對象。
- 抽象類別要被子類繼承,介面要被類實現。
- 介面只能做方法申明,抽象類別中可以做方法申明,也可以做方法實現
- 介面裡定義的變數只能是公用的靜態常量,抽象類別中的變數是普通變數。
- 抽象類別裡的抽象方法必須全部被子類所實現,如果子類不能全部實現父類抽象方法,那麼該子類只能是抽象類別。同樣,一個實現介面的時候,如不能全部實現介面方法,那麼該類也只能為抽象類別。
- 抽象方法只能申明,不能實現。abstract void abc();不能寫成abstract void abc(){}。
- 抽象類別裡可以沒有抽象方法
- 如果一個類裡有抽象方法,那麼這個類只能是抽象類別
- 抽象方法要被實現,所以不能是靜態,也不能是私人的。
- 介面可繼承介面,並可多繼承介面,但類只能單根繼承。
- 特別是對於公用的實現代碼,抽象類別有它的優點。抽象類別能夠保證實現的層次關係,避免代碼重複。然而,即使在使用抽 象類的場合,也不要忽視通過介面定義行為模型的原則。從實踐的角度來看,如果依賴於抽象類別來定義行為,往往導致過於複雜的繼承關係,而通過介面定義行為能 夠更有效地分離行為與實現,為代碼的維護和修改帶來方便。
2.設計層面上的區別
1)抽象類別是對一種事物的抽象,即對類抽象,而介面是對行為的抽象。抽象類別是對整個類整體進行抽象,包括屬性、行為,但是介面卻是對類局部(行為)進行抽象。舉個簡單的例子,飛機和鳥是不同類的事物,但是它們都有一個共性,就是都會飛。那麼在設計的時候,可以將飛機設計為一個類Airplane,將鳥設計為一個類Bird,但是不能將 飛行 這個特性也設計為類,因此它只是一個行為特性,並不是對一類事物的抽象描述。此時可以將 飛行 設計為一個介面Fly,包含方法fly( ),然後Airplane和Bird分別根據自己的需要實現Fly這個介面。然後至於有不同種類的飛機,比如戰鬥機、民用飛機等直接繼承Airplane即可,對於鳥也是類似的,不同種類的鳥直接繼承Bird類即可。從這裡可以看出,繼承是一個 "是不是"的關係,而 介面 實現則是 "有沒有"的關係。如果一個類繼承了某個抽象類別,則子類必定是抽象類別的種類,而介面實現則是有沒有、具備不具備的關係,比如鳥是否能飛(或者是否具備飛行這個特點),能飛行則可以實現這個介面,不能飛行就不實現這個介面。
2)設計層面不同,抽象類別作為很多子類的父類,它是一種模板式設計。而介面是一種行為規範,它是一種輻射式設計。什麼是模板式設計?最簡單例子,大家都用過ppt裡面的模板,如果用模板A設計了ppt B和ppt C,ppt B和ppt C公用的部分就是模板A了,如果它們的公用部分需要改動,則只需要改動模板A就可以了,不需要重新對ppt B和ppt C進行改動。而輻射式設計,比如某個電梯都裝了某種通報器,一旦要更新通報器,就必須全部更新。也就是說對於抽象類別,如果需要添加新的方法,可以直接在抽象類別中添加具體的實現,子類可以不進行變更;而對於介面則不行,如果介面進行了變更,則所有實現這個介面的類都必須進行相應的改動。
簡單來說其實也就是:
介面是對動作的抽象,抽象類別是對根源的抽象。
抽象類別表示的是,這個對象是什麼。介面表示的是,這個對象能做什麼。比如,男人,女人,這兩個類(如果是類的話……),他們的抽象類別是人。說明,他們都是人。人可以吃東西,狗也可以吃東西,你可以把“吃東西”定義成一個介面,然後讓這些類去實現它.
所以,在進階語言上,一個類只能繼承一個類(抽象類別)(正如人不可能同時是生物和非生物),但是可以實現多個介面(吃飯介面、走路介面)。
抽象類別的功能要遠超過介面,但是,定義抽象類別的代價高。因為進階語言來說(從實際設計上來說也是)每個類只能繼承一個類。在這個類中,你必須繼承或編寫出其所有子類的所有共性。雖然介面在功能上會弱化許多,但是它只是針對一個動作的描述。而且你可以在一個類中同時實現多個介面。在設計階段會降低難度的。
我們可以根據一個例子去加深一下印象:
門和警報的例子:門都有open( )和close( )兩個動作,此時我們可以定義通過抽象類別和介面來定義這個抽象概念:
抽象類別:
1 package abstract_interface;2 3 public abstract class Abstract_door {4 public abstract void close();5 public abstract void open();6 }
介面:
1 package abstract_interface;2 3 public interface Interface_door {4 public void close();5 public void open();6 }
但是現在如果我們需要門具有警示alarm( )的功能,那麼該如何??下面提供兩種思路:
1)將這三個功能都放在抽象類別裡面,但是這樣一來所有繼承於這個抽象類別的子類都具備了警示功能,但是有的門並不一定具備警示功能;
2)將這三個功能都放在介面裡面,需要用到警示功能的類就需要實現這個介面中的open( )和close( ),也許這個類根本就不具備open( )和close( )這兩個功能,比如火災通報器。
從這裡可以看出, Door的open() 、close()和alarm()根本就屬於兩個不同範疇內的行為,open()和close()屬於門本身固有的行為特性,而alarm()屬於延伸的附加行為。因此最好的解決辦法是單獨將警示設計為一個介面,包含alarm()行為,Door設計為單獨的一個抽象類別,包含open和close兩種行為。再設計一個警示門繼承Door類和實現Alarm介面。
1 package abstract_interface; 2 3 public abstract class Abstract_door { 4 public abstract void close(); 5 public abstract void open(); 6 } 7 8 9 10 11 package abstract_interface;12 13 public interface Interface_alarm {14 public void alarm();15 }16 17 18 19 package abstract_interface;20 21 public class Test2 extends Abstract_door implements Interface_alarm{22 23 @Override24 public void alarm() {25 // TODO Auto-generated method stub26 27 }28 29 @Override30 public void close() {31 // TODO Auto-generated method stub32 33 }34 35 @Override36 public void open() {37 // TODO Auto-generated method stub38 39 }40 41 }