JDK 5 引入 enum 關鍵字,提供類型安全的枚舉實現。本文從字節碼層面揭示本質,詳細講解核心 API、實現介面、Switch 使用、單例模式、策略模式等進階場景,助你用好這個「特殊的類」。
// 源碼
public enum Color { RED, BLUE, BLACK, YELLOW, GREEN }
// 編譯器生成 (概念等價)
public final class Color extends Enum {
public static final Color RED = new Color("RED", 0);
public static final Color BLUE = new Color("BLUE", 1);
// ...
private static final Color[] VALUES = {RED, BLUE, ...};
private Color(String name, int ordinal) { super(name, ordinal); }
public static Color[] values() { return VALUES.clone(); }
public static Color valueOf(String name) { ... }
}
關鍵點:
enum 隱式 extends Enum,故 不可繼承其他類 (Java 單繼承)public static final 常量,全局單例values()、valueOf()、ordinal()、compareTo()| 方法 | 說明 | 範例 |
|---|---|---|
ordinal() | 聲明順序 (從 0 開始) | Color.RED.ordinal() → 0 |
compareTo(E o) | 比較聲明順序差值 | RED.compareTo(BLUE) → -1 |
values() | 返回所有枚舉值數組 (克隆) | for (Color c : Color.values()) |
valueOf(String) | 按名稱獲取枚舉值 (區分大小寫) | Color.valueOf("RED") → RED |
name() | 返回聲明名稱 (不可覆蓋) | RED.name() → "RED" |
toString() | 默認返回 name(),可覆蓋 | System.out.println(RED) → "RED" |
equals(Object) | 引用比較 (==),final 方法 | RED.equals(RED) → true |
public enum Color {
RED(255, 0, 0),
BLUE(0, 0, 255),
BLACK(0, 0, 0),
YELLOW(255, 255, 0),
GREEN(0, 255, 0);
private final int r, g, b;
Color(int r, int g, int b) { this.r = r; this.g = g; this.b = b; }
public int getR() { return r; }
public int getG() { return g; }
public int getB() { return b; }
@Override public String toString() {
return name() + String.format("(%d,%d,%d)", r, g, b);
}
// 實現介面
public interface ColorConverter { int toGray(); }
// 但枚舉不能直接實現介面並為每個值提供不同實現
// 需用「具體方法 + switch」或「實例字段存儲策略對象」
}
// 使用
Color c = Color.RED;
System.out.println(c); // RED(255,0,0)
System.out.println(c.getR()); // 255
public enum Singleton {
INSTANCE;
private Resource resource;
Singleton() { resource = new Resource(); } // 構造器只執行一次
public void doSomething() { resource.use(); }
public static Singleton getInstance() { return INSTANCE; }
}
// 使用
Singleton.INSTANCE.doSomething();
// 線程安全、防反射破壞、防序列化重建、代碼極簡
public enum PayStrategy {
ALIPAY {
@Override public void pay(double amount) { System.out.println("支付寶支付 " + amount); }
},
WECHAT {
@Override public void pay(double amount) { System.out.println("微信支付 " + amount); }
},
UNIONPAY {
@Override public void pay(double amount) { System.out.println("銀聯支付 " + amount); }
};
public abstract void pay(double amount);
}
// 使用
PayStrategy strategy = PayStrategy.valueOf(config.getPayType());
strategy.pay(100.0);
// 傳統 Switch (需 break)
Color c = Color.RED;
switch (c) {
case RED: System.out.println("紅"); break;
case BLUE: System.out.println("藍"); break;
default: System.out.println("其他");
}
// Java 14+ Switch 表達式 (無 break、賦值、模式匹配預備)
String desc = switch (c) {
case RED -> "紅色";
case BLUE -> "藍色";
case YELLOW, GREEN -> "暖/冷色調";
default -> "未知";
};
// EnumSet: 位向量實現,極快、內存極小
EnumSet primary = EnumSet.of(Color.RED, Color.BLUE, Color.YELLOW);
EnumSet all = EnumSet.allOf(Color.class);
EnumSet none = EnumSet.noneOf(Color.class);
// EnumMap: 數組實現,鍵為枚舉,比 HashMap 快 2-10 倍
EnumMap names = new EnumMap<>(Color.class);
names.put(Color.RED, "紅");
names.put(Color.BLUE, "藍");
| 坑點 | 後果 | 解法 |
|---|---|---|
| ordinal() 依賴聲明順序 | 增刪枚舉值導致邏輯錯誤 | 業務邏輯禁用 ordinal(),用顯式字段/方法 |
| values() 返回克隆 | 頻繁調用產生垃圾 | 緩存結果或用 EnumSet.allOf() |
| valueOf() 區分大小寫 | 配置文件 "red" 解析失敗 | 預處理 name.toUpperCase() 或用 Map 映射 |
| 序列化機制特殊 | 非標準序列化 (只存 name) | 了解機制,勿手動序列化枚舉字段 |
| switch 未覆蓋所有值 | 新增枚舉值編譯不報錯 (傳統 switch) | 用 Switch 表達式 或 default: throw new IllegalStateException() |
enum 是「特殊的 final 類」,提供類型安全、單例保證、豐富 API、高性能集合。最佳實踐:用枚舉實現單例、策略模式、狀態機、固定常量集;禁用 ordinal() 做業務邏輯;優先用 Switch 表達式;集合場景用 EnumSet/EnumMap。掌握其本質與邊界,能寫出更安全、清晰、高效的代碼。