接口和內部類爲咱們提供了一種將接口與實現分離的更加結構化的方法。html
抽象類與接口是java語言中對抽象概念進行定義的兩種機制,正是因爲他們的存在才賦予java強大的面向對象的能力。他們二者之間對抽象概念的支持有很大的類似,甚至能夠互換,可是也有區別。java
咱們都知道在面向對象的領域一切都是對象,同時全部的對象都是經過類來描述的,可是並非全部的類都是來描述對象的。若是一個類沒有足夠的信息來描述一個具體的對象,而須要其餘具體的類來支撐它,那麼這樣的類咱們稱它爲抽象類。好比new Animal(),咱們都知道這個是產生一個動物Animal對象,可是這個Animal具體長成什麼樣子咱們並不知道,它沒有一個具體動物的概念,因此他就是一個抽象類,須要一個具體的動物,如狗、貓來對它進行特定的描述,咱們才知道它長成啥樣。編程
在面向對象領域因爲抽象的概念在問題領域沒有對應的具體概念,因此用以表徵抽象概念的抽象類是不能實例化的。跨域
同時,抽象類體現了數據抽象的思想,是實現多態的一種機制。它定義了一組抽象的方法,至於這組抽象方法的具體表現形式有派生類來實現。同時抽象類提供了繼承的概念,它的出發點就是爲了繼承,不然它沒有存在的任何意義。因此說定義的抽象類必定是用來繼承的,同時在一個以抽象類爲節點的繼承關係等級鏈中,葉子節點必定是具體的實現類。(不知這樣理解是否有錯!!!高手指點….)安全
在使用抽象類時須要注意幾點:ide
一、抽象類不能被實例化,實例化的工做應該交由它的子類來完成,它只須要有一個引用便可。spa
二、抽象方法必須由子類來進行重寫。.net
三、只要包含一個抽象方法的抽象類,該方法必需要定義成抽象類,無論是否還包含有其餘方法。設計
四、抽象類中能夠包含具體的方法,固然也能夠不包含抽象方法。code
五、子類中的抽象方法不能與父類的抽象方法同名。
六、abstract不能與final並列修飾同一個類。
七、abstract 不能與private、static、final或native並列修飾同一個方法。、
實例:
定義一個抽象動物類Animal,提供抽象方法叫cry(),貓、狗都是動物類的子類,因爲cry()爲抽象方法,因此Cat、Dog必需要實現cry()方法。以下:
public abstract class Animal { public abstract void cry(); } public class Cat extends Animal{ @Override public void cry() { System.out.println("貓叫:喵喵..."); } } public class Dog extends Animal{ @Override public void cry() { System.out.println("狗叫:汪汪..."); } } public class Test { public static void main(String[] args) { Animal a1 = new Cat(); Animal a2 = new Dog(); a1.cry(); a2.cry(); } } -------------------------------------------------------------------- Output: 貓叫:喵喵... 狗叫:汪汪...
建立抽象類和抽象方法很是有用,由於他們可使類的抽象性明確起來,並告訴用戶和編譯器打算怎樣使用他們.抽象類仍是有用的重構器,由於它們使咱們能夠很容易地將公共方法沿着繼承層次結構向上移動。(From:Think in java )
接口是一種比抽象類更加抽象的「類」。這裏給「類」加引號是我找不到更好的詞來表示,可是咱們要明確一點就是,接口自己就不是類,從咱們不能實例化一個接口就能夠看出。如new Runnable();確定是錯誤的,咱們只能new它的實現類。
接口是用來創建類與類之間的協議,它所提供的只是一種形式,而沒有具體的實現。同時實現該接口的實現類必需要實現該接口的全部方法,經過使用implements關鍵字,他表示該類在遵循某個或某組特定的接口,同時也表示着「interface只是它的外貌,可是如今須要聲明它是如何工做的」。
接口是抽象類的延伸,java了保證數據安全是不能多重繼承的,也就是說繼承只能存在一個父類,可是接口不一樣,一個類能夠同時實現多個接口,無論這些接口之間有沒有關係,因此接口彌補了抽象類不能多重繼承的缺陷,可是推薦繼承和接口共同使用,由於這樣既能夠保證數據安全性又能夠實現多重繼承。
在使用接口過程當中須要注意以下幾個問題:
一、個Interface的方全部法訪問權限自動被聲明爲public。確切的說只能爲public,固然你能夠顯示的聲明爲protected、private,可是編譯會出錯!
二、接口中能夠定義「成員變量」,或者說是不可變的常量,由於接口中的「成員變量」會自動變爲爲public static final。能夠經過類命名直接訪問:ImplementClass.name。
三、接口中不存在實現的方法。
四、實現接口的非抽象類必需要實現該接口的全部方法。抽象類能夠不用實現。
五、不能使用new操做符實例化一個接口,但能夠聲明一個接口變量,該變量必須引用(refer to)一個實現該接口的類的對象。可使用 instanceof 檢查一個對象是否實現了某個特定的接口。例如:if(anObject instanceof Comparable){}。
六、在實現多接口的時候必定要避免方法名的重複。
儘管抽象類和接口之間存在較大的相同點,甚至有時候還能夠互換,但這樣並不能彌補他們之間的差別之處。下面將從語法層次和設計層次兩個方面對抽象類和接口進行闡述。
在語法層次,java語言對於抽象類和接口分別給出了不一樣的定義。下面已Demo類來講明他們之間的不一樣之處。
使用抽象類來實現:
public abstract class Demo { abstract void method1(); void method2(){ //實現 } }
使用接口來實現
interface Demo { void method1(); void method2(); }
抽象類方式中,抽象類能夠擁有任意範圍的成員數據,同時也能夠擁有本身的非抽象方法,可是接口方式中,它僅可以有靜態、不能修改的成員數據(可是咱們通常是不會在接口中使用成員數據),同時它全部的方法都必須是抽象的。在某種程度上來講,接口是抽象類的特殊化。
對子類而言,它只能繼承一個抽象類(這是java爲了數據安全而考慮的),可是卻能夠實現多個接口。
上面只是從語法層次和編程角度來區分它們之間的關係,這些都是低層次的,要真正使用好抽象類和接口,咱們就必需要從較高層次來區分了。只有從設計理念的角度才能看出它們的本質所在。通常來講他們存在以下三個不一樣點:
一、 抽象層次不一樣。抽象類是對類抽象,而接口是對行爲的抽象。抽象類是對整個類總體進行抽象,包括屬性、行爲,可是接口倒是對類局部(行爲)進行抽象。
二、 跨域不一樣。抽象類所跨域的是具備類似特色的類,而接口卻能夠跨域不一樣的類。咱們知道抽象類是從子類中發現公共部分,而後泛化成抽象類,子類繼承該父類便可,可是接口不一樣。實現它的子類能夠不存在任何關係,共同之處。例如貓、狗能夠抽象成一個動物類抽象類,具有叫的方法。鳥、飛機能夠實現飛Fly接口,具有飛的行爲,這裏咱們總不能將鳥、飛機共用一個父類吧!因此說抽象類所體現的是一種繼承關係,要想使得繼承關係合理,父類和派生類之間必須存在"is-a" 關係,即父類和派生類在概念本質上應該是相同的。對於接口則否則,並不要求接口的實現者和接口定義在概念本質上是一致的, 僅僅是實現了接口定義的契約而已。
三、 設計層次不一樣。對於抽象類而言,它是自下而上來設計的,咱們要先知道子類才能抽象出父類,而接口則不一樣,它根本就不須要知道子類的存在,只須要定義一個規則便可,至於什麼子類、何時怎麼實現它一律不知。好比咱們只有一個貓類在這裏,若是你這是就抽象成一個動物類,是否是設計有點兒過分?咱們起碼要有兩個動物類,貓、狗在這裏,咱們在抽象他們的共同點造成動物抽象類吧!因此說抽象類每每都是經過重構而來的!可是接口就不一樣,好比說飛,咱們根本就不知道會有什麼東西來實現這個飛接口,怎麼實現也不得而知,咱們要作的就是事前定義好飛的行爲接口。因此說抽象類是自底向上抽象而來的,接口是自頂向下設計出來的。
(上面純屬我的看法,若有出入、錯誤之處,望各位指點!!!!)
爲了更好的闡述他們之間的區別,下面將使用一個例子來講明。該例子引自:http://blog.csdn.net/ttgjz/article/details/2960451
咱們有一個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原則將它們分開定義在兩個表明兩個不一樣概念的抽象類裏面,定義的方式有三種:
一、兩個都使用抽象類來定義。
二、兩個都使用接口來定義。
三、一個使用抽象類定義,一個是用接口定義。
因爲java不支持多繼承因此第一種是不可行的。後面兩種都是可行的,可是選擇何種就反映了你對問題域本質的理解。
若是選擇第二種都是接口來定義,那麼就反映了兩個問題:一、咱們可能沒有理解清楚問題域,AlarmDoor在概念本質上究竟是門還報警器。二、若是咱們對問題域的理解沒有問題,好比咱們在分析時肯定了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的功能,那麼上述的定義方式就要反過來了。
批註:
ISP(Interface Segregation Principle):面向對象的一個核心原則。它代表使用多個專門的接口比使用單一的總接口要好。
一個類對另一個類的依賴性應當是創建在最小的接口上的。
一個接口表明一個角色,不該當將不一樣的角色都交給一個接口。沒有關係的接口合併在一塊兒,造成一個臃腫的大接口,這是對角色和接口的污染。
一、 抽象類在java語言中所表示的是一種繼承關係,一個子類只能存在一個父類,可是能夠存在多個接口。
二、 在抽象類中能夠擁有本身的成員變量和非抽象類方法,可是接口中只能存在靜態的不可變的成員數據(不過通常都不在接口中定義成員數據),並且它的全部方法都是抽象的。
三、抽象類和接口所反映的設計理念是不一樣的,抽象類所表明的是「is-a」的關係,而接口所表明的是「like-a」的關係。
抽象類和接口是java語言中兩種不一樣的抽象概念,他們的存在對多態提供了很是好的支持,雖然他們之間存在很大的類似性。可是對於他們的選擇每每反應了您對問題域的理解。只有對問題域的本質有良好的理解,才能作出正確、合理的設計。
鞏固基礎,提升技術,不懼困難,攀登高峯!!!!!!