Java中抽象類和介面的介紹及二者間的區別

早起的小蟲子發表於2020-08-25

  介面(Interface)和抽象類(Abstract Class)是支援抽象類定義的兩種機制。

一、抽象類

  在Java中被abstract關鍵字修飾的類稱為抽象類,被abstract關鍵字修飾的方法稱為抽象方法,抽象方法只有方法的宣告,沒有方法體。抽象類是用來捕捉子類的通用特性的 。它不能被例項化,只能被用作子類的超類。抽象類是被用來建立繼承層級裡子類的模板。

  以JDK中的GenericServlet為例:

public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
    // abstract method
    abstract void service(ServletRequest req, ServletResponse res);
 
    void init() {
        // Its implementation
    }
    // other method related to Servlet
}

  當HttpServlet類繼承GenericServlet時,它提供了service方法的實現: 

public class HttpServlet extends GenericServlet {
    void service(ServletRequest req, ServletResponse res) {
        // implementation
    }
 
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        // Implementation
    }
 
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
        // Implementation
    }
 
    // some other methods related to HttpServlet
}

 抽象類的特點:

  1、抽象類不能被例項化,即不能使用new關鍵字來例項化物件,只能被繼承;

  2、包含抽象方法的一定是抽象類,但是抽象類不一定含有抽象方法;

  3、抽象類中的抽象方法的修飾符只能為public或者protected,預設為public;

  4、抽象類中的抽象方法只有方法體,沒有具體實現;

  5、如果一個子類實現了父類(抽象類)的所有抽象方法,那麼該子類可以不必是抽象類,否則就是抽象類;

  6、抽象類可以包含屬性、方法、構造方法,但是構造方法不能用於例項化,主要用途是被子類呼叫。

抽象類和普通類的主要有三點區別:

  1、抽象方法必須為public或者protected(因為如果為private,則不能被子類繼承,子類便無法實現該方法),預設情況下預設為public。

  2、抽象類不能用來建立物件;

  3、如果一個類繼承於一個抽象類,則子類必須實現父類的抽象方法。如果子類沒有實現父類的抽象方法,則必須將子類也定義為為abstract類。

在其他方面,抽象類和普通的類並沒有區別。

二、介面

  Java中介面使用interface關鍵字修飾。介面是抽象方法的集合。如果一個類實現了某個介面,那麼它就繼承了這個介面的抽象方法。這就像契約模式,如果實現了這個介面,那麼就必須確保使用這些方法。介面只是一種形式,介面自身不能做任何事情。在Java中,定一個介面的形式如下:

[public] interface InterfaceName {
 
}

 

  介面中可以含有 變數和方法。但是要注意,介面中的變數會被隱式地指定為public static final變數(並且只能是public static final變數,用private修飾會報編譯錯誤),而方法會被隱式地指定為public abstract方法且只能是public abstract方法(用其他關鍵字,比如private、protected、static、 final等修飾會報編譯錯誤),並且介面中所有的方法不能有具體的實現,也就是說,介面中的方法必須都是抽象方法。從這裡可以隱約看出介面和抽象類的區別,介面是一種極度抽象的型別,它比抽象類更加“抽象”,並且一般情況下不在介面中定義變數。

  要讓一個類遵循某組特地的介面需要使用implements關鍵字,具體格式如下:

class ClassName implements Interface1,Interface2,[....]{
}

 

  可以看出,允許一個類遵循多個特定的介面。如果一個非抽象類遵循了某個介面,就必須實現該介面中的所有方法。對於遵循某個介面的抽象類,可以不實現該介面中的抽象方法。

介面的特點為:

  1、介面可以包含變數、方法;變數被隱士指定為public static final,方法被隱士指定為public abstract(JDK1.8之前);

  2、介面支援多繼承,即一個介面可以extends多個介面,間接的解決了Java中類的單繼承問題;

  3、一個類可以實現多個介面;

  4、JDK1.8中對介面增加了新的特性:

    4.1、預設方法(default method):JDK 1.8允許給介面新增非抽象的方法實現,但必須使用default關鍵字修飾;定義了default的方法可以不被實現子類所實現,但只能被實現子類的物件呼叫;如果子類實現了多個介面,並且這些介面包含一樣的預設方法,則子類必須重寫預設方法;

    4.2、靜態方法(static method):JDK 1.8中允許使用static關鍵字修飾一個方法,並提供實現,稱為介面靜態方法。介面靜態方法只能通過介面呼叫(介面名.靜態方法名)。

三、抽象類和介面的相同點

  1、都不能被例項化
  2、介面的實現類或抽象類的子類都只有實現了介面或抽象類中的方法後才能例項化。 

四、抽象類和介面的區別

  1、語法層面上的區別:

 

 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( )兩個動作,此時我們可以定義通過抽象類和介面來定義這個抽象概念:

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

 

  或者:

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

 

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

  1)將這三個功能都放在抽象類裡面,但是這樣一來所有繼承於這個抽象類的子類都具備了報警功能,但是有的門並不一定具備報警功能;

  2)將這三個功能都放在介面裡面,需要用到報警功能的類就需要實現這個介面中的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() {
      //....
    }
}

 

五、抽象類和介面的應用場景

  1、如果你擁有一些方法並且想讓它們中的一些有預設實現,那麼使用抽象類吧。

  2、如果你想實現多重繼承,那麼你必須使用介面。由於Java不支援多繼承,子類不能夠繼承多個類,但可以實現多個介面。因此你就可以使用介面來解決它。

  3、如果基本功能在不斷改變,那麼就需要使用抽象類。如果不斷改變基本功能並且使用介面,那麼就需要改變所有實現了該介面的類。

  

參考:

https://blog.csdn.net/zhangquan2015/article/details/82808399

https://www.cnblogs.com/dolphin0520/p/3811437.html

https://www.cnblogs.com/songhuiqiang/p/10647835.html

https://www.baidu.com/link?url=hcXWrXyWN471vVxKNqZG6p0J4cWAkdYgGjkVr3wFxi-m15QxDjyHvOvTb2VJPAhlzHm7mrYAgiPCrdq_ZwA50NPjQ5uWoEG4jIVnAe_Df6a&wd=&eqid=a4a0d3090003afeb000000035f432c11

 

相關文章