「為什麼要用抽象類?介面不就夠了嗎?」這是無數 Java 開發者的靈魂拷問。本文通過一個「USB 設備驅動」的反面教材引入,深度剖析抽象類與介面在編譯器強制約束能力、代碼復用機制、演進擴展性三個維度的本質差異,並給出架構級選型決策指南。
// 父類:具體實現
public class USB {
public void QiDong() { }
public void TingZhi() { }
}
// 子類:意圖重寫
public class UPan extends USB {
public void Qidong() { // 注意:Q 小寫!
System.out.println("U盤啟動了");
}
public void TingZhi() {
System.out.println("U盤停止運行了");
}
}
UPan 實例調用 QiDong() 執行的是父類空方法。public abstract class USB {
public String price; // 成員變量
// 抽象方法:無方法體,強制子類實現
public abstract void QiDong();
public abstract void TingZhi();
// 非抽象方法:提供預設行為,子類可選擇重寫
public void showInfo() {
System.out.println("價格: " + price);
}
}
abstract。abstract。// 命名建議:形容詞或能力描述
public interface ICalculable {
// 隱含 public static final
int DEFAULT_PRECISION = 2;
// 隱含 public abstract
int calculate(int a, int b);
}
// implements 關鍵字,支持多實現
public class Adder implements ICalculable {
@Override
public int calculate(int a, int b) {
return a + b;
}
}
// 若方法名寫錯為 calculatee,編譯器直接報錯:「未實現介面方法」
Runnable、Comparable、Serializable)。Cloneable)或動詞名詞(DataExporter),表達「擁有何種能力」。| 決策維度 | 選擇抽象類 | 選擇介面 |
|---|---|---|
| 關係語義 | 「是一種」—— 核心領域模型層級繼承(如 Animal → Cat) |
「像一種」/「能幹什麼」—— 橫切能力/行為契約(如 Serializable、Comparable) |
| 代碼復用需求 | 需要共享非公共字段、構造方法、私有方法、預設實現 | 僅定義公共契約,無實現共享需求(Java 8+ default 方法輕量共享) |
| 繼承靈活性 | 單繼承,子類鎖定父類層級 | 多實現,類可組合多種能力,組合優於繼承 |
| 演進維護成本 | 新增非抽象方法不破壞現有子類 | Java 8 前極高;Java 8+ 用 default 緩解,但仍需謹慎 |
| 構造方法 | 可定義構造方法,強制子類傳參初始化 | 無構造方法 |
| 典型應用場景 | 模板方法、領域基類、框架骨架類(如 AbstractController) |
API 定義、SPI 擴展點、標記介面、函數式介面 |
作為約束體系的補充,final 從三個維度鎖定變化:
String, Integer),防止惡意繼承破壞不可變性/安全性。// 1. 介面定義核心契約(對外 API,支持多實現)
public interface IUsbDevice {
void connect();
void disconnect();
String getDeviceId();
}
// 2. 抽象類封裝通用骨架(內部復用,模板方法)
public abstract class AbstractUsbDevice implements IUsbDevice {
protected final String deviceId; // final 保證不可變
protected AbstractUsbDevice(String deviceId) {
this.deviceId = deviceId;
}
// 模板方法:final 鎖定流程,防止子類破壞
public final void connect() {
handshake();
doConnect(); // 抽象方法,強制子類實現核心差異
registerDevice();
}
protected abstract void doConnect(); // 核心差異點
private void handshake() { /* 通用握手邏輯 */ }
private void registerDevice() { /* 通用註冊邏輯 */ }
@Override public final String getDeviceId() { return deviceId; } // final 鎖定 getter
@Override public void disconnect() { /* 通用斷開邏輯 */ }
}
// 3. 具體實現:只關注差異化邏輯
public class Keyboard extends AbstractUsbDevice {
public Keyboard() { super("KB_" + UUID.randomUUID().toString().substring(0, 8)); }
@Override protected void doConnect() { /* 鍵盤專屬驅動加載 */ }
}
public class Printer extends AbstractUsbDevice {
public Printer() { super("PT_" + UUID.randomUUID().toString().substring(0, 8)); }
@Override protected void doConnect() { /* 打印機專屬佇列初始化 */ }
}
掌握這三者的邊界與協奏,是從「寫出功能代碼」邁向「設計健壯可演進架構」的關鍵認知躍遷。