JDK 5.0 引入 enum 關鍵字,將枚舉從「整數常量模式」提升為功能完整的類。枚舉實例是單例、天然線程安全、可實現接口、可定義方法/字段/構造器。本文系統梳理枚舉七大特性、進階用法、設計模式應用及最佳實踐。
| 特性 | 說明 | 代碼驗證 |
|---|---|---|
| 無 public 構造器 | 編譯器強制 private,保證實例受控 | new Color() → 編譯錯誤 |
| 實例為單例 | public static final,JVM 保證唯一 | Color.RED == Color.RED → true |
| 實現 Comparable | 按聲明順序比較,ordinal() 返回索引 | Color.RED.compareTo(Color.BLUE) < 0 |
| 覆寫 toString() | 默認返回常量名,可自定義 | Color.RED.toString() → "RED" |
| valueOf() 反向解析 | Color.valueOf("RED") → Color.RED,大小寫敏感 | 異常處理:IllegalArgumentException |
| values() 遍歷 | 返回聲明順序數組,for (Color c : Color.values()) | 編譯器生成靜態方法 |
| ordinal() 索引 | 從 0 開始,慎用於業務邏輯(插入枚舉值會改變索引) | Color.RED.ordinal() == 0 |
public enum Color {
RED, GREEN, BLUE;
// 遍歷演示
public static void printAll() {
for (Color c : Color.values()) {
System.out.println(c + " ordinal=" + c.ordinal());
}
}
}
public enum Color {
RED("紅色", 0xFF0000),
GREEN("綠色", 0x00FF00),
BLUE("藍色", 0x0000FF);
private final String chineseName;
private final int rgb;
// 私有構造器,枚舉值聲明處調用
Color(String chineseName, int rgb) {
this.chineseName = chineseName;
this.rgb = rgb;
}
public String getChineseName() { return chineseName; }
public int getRGB() { return rgb; }
// 靜態工廠:隨機顏色
private static final Random RANDOM = new Random();
public static Color randomColor() {
return values()[RANDOM.nextInt(values().length)];
}
// 覆寫 toString
@Override public String toString() {
return name() + "(" + chineseName + ")";
}
}
關鍵規則:字段/方法/構造器必須寫在枚舉值列表之後,且構造器只能 private 或包私有。
public enum Status {
SUCCESS("操作成功"), FAILURE("操作失敗");
private final String desc;
Status(String desc) { this.desc = desc; }
@Override public String toString() { return desc; }
// 必須同步覆寫 valueOf 邏輯(實際需配合映射表)
public static Status fromDescription(String desc) {
for (Status s : values()) {
if (s.desc.equals(desc)) return s;
}
throw new IllegalArgumentException("Unknown desc: " + desc);
}
}
public interface DiscountStrategy {
BigDecimal apply(BigDecimal original);
}
public enum Discount implements DiscountStrategy {
NONE("無折扣") {
@Override public BigDecimal apply(BigDecimal original) { return original; }
},
VIP_10("VIP 9折") {
@Override public BigDecimal apply(BigDecimal original) {
return original.multiply(new BigDecimal("0.9"));
}
},
HOLIDAY_20("節日 8折") {
@Override public BigDecimal apply(BigDecimal original) {
return original.multiply(new BigDecimal("0.8"));
}
};
private final String desc;
Discount(String desc) { this.desc = desc; }
public String getDesc() { return desc; }
}
// 使用
BigDecimal price = new BigDecimal("100");
Discount.VIP_10.apply(price); // 90.0
Discount.HOLIDAY_20.apply(price); // 80.0
public enum OrderState {
PENDING {
@Override public OrderState next() { return PAID; }
@Override public boolean canCancel() { return true; }
},
PAID {
@Override public OrderState next() { return SHIPPED; }
@Override public boolean canCancel() { return false; }
},
SHIPPED {
@Override public OrderState next() { return DELIVERED; }
@Override public boolean canCancel() { return false; }
},
DELIVERED {
@Override public OrderState next() { throw new IllegalStateException("已送達"); }
@Override public boolean canCancel() { return false; }
};
public abstract OrderState next();
public abstract boolean canCancel();
}
// 使用
OrderState state = OrderState.PENDING;
state = state.next(); // PAID
state.canCancel(); // false
// EnumMap:鍵為枚舉,數組底層,極快
EnumMap colorNames = new EnumMap<>(Color.class);
colorNames.put(Color.RED, "紅");
colorNames.put(Color.GREEN, "綠");
// EnumSet:位向量底層,極省內存、極快
EnumSet primaryColors = EnumSet.of(Color.RED, Color.GREEN, Color.BLUE);
EnumSet allColors = EnumSet.allOf(Color.class);
EnumSet complement = EnumSet.complementOf(primaryColors); // 空集
public enum Singleton { INSTANCE; } — 防反射/序列化破壞,Effective Java 推薦valueOf(),天然防偽造switch (color) { case RED -> ... } 編譯器檢查完整性| 誤區 | 正確認知 |
|---|---|
| 枚舉不能有方法 | 可定義實例/靜態方法、字段、構造器、實現接口 |
| 枚舉值可修改 | 枚舉實例不可變,字段必須 final |
| values() 返回可修改數組 | 返回副本,修改不影響原枚舉 |
| 可繼承枚舉 | 枚舉隱含 final,不可繼承(但可實現接口) |
枚舉是 Java 類型系統的「瑞士軍刀」——小巧、強大、類型安全。善用之,代碼更清晰、Bug 更少。