淺析接口和抽象類的區別與使用場景


      對於面向對象編程來說,抽象是它的四大特征之一。在Java中,可以通過兩種形式來體現OOP的抽象——接口和抽象類。這兩者有太多相似的地方,又有太多不同的地方。很多人在初學的時候會以為它們可以隨意互換使用,但是,事實並非如此。 我們現在縱向對比二者的區別。首先,溫故知新,回顧二者的定義;然后,知己知彼,聊聊二者的區別,簡要介紹應用場景;其次,舉例說明應用場景;最后,列舉幾個常見問題。 

1、 基本概念 

       含有abstract修飾符的類即為抽象類。抽象類不能創建實例對象,含有抽象方法的類必須定義為abstract class。在abstract class中,方法不必是抽象的,但是抽象方法必須在具體子類中實現,所以,不能有抽象構造方法或抽象靜態方法。子類如果沒有實現抽象父類中的所有抽象方法,則必須定義為abstract類型。 

       下面要注意一個問題:在《JAVA編程思想》一書中,將抽象類定義為“包含抽象方法的類”。但是在書中其它地方發現,一個類如果不包含抽象方法,只是用abstract修飾,那么也是抽象類,即抽象類不一定必須包含抽象方法。個人覺得這個屬於鑽牛角尖的問題,因為一個抽象類如果不包含任何抽象方法,為何還要設計為抽象類?所以暫且記住這個概念吧,不必去深究為什么。 

       接口(interface)可以說成是抽象類的一種特例,其中的所有方法都必須是抽象的。接口中的方法定義默認為public abstract類型,成員變量類型默認為public static final。 

       抽象類可以繼承實體類。但和實體類的繼承一樣,也要求父類可繼承,並且擁有子類可以訪問到的構造函數。其實Object就是個實體類,Java的API文檔里,每個抽象類的條目里都明確寫着直接或間接繼承自Object,所以這點是沒有疑問的。 

2、抽象類和接口的區別 

       只要明白了接口和抽象類的本質和作用,這個問題就很好回答。試想,你如果是java語言的設計者,是否會提供這樣的支持,如果不提供,有什么理由嗎?如果沒有道理不提供,那答案就是肯定的了。

 

 

抽象類

接口

方法默認實現

支持

不支持,接口完全是抽象的

實現

子類使用extends關鍵字來繼承抽象類。子類如果不是抽象類,需要實現抽象類中聲明的所有抽象方法

子類使用關鍵字implements來實現接口,需要實現接口中聲明的所有方法

是否有構造函數

與正常Java類的區別

不能實例化抽象類,因為有abstract方法

接口是完全不同的類型

訪問修飾符

publicprotecteddefault

只有public

main方法

支持

不支持

多繼承

繼承一個類和實現多個接口

只可繼承一個或多個其它接口

速度

速度快

稍微有點慢,因為它需要時間去尋找在類中實現的方法

添加新方法

添加后可以給它提供默認的實現,故不需要改變現在的代碼

添加后必須改變實現該接口的類

      從設計層面看,抽象類體現繼承關系(is a),它主要描述類的從屬關系或者父子關系,抽象類和它的派生類之間是典型的IS-A關系,即“子is a父”。 

       interface可以多實現,而且不要求實現者和interface定義在概念本質上是一致的,僅僅是實現了interface定義的契約而已。它主要描述的是類型間的行為合同,接口和它的實現類之間是典型的CAN-DO關系,即“子can do父”。 

3、 應用場景介紹 

       什么時候使用抽象類和接口? 

  • 如果擁有一些方法並且想讓它們中的一些有默認實現,那么使用抽象類吧。 
  • 如果想實現多重繼承,那么必須使用接口。由於Java不支持多繼承,即一個類只能有一個超類。但是,可以實現多個接口,因此可以使用接口來解決它。 
  • 如果基本功能在不斷改變,那么就需要使用抽象類,達到解耦目的。如果不斷改變基本功能並且使用接口,那么就需要改變所有實現了該接口的類。

       接口更多的是在系統架構設計方面發揮作用,主要用於定義模塊之間的通信契約。而抽象類在代碼實現方面發揮作用,可以實現代碼的重用。例如,模板方法設計模式就是抽象類的一個典型應用,假設某個項目的所有HTTP請求都要用相同的方式進行權限判斷、訪問日志記錄和異常處理,那么就可以定義一個抽象的基類,讓所有的controller都繼承這個抽象基類,在抽象基類的service方法中實現上述功能,在各個子類中只是完成各自的業務邏輯代碼,偽代碼如下:

import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public abstract class BaseServlet extends HttpServlet {  public final void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // 記錄訪問日志 // 進行權限判斷 } protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException; // 注意訪問權限定義成protected,顯得既專業,又嚴謹,因為它是專門給子類用的 } class MyServlet1 extends BaseServlet { protected void doService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // 本Servlet只處理的具體業務邏輯代碼 } }

        基類方法中間的某段代碼不確定,留給子類去實現。

       溫馨提示:這道題的思路是先從總體解釋抽象類和接口的基本概念,然后再比較兩者的語法細節,最后再說兩者的應用區別。比較兩者語法細節區別的條理是:先從一個類中的構造方法、普通成員變量和方法(包括抽象方法),靜態變量和方法,繼承性等6個方面逐一去比較回答,接着從第三者繼承的角度的回答,特別是最后用了一個典型的例子來展現自己深厚的技術功底。

4、舉例說明

   下面看一個網上流傳最廣泛的例子——門和警報——門都有open( )和close( )兩個動作,通過抽象類和接口來定義這個抽象概念。  

abstract class Door { public abstract void open(); public abstract void close(); }

或者

interface Door { public abstract void open(); public abstract void close(); }

     但是現在如果需要門具有報警alarm( )的功能,那么該如何實現?下面提供兩種思路:

  • 將這三個功能都放在抽象類里面,但是這樣一來所有繼承於這個抽象類的子類都具備了報警功能,但是有的門並不一定具備報警功能;
  • 將這三個功能都放在接口里面,需要用到報警功能的類就實現接口中的open( )和close( ),也許這個類根本就不具備open( )和close( )功能,比如火災報警器。

       從這里可以看出, Door的open() 、close()和alarm()根本就屬於兩個不同范疇內的行為,open()和close()屬於門本身固有的行為特性,而alarm()屬於延伸的附加行為。因此最好的解決辦法是單獨將報警設計為一個接口,包含alarm()行為,Door設計為單獨的一個抽象類,包含open和close兩種行為。再設計一個報警門繼承Door類和實現Alarm接口。

interface Alram { void alarm(); } abstract class Door { void open(); void close(); } class AlarmDoor extends Door implements Alarm { void oepn() { //....
 } void close() { //....
 } void alarm() { //....
 } }

5、問與答 

Q1:接口是否可繼承接口?

答:接口可以繼承接口。

 

Q2:抽象類是否可實現(implements)接口?

答:抽象類可以實現接口。

 

Q3:抽象類是否可繼承實體類(concrete class)?

答:抽象類可以繼承實體類。

 

Q4:抽象類中是否可以有靜態的main方法?

答:抽象類中可以有靜態的main方法。

 

Q5:抽象類與普通類的區別是?

答:二者的區別就是①不能創建實例對象,②允許有abstract方法。也可以這么理解——抽象類就是一個不能實例化的普通類,不過如果方法加了abstract,那么就必須在子類里面重寫。

 

Q6:抽象類為什么不能實例化對象?

答:現實生活中也有抽象類的例子,比如說人類是一個抽象類,我們無法創建一個稱作人類的對象,但是,人可以在繼承人類后來創建對象。況且抽象類中的抽象方法只有聲明,是未實現的方法,如果實例化了,又如何去實現方法調用呢?

 

Q7:abstract和final能否共用?

答:抽象類需要被繼承才能使用,而被final修飾的類無法被繼承,所以abstract和final是不能共存的。

 

Reference: http://www.cnblogs.com/dolphin0520/p/3811437.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM