夯實Java基礎(十)——抽象類和接口

以前講了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 }
相關文章
相關標籤/搜索