接口和內(nèi)部類(lèi)為我們提供了一種將接口與實(shí)現(xiàn)分離的更加結(jié)構(gòu)化的方法。
抽象類(lèi)與接口是java語(yǔ)言中對(duì)抽象概念進(jìn)行定義的兩種機(jī)制,正是由于他們的存在才賦予java強(qiáng)大的面向?qū)ο蟮哪芰ΑK麄儍烧咧g對(duì)抽象概念的支持有很大的相似,甚至可以互換,但是也有區(qū)別。
一、抽象類(lèi)
我們都知道在面向?qū)ο蟮念I(lǐng)域一切都是對(duì)象,同時(shí)所有的對(duì)象都是通過(guò)類(lèi)來(lái)描述的,但是并不是所有的類(lèi)都是來(lái)描述對(duì)象的。如果一個(gè)類(lèi)沒(méi)有足夠的信息來(lái)描述一個(gè)具體的對(duì)象,而需要其他具體的類(lèi)來(lái)支撐它,那么這樣的類(lèi)我們稱(chēng)它為抽象類(lèi)。比如new Animal(),我們都知道這個(gè)是產(chǎn)生一個(gè)動(dòng)物Animal對(duì)象,但是這個(gè)Animal具體長(zhǎng)成什么樣子我們并不知道,它沒(méi)有一個(gè)具體動(dòng)物的概念,所以他就是一個(gè)抽象類(lèi),需要一個(gè)具體的動(dòng)物,如狗、貓來(lái)對(duì)它進(jìn)行特定的描述,我們才知道它長(zhǎng)成啥樣。
在面向?qū)ο箢I(lǐng)域由于抽象的概念在問(wèn)題領(lǐng)域沒(méi)有對(duì)應(yīng)的具體概念,所以用以表征抽象概念的抽象類(lèi)是不能實(shí)例化的。
同時(shí),抽象類(lèi)體現(xiàn)了數(shù)據(jù)抽象的思想,是實(shí)現(xiàn)多態(tài)的一種機(jī)制。它定義了一組抽象的方法,至于這組抽象方法的具體表現(xiàn)形式有派生類(lèi)來(lái)實(shí)現(xiàn)。同時(shí)抽象類(lèi)提供了繼承的概念,它的出發(fā)點(diǎn)就是為了繼承,否則它沒(méi)有存在的任何意義。所以說(shuō)定義的抽象類(lèi)一定是用來(lái)繼承的,同時(shí)在一個(gè)以抽象類(lèi)為節(jié)點(diǎn)的繼承關(guān)系等級(jí)鏈中,葉子節(jié)點(diǎn)一定是具體的實(shí)現(xiàn)類(lèi)。(不知這樣理解是否有錯(cuò)!!!高手指點(diǎn)….)
在使用抽象類(lèi)時(shí)需要注意幾點(diǎn):
1、抽象類(lèi)不能被實(shí)例化,實(shí)例化的工作應(yīng)該交由它的子類(lèi)來(lái)完成,它只需要有一個(gè)引用即可。
2、抽象方法必須由子類(lèi)來(lái)進(jìn)行重寫(xiě)。
3、只要包含一個(gè)抽象方法的抽象類(lèi),該方法必須要定義成抽象類(lèi),不管是否還包含有其他方法。
4、抽象類(lèi)中可以包含具體的方法,當(dāng)然也可以不包含抽象方法。
5、子類(lèi)中的抽象方法不能與父類(lèi)的抽象方法同名。
6、abstract不能與final并列修飾同一個(gè)類(lèi)。
7、abstract 不能與PRivate、static、final或native并列修飾同一個(gè)方法。、
實(shí)例:
定義一個(gè)抽象動(dòng)物類(lèi)Animal,提供抽象方法叫cry(),貓、狗都是動(dòng)物類(lèi)的子類(lèi),由于cry()為抽象方法,所以Cat、Dog必須要實(shí)現(xiàn)cry()方法。如下:
[java] view plain copypublic 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: 貓叫:喵喵... 狗叫:汪汪...
創(chuàng)建抽象類(lèi)和抽象方法非常有用,因?yàn)樗麄兛梢允诡?lèi)的抽象性明確起來(lái),并告訴用戶和編譯器打算怎樣使用他們.抽象類(lèi)還是有用的重構(gòu)器,因?yàn)樗鼈兪刮覀兛梢院苋菀椎貙⒐卜椒ㄑ刂^承層次結(jié)構(gòu)向上移動(dòng)。(From:Think%20in%20java%20)
二、接口
接口是一種比抽象類(lèi)更加抽象的“類(lèi)”。這里給“類(lèi)”加引號(hào)是我找不到更好的詞來(lái)表示,但是我們要明確一點(diǎn)就是,接口本身就不是類(lèi),從我們不能實(shí)例化一個(gè)接口就可以看出。如new%20Runnable();肯定是錯(cuò)誤的,我們只能new它的實(shí)現(xiàn)類(lèi)。
接口是用來(lái)建立類(lèi)與類(lèi)之間的協(xié)議,它所提供的只是一種形式,而沒(méi)有具體的實(shí)現(xiàn)。同時(shí)實(shí)現(xiàn)該接口的實(shí)現(xiàn)類(lèi)必須要實(shí)現(xiàn)該接口的所有方法,通過(guò)使用implements關(guān)鍵字,他表示該類(lèi)在遵循某個(gè)或某組特定的接口,同時(shí)也表示著“interface只是它的外貌,但是現(xiàn)在需要聲明它是如何工作的”。
接口是抽象類(lèi)的延伸,java了保證數(shù)據(jù)安全是不能多重繼承的,也就是說(shuō)繼承只能存在一個(gè)父類(lèi),但是接口不同,一個(gè)類(lèi)可以同時(shí)實(shí)現(xiàn)多個(gè)接口,不管這些接口之間有沒(méi)有關(guān)系,所以接口彌補(bǔ)了抽象類(lèi)不能多重繼承的缺陷,但是推薦繼承和接口共同使用,因?yàn)檫@樣既可以保證數(shù)據(jù)安全性又可以實(shí)現(xiàn)多重繼承。
在使用接口過(guò)程中需要注意如下幾個(gè)問(wèn)題:
1、個(gè)Interface的方所有法訪問(wèn)權(quán)限自動(dòng)被聲明為public。確切的說(shuō)只能為public,當(dāng)然你可以顯示的聲明為protected、private,但是編譯會(huì)出錯(cuò)!
2、接口中可以定義“成員變量”,或者說(shuō)是不可變的常量,因?yàn)榻涌谥械摹俺蓡T變量”會(huì)自動(dòng)變?yōu)闉閜ublic%20static%20final。可以通過(guò)類(lèi)命名直接訪問(wèn):ImplementClass.name。
3、接口中不存在實(shí)現(xiàn)的方法。
%20 4、實(shí)現(xiàn)接口的非抽象類(lèi)必須要實(shí)現(xiàn)該接口的所有方法。抽象類(lèi)可以不用實(shí)現(xiàn)。
%20 %20 5、不能使用new操作符實(shí)例化一個(gè)接口,但可以聲明一個(gè)接口變量,該變量必須引用(refer%20to)一個(gè)實(shí)現(xiàn)該接口的類(lèi)的對(duì)象。可以使用%20instanceof%20檢查一個(gè)對(duì)象是否實(shí)現(xiàn)了某個(gè)特定的接口。例如:if(anObject%20instanceof%20Comparable){}。
%20 %20 6、在實(shí)現(xiàn)多接口的時(shí)候一定要避免方法名的重復(fù)。
三、抽象類(lèi)與接口的區(qū)別
盡管抽象類(lèi)和接口之間存在較大的相同點(diǎn),甚至有時(shí)候還可以互換,但這樣并不能彌補(bǔ)他們之間的差異之處。下面將從語(yǔ)法層次和設(shè)計(jì)層次兩個(gè)方面對(duì)抽象類(lèi)和接口進(jìn)行闡述。
3.1語(yǔ)法層次
在語(yǔ)法層次,java語(yǔ)言對(duì)于抽象類(lèi)和接口分別給出了不同的定義。下面已Demo類(lèi)來(lái)說(shuō)明他們之間的不同之處。
使用抽象類(lèi)來(lái)實(shí)現(xiàn):
[java] view%20plain copypublic abstract class Demo { abstract void method1(); void method2(){ //實(shí)現(xiàn) } } 使用接口來(lái)實(shí)現(xiàn)[java] view%20plain copy
interface Demo { void method1(); void method2(); } 抽象類(lèi)方式中,抽象類(lèi)可以擁有任意范圍的成員數(shù)據(jù),同時(shí)也可以擁有自己的非抽象方法,但是接口方式中,它僅能夠有靜態(tài)、不能修改的成員數(shù)據(jù)(但是我們一般是不會(huì)在接口中使用成員數(shù)據(jù)),同時(shí)它所有的方法都必須是抽象的。在某種程度上來(lái)說(shuō),接口是抽象類(lèi)的特殊化。
對(duì)子類(lèi)而言,它只能繼承一個(gè)抽象類(lèi)(這是java為了數(shù)據(jù)安全而考慮的),但是卻可以實(shí)現(xiàn)多個(gè)接口。
3.2設(shè)計(jì)層次
上面只是從語(yǔ)法層次和編程角度來(lái)區(qū)分它們之間的關(guān)系,這些都是低層次的,要真正使用好抽象類(lèi)和接口,我們就必須要從較高層次來(lái)區(qū)分了。只有從設(shè)計(jì)理念的角度才能看出它們的本質(zhì)所在。一般來(lái)說(shuō)他們存在如下三個(gè)不同點(diǎn):
1、%20抽象層次不同。抽象類(lèi)是對(duì)類(lèi)抽象,而接口是對(duì)行為的抽象。抽象類(lèi)是對(duì)整個(gè)類(lèi)整體進(jìn)行抽象,包括屬性、行為,但是接口卻是對(duì)類(lèi)局部(行為)進(jìn)行抽象。
2、%20跨域不同。抽象類(lèi)所跨域的是具有相似特點(diǎn)的類(lèi),而接口卻可以跨域不同的類(lèi)。我們知道抽象類(lèi)是從子類(lèi)中發(fā)現(xiàn)公共部分,然后泛化成抽象類(lèi),子類(lèi)繼承該父類(lèi)即可,但是接口不同。實(shí)現(xiàn)它的子類(lèi)可以不存在任何關(guān)系,共同之處。例如貓、狗可以抽象成一個(gè)動(dòng)物類(lèi)抽象類(lèi),具備叫的方法。鳥(niǎo)、飛機(jī)可以實(shí)現(xiàn)飛Fly接口,具備飛的行為,這里我們總不能將鳥(niǎo)、飛機(jī)共用一個(gè)父類(lèi)吧!所以說(shuō)抽象類(lèi)所體現(xiàn)的是一種繼承關(guān)系,要想使得繼承關(guān)系合理,父類(lèi)和派生類(lèi)之間必須存在"is-a"%20關(guān)系,即父類(lèi)和派生類(lèi)在概念本質(zhì)上應(yīng)該是相同的。對(duì)于接口則不然,并不要求接口的實(shí)現(xiàn)者和接口定義在概念本質(zhì)上是一致的,%20僅僅是實(shí)現(xiàn)了接口定義的契約而已。
3、%20設(shè)計(jì)層次不同。對(duì)于抽象類(lèi)而言,它是自下而上來(lái)設(shè)計(jì)的,我們要先知道子類(lèi)才能抽象出父類(lèi),而接口則不同,它根本就不需要知道子類(lèi)的存在,只需要定義一個(gè)規(guī)則即可,至于什么子類(lèi)、什么時(shí)候怎么實(shí)現(xiàn)它一概不知。比如我們只有一個(gè)貓類(lèi)在這里,如果你這是就抽象成一個(gè)動(dòng)物類(lèi),是不是設(shè)計(jì)有點(diǎn)兒過(guò)度?我們起碼要有兩個(gè)動(dòng)物類(lèi),貓、狗在這里,我們?cè)诔橄笏麄兊墓餐c(diǎn)形成動(dòng)物抽象類(lèi)吧!所以說(shuō)抽象類(lèi)往往都是通過(guò)重構(gòu)而來(lái)的!但是接口就不同,比如說(shuō)飛,我們根本就不知道會(huì)有什么東西來(lái)實(shí)現(xiàn)這個(gè)飛接口,怎么實(shí)現(xiàn)也不得而知,我們要做的就是事前定義好飛的行為接口。所以說(shuō)抽象類(lèi)是自底向上抽象而來(lái)的,接口是自頂向下設(shè)計(jì)出來(lái)的。
(上面純屬個(gè)人見(jiàn)解,如有出入、錯(cuò)誤之處,望各位指點(diǎn)!!!!)
為了更好的闡述他們之間的區(qū)別,下面將使用一個(gè)例子來(lái)說(shuō)明。該例子引自:http://blog.csdn.net/ttgjz/article/details/2960451
我們有一個(gè)Door的抽象概念,它具備兩個(gè)行為open()和close(),此時(shí)我們可以定義通過(guò)抽象類(lèi)和接口來(lái)定義這個(gè)抽象概念:
抽象類(lèi):
[java] view%20plain copyabstract class Door{ abstract void open(); abstract void close(); } 接口[java] view%20plain copy
interface Door{ void open(); void close(); } 至于其他的具體類(lèi)可以通過(guò)使用extends使用抽象類(lèi)方式定義Door或者Implements使用接口方式定義Door,這里發(fā)現(xiàn)兩者并沒(méi)有什么很大的差異。
但是現(xiàn)在如果我們需要門(mén)具有報(bào)警的功能,那么該如何實(shí)現(xiàn)呢?
解決方案一:給Door增加一個(gè)報(bào)警方法:clarm();
[java] view%20plain copyabstract class Door{ abstract void open(); abstract void close(); abstract void alarm(); } 或者[java] view%20plain copy
interface Door{ void open(); void close(); void alarm(); } 這種方法違反了面向?qū)ο笤O(shè)計(jì)中的一個(gè)核心原則%20ISP%20(Interface%20Segregation%20Princ
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(){} } 這種實(shí)現(xiàn)方式基本上能夠明確的反映出我們對(duì)于問(wèn)題領(lǐng)域的理解,正確的揭示我們的設(shè)計(jì)意圖。其實(shí)抽象類(lèi)表示的是"is-a"關(guān)系,接口表示的是"like-a"關(guān)系,大家在選擇時(shí)可以作為一個(gè)依據(jù),當(dāng)然這是建立在對(duì)問(wèn)題領(lǐng)域的理解上的,比如:如果我們認(rèn)為AlarmDoor在概念本質(zhì)上是報(bào)警器,同時(shí)又具有Door的功能,那么上述的定義方式就要反過(guò)來(lái)了。
批注:
ISP(Interface Segregation Principle):面向?qū)ο蟮囊粋€(gè)核心原則。它表明使用多個(gè)專(zhuān)門(mén)的接口比使用單一的總接口要好。
一個(gè)類(lèi)對(duì)另外一個(gè)類(lèi)的依賴(lài)性應(yīng)當(dāng)是建立在最小的接口上的。
一個(gè)接口代表一個(gè)角色,不應(yīng)當(dāng)將不同的角色都交給一個(gè)接口。沒(méi)有關(guān)系的接口合并在一起,形成一個(gè)臃腫的大接口,這是對(duì)角色和接口的污染。
四、總結(jié)
1、 抽象類(lèi)在java語(yǔ)言中所表示的是一種繼承關(guān)系,一個(gè)子類(lèi)只能存在一個(gè)父類(lèi),但是可以存在多個(gè)接口。
2、 在抽象類(lèi)中可以擁有自己的成員變量和非抽象類(lèi)方法,但是接口中只能存在靜態(tài)的不可變的成員數(shù)據(jù)(不過(guò)一般都不在接口中定義成員數(shù)據(jù)),而且它的所有方法都是抽象的。
3、抽象類(lèi)和接口所反映的設(shè)計(jì)理念是不同的,抽象類(lèi)所代表的是“is-a”的關(guān)系,而接口所代表的是“l(fā)ike-a”的關(guān)系。
抽象類(lèi)和接口是java語(yǔ)言中兩種不同的抽象概念,他們的存在對(duì)多態(tài)提供了非常好的支持,雖然他們之間存在很大的相似性。但是對(duì)于他們的選擇往往反應(yīng)了您對(duì)問(wèn)題域的理解。只有對(duì)問(wèn)題域的本質(zhì)有良好的理解,才能做出正確、合理的設(shè)計(jì)。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注