更新時間:2020-10-16 17:28:12 來源:動力節點 瀏覽1578次
盡管抽象類和接口之間存在較大的相同點,甚至有時候還可以互換,但這并不能彌補他們之間的差異之處。下面將從語法層次和設計層次兩個大的方面對抽象類與接口區別進行闡述。
一、語法層次
在語法層次,java語言對于抽象類和接口分別給出了不同的定義。下面已Demo類來說明他們之間的不同之處。
使用抽象類來實現:
public abstract class Demo {
abstract void method1();
void method2(){
//實現
}
}
使用接口來實現
interface Demo {
void method1();
void method2();
}
抽象類方式中,抽象類可以擁有任意范圍的成員數據,同時也可以擁有自己的非抽象方法,但是接口方式中,它僅能夠有靜態、不能修改的成員數據(但是我們一般是不會在接口中使用成員數據),同時它所有的方法都必須是抽象的。在某種程度上來說,接口是抽象類的特殊化。
對子類而言,它只能繼承一個抽象類(這是java為了數據安全而考慮的),但是卻可以實現多個接口。
二、設計層次
上面只是從語法層次和編程角度來區分它們之間的關系,這些都是低層次的,要真正使用好抽象類和接口,我們就必須要從較高層次來區分了。只有從設計理念的角度才能看出它們的本質所在。一般來說他們存在如下三個不同點:
1、 抽象層次不同。抽象類是對類抽象,而接口是對行為的抽象。抽象類是對整個類整體進行抽象,包括屬性、行為,但是接口卻是對類局部(行為)進行抽象。
2、 跨域不同。抽象類所跨域的是具有相似特點的類,而接口卻可以跨域不同的類。我們知道抽象類是從子類中發現公共部分,然后泛化成抽象類,子類繼承該父類即可,但是接口不同。實現它的子類可以不存在任何關系,共同之處。例如貓、狗可以抽象成一個動物類抽象類,具備叫的方法。鳥、飛機可以實現飛Fly接口,具備飛的行為,這里我們總不能將鳥、飛機共用一個父類吧!所以說抽象類所體現的是一種繼承關系,要想使得繼承關系合理,父類和派生類之間必須存在"is-a" 關系,即父類和派生類在概念本質上應該是相同的。對于接口則不然,并不要求接口的實現者和接口定義在概念本質上是一致的, 僅僅是實現了接口定義的契約而已。
3、 設計層次不同。對于抽象類而言,它是自下而上來設計的,我們要先知道子類才能抽象出父類,而接口則不同,它根本就不需要知道子類的存在,只需要定義一個規則即可,至于什么子類、什么時候怎么實現它一概不知。比如我們只有一個貓類在這里,如果你這是就抽象成一個動物類,是不是設計有點兒過度?我們起碼要有兩個動物類,貓、狗在這里,我們在抽象他們的共同點形成動物抽象類吧!所以說抽象類往往都是通過重構而來的!但是接口就不同,比如說飛,我們根本就不知道會有什么東西來實現這個飛接口,怎么實現也不得而知,我們要做的就是事前定義好飛的行為接口。所以說抽象類是自底向上抽象而來的,接口是自頂向下設計出來的。
為了更好的闡述他們之間的區別,我們將用實例來說明:
我們有一個Door的抽象概念,它具備兩個行為open()和close(),此時我們可以定義通過抽象類和接口來定義這個抽象概念:
抽象類:
abstract class Door{
abstract void open();
abstract void close();
}
接口
interface Door{
void open();
void close();
}
至于其他的具體類可以通過使用extends使用抽象類方式定義Door或者Implements使用接口方式定義Door,這里發現兩者并沒有什么很大的差異。
但是現在如果我們需要門具有報警的功能,那么該如何實現呢?
解決方案一:給Door增加一個報警方法:clarm();
abstract class Door{
abstract void open();
abstract void close();
abstract void alarm();
}
或者
interface Door{
void open();
void close();
void alarm();
}
這種方法違反了面向對象設計中的一個核心原則 ISP (Interface Segregation Principle)—見批注,在Door的定義中把Door概念本身固有的行為方法和另外一個概念"報警器"的行為方 法混在了一起。這樣引起的一個問題是那些僅僅依賴于Door這個概念的模塊會因為"報警器"這個概念的改變而改變,反之依然。
解決方案二
既然open()、close()和alarm()屬于兩個不同的概念,那么我們依據ISP原則將它們分開定義在兩個代表兩個不同概念的抽象類里面,定義的方式有三種:
1、兩個都使用抽象類來定義。
2、兩個都使用接口來定義。
3、一個使用抽象類定義,一個是用接口定義。
由于java不支持多繼承所以第一種是不可行的。后面兩種都是可行的,但是選擇何種就反映了你對問題域本質的理解。
如果選擇第二種都是接口來定義,那么就反映了兩個問題:1、我們可能沒有理解清楚問題域,AlarmDoor在概念本質上到底是門還報警器。2、如果我們對問題域的理解沒有問題,比如我們在分析時確定了AlarmDoor在本質上概念是一致的,那么我們在設計時就沒有正確的反映出我們的設計意圖。因為你使用了兩個接口來進行定義,他們概念的定義并不能夠反映上述含義。
第三種,如果我們對問題域的理解是這樣的:AlarmDoor本質上Door,但同時它也擁有報警的行為功能,這個時候我們使用第三種方案恰好可以闡述我們的設計意圖。AlarmDoor本質是們,所以對于這個概念我們使用抽象類來定義,同時AlarmDoor具備報警功能,說明它能夠完成報警概念中定義的行為功能,所以alarm可以使用接口來進行定義。如下:
abstract class Door{
abstract void open();
abstract void close();
}
interface Alarm{
void alarm();
}
class AlarmDoor extends Door implements Alarm{
void open(){}
void close(){}
void alarm(){}
}
這種實現方式基本上能夠明確的反映出我們對于問題領域的理解,正確的揭示我們的設計意圖。其實抽象類表示的是"is-a"關系,接口表示的是"like-a"關系,大家在選擇時可以作為一個依據,當然這是建立在對問題領域的理解上的,比如:如果我們認為AlarmDoor在概念本質上是報警器,同時又具有Door的功能,那么上述的定義方式就要反過來了。
一個類對另外一個類的依賴性應當是建立在最小的接口上的。
一個接口代表一個角色,不應當將不同的角色都交給一個接口。沒有關系的接口合并在一起,形成一個臃腫的大接口,這是對角色和接口的污染。
通過實例,我們發現其實接口和抽象類區別還是蠻大的,不論是在語法層次,還是在設計層次都有著嚴格的區分。從構造方法到普通成員變量,這些都是區分接口和抽象類的典型標志。接口更多的是在系統架構設計方法發揮作用,主要用于定義模塊之間的通信契約。而抽象類在代碼實現方面發揮作用,可以實現代碼的重用。本文對接口和抽象類區別的講解是不是很透徹呢?這些Java基礎知識在本站的Java零基礎入門教程中還有更加精彩的講解,想深入學習Java的小伙伴不容錯過哦。
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習