Java中的abstract和interface
1、簡介
abstract
和interface
關(guān)鍵字在Java
中隨處可見,它是Java三大特性封裝、繼承、多態(tài)特性的實現(xiàn)重要支柱之一。interface
關(guān)鍵字用于定義接口抽象,其本質(zhì)上是用于定義類型、定義類所具有的能力。但是新手往往錯誤的使用了abstract和interface
,小捌其實也一樣犯錯誤,這篇文章我們盤一盤interface
接口和abstract
抽象類的使用。
文章開始前建議帶著兩個疑問閱讀:
abstract
和interface
有什么區(qū)別?abstract
和interface
應(yīng)該怎么選?
2、準則
定義接口的時候,有一些準則可以參考,根據(jù)這些準則可以更好的確定自己應(yīng)不應(yīng)該定義接口、或者是否有其他更好的代替方案。(注意小捌說的點不是絕對正確的,實際開發(fā)過程中要具體分析,有不對的可以互相交流。)
2.1 接口優(yōu)先于抽象類
小捌這里用JDK的源碼HashMap
的繼承體系來說明接口優(yōu)先于抽象類這一點。
HashMap繼承體系類圖結(jié)構(gòu):
HashMap的頂層接口:
public interface Map<K,V>{}
HashMap實現(xiàn)的抽象類:
public abstract class AbstractMap<K,V> implements Map<K,V> {}
可以看到HashMap
繼承了AbstractMap
抽象類實現(xiàn)了Map接口,但為什么說接口優(yōu)先于抽象類呢?這些因為Java是單繼承多實現(xiàn),HashMap
繼承了AbstractMap
抽象類之后就無法繼承其他類了,如果是接口就沒有這個限制,比如HashMap
還需要提供序列化和克隆的功能,HashMap
就可以實現(xiàn)三個接口Map<K,V>, Cloneable, Serializable
。
既然這樣為什么HashMap
還要去繼承AbstractMap
抽象類呢?
這是因為在JDK源碼設(shè)計中,Map結(jié)構(gòu)JDK需要提供部分方法的默認實現(xiàn),因此JDK的作者們單獨拉取了一個抽象類來實現(xiàn)這些方法;盡管Java8 Oracle
嘗試在接口中提供靜態(tài)方法和普通方法,但是小捌認為沒有到一定的需求程度,盡量、甚至完全不應(yīng)該將方法實現(xiàn)定義在接口中。
abstract和interface
有什么區(qū)別呢?
其實在Java8之后區(qū)別在不斷的縮小,但是總體上來說還是兩個完全不同的概念:
抽象類abstract的特點:
- 抽象方法和抽象類都必須被
abstract
關(guān)鍵字修飾 - 一個類中有抽象方法,那么這個類一定是抽象類
- 抽象類中不一定有抽象方法
- 抽象類中可以存在構(gòu)造方法
- 抽象類中可以存在普通屬性、方法、靜態(tài)屬性和靜態(tài)方法
- 抽象類的方法必須在子類中實現(xiàn),否則子類也需要定義為抽象類
- 抽象類不可以用
new
創(chuàng)建對象,因為調(diào)用抽象方法沒有實現(xiàn)就沒有意義
接口interface的特點:
- 接口中的方法,都被
public
來修飾 - 接口中沒有構(gòu)造方法,不能實例化接口對象
- 接口中只有常量,如果定義變量,則默認加上
public static final
- 使用接口可以實現(xiàn)多繼承
- 接口中只有方法的聲明,沒有方法體(適用于Java8之前,當我沒說,但是很多人都是這么認為的,這種錯誤的認為往往能正確的設(shè)計代碼)
- 接口中可以聲明靜態(tài)方法,必須是
public
修飾(默認),靜態(tài)方法無法被子類重寫 - 接口中可以聲明普通方法,必須是
default
修飾
比較項 | 抽象類(abstract) | 接口(interface) |
---|---|---|
多繼承 | 不支持(只能繼承一個抽象類) | 支持(類可以實現(xiàn)很多個接口) |
方法 | 抽象類則可以同時包含抽象和非抽象的方法 | 接口中所有的方法隱含都是抽象的(Java可以定義靜態(tài)方法) |
構(gòu)造器 | 允許 | 不允許 |
實例化 | 不能實例化 | 不能實例化 |
訪問修飾符 | 抽象類可以使用public、default;抽象方法可以使用public、default、protected;普通方法可以使用public、default、protected、private | 接口可以使用public、default;方法默認public; |
總結(jié):
- 在整個抽象實現(xiàn)體系中,必須提供一些方法的默認實現(xiàn),可以使用抽象類(因為非常不建議在接口中直接實現(xiàn)某些方法)
- 如果不需要提供默認實現(xiàn),且需要實現(xiàn)多繼承的功能就使用接口
2.2 接口中不應(yīng)該實現(xiàn)方法
接口無處不在,接口作為類體系結(jié)構(gòu)的最頂層,接口提供的一切約束和規(guī)范都是直接影響下層實現(xiàn)類。因此不建議在接口中實現(xiàn)具體的方法,盡管Java8
之后的接口定義可以提供靜態(tài)方法實現(xiàn)和普通方法實現(xiàn),但是這種實現(xiàn)方式有很大的風險,除非你的接口設(shè)計真的很完美,完美到能對所有的實現(xiàn)類都負責任的說你的邏輯永遠不會變。要不然接口的具體實現(xiàn)方法邏輯修改后,下面那些使用了該方法的類都得遭殃。
因此接口盡可能的只用來定義類型、定義類所具有的能力。如果一定要定義實現(xiàn),可以考慮使用抽象類來定義。
2.3 接口不應(yīng)該用于導(dǎo)出常量
由于接口中定義常量非常方便,因此有一些小伙伴會使用接口直接作為常量導(dǎo)出類,比如如下這種方式:
/** * <p> * 緩存key * </p> * * @Author: Liziba * @Date: 2021/11/2 23:12 */ public interface CacheKey { String USER = "user"; String ORDER = "order"; String MAIL = "mail"; }
它雖然看起來非常簡便、使用上也沒什么問題。但是問題就出在接口它不是用來給你導(dǎo)出常量的,如果需要定義常量我們可以使用枚舉或者常量類,
比如如下這種方法:
public class CacheKey { public static final String USER = "user"; public static final String ORDER = "order"; public static final String MAIL = "mail"; }
注意小捌這里說的是不要拿接口僅僅只作為常量導(dǎo)出類,而不是說不能在接口中定義常量,如果部分常量是類抽象類型中統(tǒng)一使用的可以考慮這樣設(shè)計(但是也不推薦啦?。?,單獨抽出常量類來管理這些常量往往要更好一些的。
到此這篇關(guān)于Java
中的abstract
和interface
的文章就介紹到這了,更多相關(guān)Java
中的abstract
和interface
內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決Spring boot整合mybatis,xml資源文件放置及路徑配置問題
這篇文章主要介紹了解決Spring boot整合mybatis,xml資源文件放置及路徑配置問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12解決Eclipse add external jars運行出現(xiàn)java.lang.NoClassDefFoundErro
本篇文章對Eclipse add external jars導(dǎo)致運行出現(xiàn)java.lang.NoClassDefFoundError的解決方法進行了詳細的分析介紹。需要的朋友參考下2013-05-05淺析java中 Spring MVC 攔截器作用及其實現(xiàn)
本篇文章主要介紹了java中SpringMVC 攔截器的使用及其實例,需要的朋友可以參考2017-04-04java 判斷一個數(shù)是否為2的整數(shù)次冪方法
今天小編就為大家分享一篇java 判斷一個數(shù)是否為2的整數(shù)次冪方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-07-07