以前講了Java的三大特徵有封裝、繼承、多態,這樣說其實咱們少講了一個,那就是抽象性,在平時的教程中認爲只有三種特徵,由於它們把抽象放到繼承裏了,認爲抽象類是繼承的一種,這也使抽象性是不是Java的一大特徵具備爭議。而Java語言中對抽象概念定義的機制就是抽象類和接口,正因爲它們才賦予Java強大的面向對象的功能。他們二者之間對抽象概念的支持有很大的類似,可是也有區別。因此接下來咱們就來學習它們兩的使用和區別。(若是在平時面試時遇到問Java有幾大特徵,咱們就說封裝、繼承、多態這三特徵便可)html
在面向對象的概念中,咱們知道全部的對象都是經過類來描繪的,可是反過來卻不是這樣。並非全部的類都是用來描繪對象的,若是一個類中沒有包含足夠的信息來描繪一個具體的對象,這樣的類就是抽象類。抽象類每每用來表徵咱們在對問題領域進行分析、設計中得出的抽象概念,是對一系列看上去不一樣,可是本質上相同的具體概念的抽象。好比咱們在建立一個Animal對象時,咱們只知道動物有吃喝拉撒睡等特徵,可是殊不知道它究竟是哪種動物,此時這個Animal類是比較抽象的,因此咱們須要一個具體的類來描述該動物,如用狗、貓來對它進行特定的描述,咱們才知道它是什麼動物。面試
在Java中用abstract關鍵字來修飾的類就是抽象類,固然這個關鍵字也能夠用來修飾方法,代表該方法是抽象方法。ide
咱們在使用抽象類的時候須要注意如下幾點:學習
①、抽象類不能實例化,必需要由繼承它的子類來建立實例。spa
②、抽象方法只有方法的聲明,沒有方法體。設計
③、抽象類中的抽象方法必需要在子類中重寫。code
④、抽象類中既能夠有抽象方法,也能夠有普通方法,普通方法可不用重寫。htm
⑤、只要包含一個抽象方法的類,該類必需要定義成抽象類。對象
⑥、abstract不能用來修飾屬性、構造器等結構。blog
⑦、abstract不能與final並列修飾同一個類。
⑧、abstract不能與private、static、final或native並列修飾同一個方法。
而後給出一個簡單的示例:
1 public abstract class Animal { 2 //抽象方法 3 public abstract void eat(); 4 //普通方法 5 public void sleep(){ 6 System.out.println("動物須要睡覺..."); 7 } 8 } 9 10 class Cat extends Animal{ 11 12 @Override 13 public void eat() { 14 System.out.println("貓喜歡吃魚..."); 15 } 16 17 @Override 18 public void sleep() { 19 System.out.println("貓須要睡覺..."); 20 } 21 } 22 23 class Dog extends Animal { 24 25 @Override 26 public void eat() { 27 System.out.println("狗喜歡吃骨頭..."); 28 } 29 30 @Override 31 public void sleep() { 32 System.out.println("狗須要睡覺..."); 33 } 34 35 public static void main(String[] args) { 36 Animal a1=new Dog(); 37 a1.eat(); 38 a1.sleep(); 39 40 Animal a2= new Cat(); 41 a2.eat(); 42 a2.sleep(); 43 } 44 }
程序運行結果:
到這裏咱們能夠看出,抽象類就是爲了繼承而存在的,若是你定義了一個抽象類,卻不去繼承它,那麼這個抽象類就沒有任何的意義了。
接口的英文名稱是 interface。接口是屬於和類類型同等級別的結構,可是它不是類。卻和類類型有着共同點,在接口中能夠含有變量、方法。可是須要注意是,接口中的變量會被隱式地指定爲public static final變量,而且只能是public static final變量,用private修飾會報編譯錯誤。而接口中的方法會被隱式地指定爲public abstract方法,且只能是public abstract方法,若是用其餘關鍵字,如private、protected、static、 final等修飾都會致使報編譯錯誤。也就是說接口中的變量全都是常量,方法都是抽象方法(JDK1.8之前)。從這裏能夠隱約看出接口和抽象類的區別,接口是一種極度抽象的類型,它比抽象類更加「抽象」。
準確來講接口定義的是一種規範。 例如鳥和蝴蝶都是能夠飛的動物,它們都符合 飛行 的規範,因此它們能夠實現 飛 這個接口,對於任何其餘能夠飛行的動物都須要實現該接口。由於它們符合這種規範。
咱們在使用接口的時候一樣須要注意如下幾點:
①、接口使用interface關鍵字來定義,而且其子類要implement關鍵字來實現該接口。
②、接口和抽象類同樣不能實例化,必需要由實現它的子類來建立實例。
③、接口中定義的成員變量都是全局常量,系統會在變量前面自動加上public static final,能夠經過類命名直接訪問:ImplementClass.name。
④、接口中的方法都是抽象方法,可是在JDK1.8之後,能夠包含普通方法。
⑤、接口能夠多實現,而抽象類只有單繼承。
1 public interface Fly { 2 public void fly(); 3 } 4 5 class Bird implements Fly{ 6 7 @Override 8 public void fly() { 9 System.out.println("鳥能夠飛行..."); 10 } 11 } 12 13 class Butterfly implements Fly{ 14 15 @Override 16 public void fly() { 17 System.out.println("蝴蝶能夠飛行..."); 18 } 19 } 20 21 class Test{ 22 public static void main(String[] args) { 23 Fly bird = new Bird(); 24 bird.fly(); 25 26 Fly butterfly = new Butterfly(); 27 butterfly.fly(); 28 } 29 } 30 //運行結果: 31 //鳥能夠飛行... 32 //蝴蝶能夠飛行...
接口在JDK1.8以前說裏面的方法全都是抽象方法,可是在JDK1.8中,接口新添加了一些特性,咱們能夠在接口中添加靜態方法和默認方法。固然從技術的角度來看,這徹底合法的,只是看起來違反了接口做爲一個抽象定義的理念。
靜態方法:使用static關鍵字來修飾。可使用接口直接調用靜態方法而且執行該方法體的內容。咱們常常在相互一塊兒使用的類中 使用靜態方法。你能夠在標準庫中找到像Collection / Collections或者Path/Paths這樣成對的接口和類。
默認方法:默認方法必需要用default關鍵字來修飾。能夠經過子類的實現類來調用。咱們在已有的接口中提供新方法的同時,還保持了與舊版本代碼的兼容性。好比Collection、Comparator等接口中提供了豐富的默認方法。
1 public interface InterfaceTest { 2 //靜態方法 3 public static void method1(){ 4 System.out.println("接口中靜態方法..."); 5 } 6 7 //默認方法 8 public default void method2(){ 9 System.out.println("接口中默認方法..."); 10 } 11 12 //默認方法 13 default void method3(){ 14 System.out.println("接口中無修飾默認方法..."); 15 } 16 } 17 //建立一個父類 18 class SuperClass{ 19 public void method2() { 20 System.out.println("父類中默認方法method2..."); 21 } 22 23 public void method3() { 24 System.out.println("父類中默認方法method3..."); 25 } 26 } 27 //建立子類,繼承SuperClass,實現InterfaceTest接口 28 class SubClass extends SuperClass implements InterfaceTest{ 29 //重寫一個方法method2 30 @Override 31 public void method2() { 32 System.out.println("子類中默認方法method2..."); 33 } 34 } 35 36 class Test{ 37 public static void main(String[] args) { 38 //調用接口中靜態方法 39 InterfaceTest.method1(); 40 41 InterfaceTest test=new SubClass(); 42 test.method2(); 43 test.method3(); 44 } 45 }
程序運行結果:
經過運行咱們能夠得出如下幾點:
①、接口中靜態方法只能用接口. 方法來調用,而不能用實例調用。
②、接口中默認方法必需要用子類的實例來調用。
③、若是子類重寫了接口中的默認方法,則調用的是子類中重寫方法的內容。
④、若是子類(或實現類)繼承的父類和實現的接口中聲明瞭同名同參數的方法,那麼子類在沒有重寫該方法的狀況下,默認調用父類中的同名同參數的方法。-->類優先原則。
⑤、若是實現類實現了多個接口(沒有繼承),而多個接口中定義了同名同參數的默認方法,那麼在實現類沒有重寫此方法的狀況下,會報錯。-->接口衝突。這就須要咱們在實現類中重寫此方法。
⑥、若是須要調用父類中方法或接口中默認方法,父類使用 super . 方法名,接口使用 接口名 . super . 方法名。
這裏講的很是的細,其實咱們只須要了解便可。
如下參考連接:http://www.javashuo.com/article/p-ysawzibf-bo.html
儘管抽象類和接口之間存在較大的相同點,甚至有時候還能夠互換,但這樣並不能彌補他們之間的差別之處。下面將從語法層次和設計層次兩個方面對抽象類和接口進行闡述。
1.語法層面上的區別
1)抽象類能夠提供成員方法的實現細節,而接口中只能存在public abstract 方法;
2)抽象類中的成員變量能夠是各類類型的,而接口中的成員變量只能是public static final類型的;
3)接口中不能含有靜態代碼塊以及靜態方法,而抽象類能夠有靜態代碼塊和靜態方法;
4)一個類只能繼承一個抽象類,而一個類卻能夠實現多個接口。
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 abstract class Door { 2 public abstract void open(); 3 public abstract void close(); 4 }
或者:
1 interface Door { 2 public abstract void open(); 3 public abstract void close(); 4 }
可是如今若是咱們須要門具備報警alarm( )的功能,那麼該如何實現?下面提供兩種思路:
1)將這三個功能都放在抽象類裏面,可是這樣一來全部繼承於這個抽象類的子類都具有了報警功能,可是有的門並不必定具有報警功能;
2)將這三個功能都放在接口裏面,須要用到報警功能的類就須要實現這個接口中的open( )和close( ),也許這個類根本就不具有open( )和close( )這兩個功能,好比火災報警器。
從這裏能夠看出, Door的open() 、close()和alarm()根本就屬於兩個不一樣範疇內的行爲,open()和close()屬於門自己固有的行爲特性,而alarm()屬於延伸的附加行爲。所以最好的解決辦法是單獨將報警設計爲一個接口,包含alarm()行爲,Door設計爲單獨的一個抽象類,包含open和close兩種行爲。再設計一個報警門繼承Door類和實現Alarm接口。
1 interface Alram { 2 void alarm(); 3 } 4 5 abstract class Door { 6 void open(); 7 void close(); 8 } 9 10 class AlarmDoor extends Door implements Alarm { 11 void oepn() { 12 //.... 13 } 14 void close() { 15 //.... 16 } 17 void alarm() { 18 //.... 19 } 20 }