接口(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