Java中的abstract和interface

1、簡(jiǎn)介
abstract和interface關(guān)鍵字在Java中隨處可見(jiàn),它是Java三大特性封裝、繼承、多態(tài)特性的實(shí)現(xiàn)重要支柱之一。interface關(guān)鍵字用于定義接口抽象,其本質(zhì)上是用于定義類(lèi)型、定義類(lèi)所具有的能力。但是新手往往錯(cuò)誤的使用了abstract和interface,小捌其實(shí)也一樣犯錯(cuò)誤,這篇文章我們盤(pán)一盤(pán)interface接口和abstract抽象類(lèi)的使用。
文章開(kāi)始前建議帶著兩個(gè)疑問(wèn)閱讀:
abstract和interface有什么區(qū)別?abstract和interface應(yīng)該怎么選?
2、準(zhǔn)則
定義接口的時(shí)候,有一些準(zhǔn)則可以參考,根據(jù)這些準(zhǔn)則可以更好的確定自己應(yīng)不應(yīng)該定義接口、或者是否有其他更好的代替方案。(注意小捌說(shuō)的點(diǎn)不是絕對(duì)正確的,實(shí)際開(kāi)發(fā)過(guò)程中要具體分析,有不對(duì)的可以互相交流。)
2.1 接口優(yōu)先于抽象類(lèi)
小捌這里用JDK的源碼HashMap的繼承體系來(lái)說(shuō)明接口優(yōu)先于抽象類(lèi)這一點(diǎn)。
HashMap繼承體系類(lèi)圖結(jié)構(gòu):

