Java中接口和抽象類的異同以及具體的使用場景
在 Java 中,接口(Interface) 和 抽象類(Abstract Class) 是實(shí)現(xiàn)抽象編程的核心機(jī)制,二者都用于定義 “規(guī)范” 而非完整實(shí)現(xiàn),但在設(shè)計理念、語法規(guī)則和使用場景上存在顯著差異。下面從 異同點(diǎn)、核心區(qū)別對比、具體使用場景 三方面詳細(xì)解析。
一、接口和抽象類的 “相同點(diǎn)”
二者的核心目標(biāo)都是 抽象封裝共性行為 / 特征,避免代碼重復(fù),同時約束子類實(shí)現(xiàn),具體相同點(diǎn)如下:
- 都不能實(shí)例化:無法通過
new關(guān)鍵字創(chuàng)建對象(抽象類本質(zhì)是 “不完整的類”,接口是 “純規(guī)范”,均缺少完整實(shí)現(xiàn))。 - 都可包含抽象方法:抽象方法是 “只有聲明、沒有實(shí)現(xiàn)” 的方法(接口中默認(rèn)抽象,抽象類需顯式加
abstract關(guān)鍵字),子類必須實(shí)現(xiàn)所有抽象方法(否則子類仍為抽象類)。 - 都用于被繼承 / 實(shí)現(xiàn):抽象類通過
extends被繼承,接口通過implements被實(shí)現(xiàn),子類 / 實(shí)現(xiàn)類需遵循其定義的規(guī)范。 - 都支持多態(tài):可以聲明抽象類 / 接口類型的引用,指向其具體子類 / 實(shí)現(xiàn)類的對象(核心體現(xiàn) “面向接口編程” 思想)。
示例(多態(tài)共性):
// 抽象類多態(tài)
abstract class Animal {}
class Dog extends Animal {}
Animal animal = new Dog(); // 合法
// 接口多態(tài)
interface Flyable {}
class Bird implements Flyable {}
Flyable flyable = new Bird(); // 合法二、接口和抽象類的 “核心區(qū)別”
這是重點(diǎn),需從 語法規(guī)則 和 設(shè)計理念 兩方面區(qū)分,下表是全面對比:
| 對比維度 | 抽象類(Abstract Class) | 接口(Interface) |
|---|---|---|
| 繼承 / 實(shí)現(xiàn)方式 | 子類通過 extends 單繼承(Java 不支持多繼承) | 類通過 implements 多實(shí)現(xiàn)(可同時實(shí)現(xiàn)多個接口) |
| 構(gòu)造方法 | 可以有構(gòu)造方法(用于子類初始化時調(diào)用 super()) | 不能有構(gòu)造方法(接口無 “實(shí)例狀態(tài)”,僅定義規(guī)范) |
| 成員變量 | 可包含任意成員變量(public/private/protected、靜態(tài) / 非靜態(tài)) | 只能是 public static final 常量(默認(rèn)隱式修飾,必須初始化) |
| 成員方法 | 1. 抽象方法( 2. 普通非抽象方法(有方法體);3. JDK8+ 支持默認(rèn)方法( | 1. JDK7-:只能是抽象方法(默認(rèn) 2. JDK8+:支持默認(rèn)方法( 3. JDK9+:支持私有方法( |
| 訪問權(quán)限 | 成員可聲明任意權(quán)限(private/protected/public) | 所有成員(方法、常量)默認(rèn) public(顯式聲明也只能是 public) |
| 設(shè)計理念 | 體現(xiàn) “is-a” 關(guān)系(子類是抽象類的 “一種具體實(shí)現(xiàn)”,包含繼承的屬性和行為) | 體現(xiàn) “has-a” 關(guān)系(類 “具備” 接口定義的功能,是對行為的補(bǔ)充擴(kuò)展) |
| 核心作用 | 封裝子類的共性屬性和行為(既有抽象規(guī)范,也有具體實(shí)現(xiàn)復(fù)用) | 定義純行為規(guī)范(不關(guān)心類的本質(zhì),只約束必須實(shí)現(xiàn)的功能) |
| 靈活性 | 單繼承限制,靈活性低 | 多實(shí)現(xiàn) + 接口繼承(extends 多個接口),靈活性高 |
關(guān)鍵區(qū)別詳解(避免踩坑)
繼承限制:抽象類只能單繼承(子類 extends 一個抽象類),接口支持多實(shí)現(xiàn)(類 implements A, B)+ 接口多繼承(接口 extends A, B)。示例(接口多繼承):
interface Runable { void run(); }
interface Flyable { void fly(); }
// 接口可繼承多個接口,合并規(guī)范
interface SuperAbility extends Runable, Flyable {}
// 類可實(shí)現(xiàn)多個接口,實(shí)現(xiàn)所有抽象方法
class Superman implements SuperAbility {
@Override
public void run() {}
@Override
public void fly() {}
}成員變量差異:
- 抽象類的變量可修改(非
final):abstract class Person { protected String name; // 可被子類修改 public static int age = 18; // 靜態(tài)變量可直接訪問 } - 接口的變量默認(rèn)是
public static final(必須初始化,不可修改):interface Constants { String NAME = "Java"; // 等價于 public static final String NAME = "Java"; // int NUM; // 編譯報錯:必須初始化 }
方法實(shí)現(xiàn)差異:
抽象類的普通方法可直接被子類復(fù)用:
abstract class Vehicle {
// 具體方法(子類可直接使用,無需重寫)
public void refuel() {
System.out.println("加油...");
}
// 抽象方法(子類必須實(shí)現(xiàn))
public abstract void drive();
}
class Car extends Vehicle {
@Override
public void drive() {
System.out.println("開車...");
}
}
Car car = new Car();
car.refuel(); // 直接復(fù)用父類方法,輸出“加油...”接口的默認(rèn)方法(JDK8+)是為了 “接口升級不破壞原有實(shí)現(xiàn)類”,實(shí)現(xiàn)類可重寫:
interface Greet {
default void sayHello() {
System.out.println("Hello");
}
}
class Chinese implements Greet {
// 可選重寫默認(rèn)方法
@Override
public void sayHello() {
System.out.println("你好");
}
}三、具體使用場景(核心:選對設(shè)計方向)
選擇接口還是抽象類,核心看 你要表達(dá)的是 “繼承關(guān)系” 還是 “功能擴(kuò)展”,以及是否需要復(fù)用具體實(shí)現(xiàn)。
1. 優(yōu)先使用抽象類的場景
當(dāng)你需要定義一個 “類的模板”,子類和父類是 “is-a” 關(guān)系,且需要復(fù)用 屬性或具體方法 時,用抽象類。典型場景:
共性屬性 + 行為復(fù)用:例如 Animal 抽象類(子類 Dog/Cat 是 “一種動物”),包含 name 屬性和 eat() 具體方法(所有動物都要吃飯,實(shí)現(xiàn)相同),同時定義 makeSound() 抽象方法(不同動物叫聲不同,子類實(shí)現(xiàn))。
abstract class Animal {
protected String name;
// 具體方法:復(fù)用實(shí)現(xiàn)
public void eat() {
System.out.println(name + "在吃飯");
}
// 抽象方法:約束子類實(shí)現(xiàn)
public abstract void makeSound();
}
class Dog extends Animal {
public Dog(String name) {
this.name = name;
}
@Override
public void makeSound() {
System.out.println("汪汪叫");
}
}
class Cat extends Animal {
public Cat(String name) {
this.name = name;
}
@Override
public void makeSound() {
System.out.println("喵喵叫");
}
}- 需要構(gòu)造方法初始化:抽象類的構(gòu)造方法可用于子類初始化(例如通過
super(name)給父類屬性賦值),接口無構(gòu)造方法,無法實(shí)現(xiàn)。 - 限制子類數(shù)量:單繼承特性可避免子類過度擴(kuò)展(例如
HttpServlet抽象類,子類只需重寫doGet()/doPost(),無需關(guān)注其他 Servlet 生命周期方法)。
2. 優(yōu)先使用接口的場景
當(dāng)你需要定義 “功能規(guī)范”,類和接口是 “has-a” 關(guān)系,且需要 多實(shí)現(xiàn)擴(kuò)展 或 跨類層次復(fù)用行為 時,用接口。典型場景:
定義純行為規(guī)范(不關(guān)心類的本質(zhì)):例如 Runnable 接口(只要求類實(shí)現(xiàn) run() 方法,不管是 Thread、Task 還是其他類)、Comparable 接口(只要求類實(shí)現(xiàn)比較邏輯)。
// 接口定義“可比較”規(guī)范
interface Comparable<T> {
int compareTo(T o);
}
// 任意類都可實(shí)現(xiàn)該接口,獲得比較能力(跨類層次復(fù)用)
class Student implements Comparable<Student> {
private int score;
@Override
public int compareTo(Student o) {
return this.score - o.score;
}
}
class Product implements Comparable<Product> {
private double price;
@Override
public int compareTo(Product o) {
return Double.compare(this.price, o.price);
}
}- 多功能擴(kuò)展:一個類需要具備多種無關(guān)功能時,通過多實(shí)現(xiàn)接口實(shí)現(xiàn)(例如
Superman類同時實(shí)現(xiàn)Runable、Flyable、Swimable接口,獲得跑、飛、游泳三種能力)。 - 接口回調(diào) / 解耦:例如 Spring 框架的
ApplicationContextAware接口(實(shí)現(xiàn)類可獲取ApplicationContext對象),框架通過接口規(guī)范調(diào)用方法,無需關(guān)心實(shí)現(xiàn)類的具體類型,降低耦合。 - 接口升級(默認(rèn)方法):JDK8 后,接口可通過
default方法添加新功能,而不破壞原有實(shí)現(xiàn)類(例如Collection接口新增stream()default 方法,所有集合實(shí)現(xiàn)類無需修改即可使用)。
3. 抽象類和接口結(jié)合使用(最佳實(shí)踐)
實(shí)際開發(fā)中,常結(jié)合二者的優(yōu)勢:抽象類提供基礎(chǔ)實(shí)現(xiàn),接口定義擴(kuò)展功能。典型示例:Java 集合框架中的 AbstractCollection(抽象類)和 Collection(接口)。
Collection接口:定義所有集合的核心規(guī)范(add()、size()、iterator()等抽象方法),不關(guān)心實(shí)現(xiàn)。AbstractCollection抽象類:實(shí)現(xiàn)Collection接口的部分方法(例如isEmpty()、contains()),復(fù)用基礎(chǔ)邏輯,子類(如ArrayList、LinkedList)只需重寫核心方法(add()、iterator())即可。
代碼簡化示例:
// 接口:定義規(guī)范
interface Collection {
boolean add(Object obj);
int size();
boolean isEmpty();
}
// 抽象類:實(shí)現(xiàn)通用方法,減少子類重復(fù)代碼
abstract class AbstractCollection implements Collection {
private int size = 0;
@Override public boolean add(Object obj) {
// 子類需實(shí)現(xiàn)具體添加邏輯,但size遞增可復(fù)用
doAdd(obj);
size++;
return true;
}
@Override public int size() { return size; }
@Override public boolean isEmpty() { return size == 0; }
// 抽象方法:子類必須實(shí)現(xiàn)具體添加邏輯
protected abstract void doAdd(Object obj);
}
// 具體子類:只需關(guān)注核心實(shí)現(xiàn)
class MyList extends AbstractCollection {
private Object[] elements = new Object[10];
@Override protected void doAdd(Object obj) {
// 實(shí)現(xiàn)數(shù)組添加邏輯
for (int i = 0; i < elements.length; i++) {
if (elements[i] == null) {
elements[i] = obj;
break;
}
}
}
}四、總結(jié)(核心選型口訣)
- 若子類和父類是 “is-a” 關(guān)系,需復(fù)用屬性或具體方法 → 用 抽象類;
- 若類需要具備多種無關(guān)功能,或定義純行為規(guī)范 → 用 接口;
- 若需既保證基礎(chǔ)實(shí)現(xiàn)復(fù)用,又支持靈活擴(kuò)展 → 抽象類 + 接口 結(jié)合使用。
本質(zhì)區(qū)別:抽象類是 “類的模板”,側(cè)重繼承和復(fù)用;接口是 “行為的契約”,側(cè)重規(guī)范和擴(kuò)展。遵循 “面向接口編程” 的設(shè)計思想,優(yōu)先使用接口(解耦、靈活),僅在需要復(fù)用具體實(shí)現(xiàn)時才用抽象類。
到此這篇關(guān)于Java中接口和抽象類的異同以及具體的使用場景的文章就介紹到這了,更多相關(guān)java接口和抽象類使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java獲取Object中Value的實(shí)現(xiàn)方法
本文介紹了在Java中獲取對象屬性值的幾種常見方法,包括使用反射機(jī)制、getter方法、接口或抽象類、Map數(shù)據(jù)結(jié)構(gòu)、序列化與反序列化以及JavaBeans規(guī)范,每種方法都有其適用場景和優(yōu)缺點(diǎn),選擇合適的方法取決于具體需求2025-03-03
Spring boot中filter類不能注入@Autowired變量問題
這篇文章主要介紹了Spring boot中filter類不能注入@Autowired變量問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
Spring?Boot虛擬線程Webflux在JWT驗(yàn)證和MySQL查詢性能比較
這篇文章主要為大家介紹了Spring Boot虛擬線程與Webflux在JWT驗(yàn)證和MySQL查詢上的性能比較,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09
Java的MyBatis框架中Mapper映射配置的使用及原理解析
Mapper用于映射SQL語句,可以說是MyBatis操作數(shù)據(jù)庫的核心特性之一,這里我們來討論Java的MyBatis框架中Mapper映射配置的使用及原理解析,包括對mapper的xml配置文件的讀取流程解讀.2016-06-06