HashMap的頂層接口:
public interface Map<K,V>{}
HashMap實(shí)現(xiàn)的抽象類(lèi):
public abstract class AbstractMap<K,V> implements Map<K,V> {}
可以看到HashMap繼承了AbstractMap抽象類(lèi)實(shí)現(xiàn)了Map接口,但為什么說(shuō)接口優(yōu)先于抽象類(lèi)呢?這些因?yàn)镴ava是單繼承多實(shí)現(xiàn),HashMap繼承了AbstractMap抽象類(lèi)之后就無(wú)法繼承其他類(lèi)了,如果是接口就沒(méi)有這個(gè)限制,比如HashMap還需要提供序列化和克隆的功能,HashMap就可以實(shí)現(xiàn)三個(gè)接口Map<K,V>, Cloneable, Serializable。
既然這樣為什么HashMap還要去繼承AbstractMap抽象類(lèi)呢?
這是因?yàn)樵贘DK源碼設(shè)計(jì)中,Map結(jié)構(gòu)JDK需要提供部分方法的默認(rèn)實(shí)現(xiàn),因此JDK的作者們單獨(dú)拉取了一個(gè)抽象類(lèi)來(lái)實(shí)現(xiàn)這些方法;盡管Java8 Oracle嘗試在接口中提供靜態(tài)方法和普通方法,但是小捌認(rèn)為沒(méi)有到一定的需求程度,盡量、甚至完全不應(yīng)該將方法實(shí)現(xiàn)定義在接口中。
abstract和interface有什么區(qū)別呢?
其實(shí)在Java8之后區(qū)別在不斷的縮小,但是總體上來(lái)說(shuō)還是兩個(gè)完全不同的概念:
抽象類(lèi)abstract的特點(diǎn):
- 抽象方法和抽象類(lèi)都必須被
abstract關(guān)鍵字修飾 - 一個(gè)類(lèi)中有抽象方法,那么這個(gè)類(lèi)一定是抽象類(lèi)
- 抽象類(lèi)中不一定有抽象方法
- 抽象類(lèi)中可以存在構(gòu)造方法
- 抽象類(lèi)中可以存在普通屬性、方法、靜態(tài)屬性和靜態(tài)方法
- 抽象類(lèi)的方法必須在子類(lèi)中實(shí)現(xiàn),否則子類(lèi)也需要定義為抽象類(lèi)
- 抽象類(lèi)不可以用
new創(chuàng)建對(duì)象,因?yàn)檎{(diào)用抽象方法沒(méi)有實(shí)現(xiàn)就沒(méi)有意義
接口interface的特點(diǎn):
- 接口中的方法,都被
public來(lái)修飾 - 接口中沒(méi)有構(gòu)造方法,不能實(shí)例化接口對(duì)象
- 接口中只有常量,如果定義變量,則默認(rèn)加上
public static final - 使用接口可以實(shí)現(xiàn)多繼承
- 接口中只有方法的聲明,沒(méi)有方法體(適用于Java8之前,當(dāng)我沒(méi)說(shuō),但是很多人都是這么認(rèn)為的,這種錯(cuò)誤的認(rèn)為往往能正確的設(shè)計(jì)代碼)
- 接口中可以聲明靜態(tài)方法,必須是
public修飾(默認(rèn)),靜態(tài)方法無(wú)法被子類(lèi)重寫(xiě) - 接口中可以聲明普通方法,必須是
default修飾
| 比較項(xiàng) | 抽象類(lèi)(abstract) | 接口(interface) |
|---|---|---|
| 多繼承 | 不支持(只能繼承一個(gè)抽象類(lèi)) | 支持(類(lèi)可以實(shí)現(xiàn)很多個(gè)接口) |
| 方法 | 抽象類(lèi)則可以同時(shí)包含抽象和非抽象的方法 | 接口中所有的方法隱含都是抽象的(Java可以定義靜態(tài)方法) |
| 構(gòu)造器 | 允許 | 不允許 |
| 實(shí)例化 | 不能實(shí)例化 | 不能實(shí)例化 |
| 訪(fǎng)問(wèn)修飾符 | 抽象類(lèi)可以使用public、default;抽象方法可以使用public、default、protected;普通方法可以使用public、default、protected、private | 接口可以使用public、default;方法默認(rèn)public; |
總結(jié):
- 在整個(gè)抽象實(shí)現(xiàn)體系中,必須提供一些方法的默認(rèn)實(shí)現(xiàn),可以使用抽象類(lèi)(因?yàn)榉浅2唤ㄗh在接口中直接實(shí)現(xiàn)某些方法)
- 如果不需要提供默認(rèn)實(shí)現(xiàn),且需要實(shí)現(xiàn)多繼承的功能就使用接口
2.2 接口中不應(yīng)該實(shí)現(xiàn)方法
接口無(wú)處不在,接口作為類(lèi)體系結(jié)構(gòu)的最頂層,接口提供的一切約束和規(guī)范都是直接影響下層實(shí)現(xiàn)類(lèi)。因此不建議在接口中實(shí)現(xiàn)具體的方法,盡管Java8之后的接口定義可以提供靜態(tài)方法實(shí)現(xiàn)和普通方法實(shí)現(xiàn),但是這種實(shí)現(xiàn)方式有很大的風(fēng)險(xiǎn),除非你的接口設(shè)計(jì)真的很完美,完美到能對(duì)所有的實(shí)現(xiàn)類(lèi)都負(fù)責(zé)任的說(shuō)你的邏輯永遠(yuǎn)不會(huì)變。要不然接口的具體實(shí)現(xiàn)方法邏輯修改后,下面那些使用了該方法的類(lèi)都得遭殃。
因此接口盡可能的只用來(lái)定義類(lèi)型、定義類(lèi)所具有的能力。如果一定要定義實(shí)現(xiàn),可以考慮使用抽象類(lèi)來(lái)定義。
2.3 接口不應(yīng)該用于導(dǎo)出常量
由于接口中定義常量非常方便,因此有一些小伙伴會(huì)使用接口直接作為常量導(dǎo)出類(lèi),比如如下這種方式:
/**
* <p>
* 緩存key
* </p>
*
* @Author: Liziba
* @Date: 2021/11/2 23:12
*/
public interface CacheKey {
String USER = "user";
String ORDER = "order";
String MAIL = "mail";
}
它雖然看起來(lái)非常簡(jiǎn)便、使用上也沒(méi)什么問(wèn)題。但是問(wèn)題就出在接口它不是用來(lái)給你導(dǎo)出常量的,如果需要定義常量我們可以使用枚舉或者常量類(lèi),
比如如下這種方法:
public class CacheKey {
public static final String USER = "user";
public static final String ORDER = "order";
public static final String MAIL = "mail";
}
注意小捌這里說(shuō)的是不要拿接口僅僅只作為常量導(dǎo)出類(lèi),而不是說(shuō)不能在接口中定義常量,如果部分常量是類(lèi)抽象類(lèi)型中統(tǒng)一使用的可以考慮這樣設(shè)計(jì)(但是也不推薦啦!),單獨(dú)抽出常量類(lèi)來(lái)管理這些常量往往要更好一些的。
到此這篇關(guān)于Java中的abstract和interface的文章就介紹到這了,更多相關(guān)Java中的abstract和interface內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mybatis整合springboot報(bào)BindingException:Invalid?bound?stateme
這篇文章主要給大家介紹了關(guān)于mybatis整合springboot報(bào)BindingException:Invalid?bound?statement?(not?found)異常的解決辦法,這個(gè)錯(cuò)誤通常是由于Mapper文件中的statement?id與Java代碼中的方法名不一致導(dǎo)致的,需要的朋友可以參考下2024-01-01
java 數(shù)據(jù)的加密與解密普遍實(shí)例代碼
本篇文章介紹了一個(gè)關(guān)于密鑰查詢(xún)的jsp文件簡(jiǎn)單實(shí)例代碼,需要的朋友可以參考下2017-04-04
基于SpringBoot實(shí)現(xiàn)HTTP請(qǐng)求簽名驗(yàn)證機(jī)制
在分布式系統(tǒng)交互中,API接口的安全性至關(guān)重要,本文將深入解析基于Spring Boot實(shí)現(xiàn)的HTTP請(qǐng)求簽名驗(yàn)證機(jī)制,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-04-04
Java?SE循環(huán)一些基本練習(xí)題總結(jié)
循環(huán)語(yǔ)句可以在滿(mǎn)足循環(huán)條件的情況下,反復(fù)執(zhí)行某一段代碼,這段被重復(fù)執(zhí)行的代碼被稱(chēng)為循環(huán)體語(yǔ)句,下面這篇文章主要給大家總結(jié)介紹了關(guān)于Java?SE循環(huán)一些基本練習(xí)題,需要的朋友可以參考下2024-03-03
Spring中@Transactional注解的屬性說(shuō)明
這篇文章主要介紹了Spring中@Transactional注解的屬性說(shuō)明,@Transactional 是聲明式事務(wù)管理 編程中使用的注解,@Transactional 注解應(yīng)該只被應(yīng)用到 public 方法上,這是由 Spring AOP 的本質(zhì)決定的,需要的朋友可以參考下2023-11-11
Java覆蓋第三方j(luò)ar包中的某一個(gè)類(lèi)的實(shí)現(xiàn)方法
在我們?nèi)粘5拈_(kāi)發(fā)中,經(jīng)常需要使用第三方的 jar 包,有時(shí)候我們會(huì)發(fā)現(xiàn)第三方的 jar 包中的某一個(gè)類(lèi)有問(wèn)題,或者我們需要定制化修改其中的邏輯,那么應(yīng)該如何實(shí)現(xiàn)呢,本文給大家介紹了Java覆蓋第三方j(luò)ar包中的某一個(gè)類(lèi)的實(shí)現(xiàn)方法,需要的朋友可以參考下2025-02-02
Java游戲開(kāi)發(fā)拼圖游戲經(jīng)典版
這篇文章主要介紹了Java游戲開(kāi)發(fā)拼圖游戲經(jīng)典版,對(duì)這方面感興趣的同學(xué)可以跟著教程試下2021-01-01

