首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

《編寫高質量代碼》第6章精華:枚舉與注解最佳實踐(建議88-92)

     編輯:Java 綜合教學

《編寫高質量代碼:改善Java程序的151個建議》第6章精華:枚舉與注解最佳實踐

本文摘錄並深度解析書籍第六章「枚舉和注解」中的建議88-92,涵蓋枚舉實現工廠模式、EnumSet性能優化、注解繼承陷阱、枚舉注解組合實戰、@Override版本差異五大核心知識點,並結合源碼與實戰案例進行擴展,助力開發者掌握高質量代碼設計精髓。

建議88:用枚舉實現工廠方法模式更簡潔

傳統工廠模式的痛點

傳統工廠方法需接收 Class、String 或 int 參數,存在三大缺陷:

  • 類型不安全:編譯期無法檢測非法參數(如傳入接口 Car.class 導致運行時 InstantiationException)
  • 違背迪米特法則:客戶端需依賴具體產品類(如 FordCar.class),增加耦合
  • 參數驗證繁瑣:需手動處理 null、越界等邊界情況

枚舉實現工廠的兩種優雅方案

方案一:枚舉非靜態方法

enum CarFactory {
    FordCar, BuickCar;
    public Car create() {
        switch (this) {
            case FordCar:  return new FordCar();
            case BuickCar: return new BuickCar();
            default: throw new AssertionError("無效類型");
        }
    }
}
// 客戶端調用:Car car = CarFactory.BuickCar.create();

方案二:枚舉抽象方法(每個枚舉項實現各自邏輯)

enum CarFactory {
    FordCar { public Car create() { return new FordCar(); } },
    BuickCar { public Car create() { return new BuickCar(); } };
    public abstract Car create();
}

三大核心優勢

  1. 編譯期類型安全:無需傳參,直接選擇枚舉項,不可能出現非法參數
  2. 極致性能:枚舉底層按 int 索引計算,無反射開銷,比 Class.newInstance() 快數量級
  3. 零耦合設計:客戶端僅依賴工廠枚舉,完全屏蔽具體產品類,完美符合最少知識原則

建議89:枚舉項數量限制在64個以內

EnumSet 雙實現機制深度解析

EnumSet 根據枚舉項數量自動選擇實現:

  • RegularEnumSet(≤64項):單個 long 位向量存儲,elements = -1L >>> -universe.length 一次位運算完成全量添加,O(1) 極致性能
  • JumboEnumSet(>64項):long[] 數組分段存儲,每段64位,需遍歷數組操作,性能下降

源碼證據

public static > EnumSet noneOf(Class elementType) {
    Enum[] universe = getUniverse(elementType);
    if (universe.length <= 64)
        return new RegularEnumSet<>(elementType, universe);
    else
        return new JumboEnumSet<>(elementType, universe);
}

實戰建議

  • 業務枚舉(如訂單狀態、權限類型)通常遠小於64項,優先使用 EnumSet
  • 若超過64項,考慮拆分為多個語義獨立的枚舉,或改用 EnumMap / 普通 HashSet
  • 避免將「系統字典表」類大量數據定義為枚舉,應存放於數據庫/配置中心

建議90:小心注解繼承(@Inherited 的雙刃劍)

@Inherited 行為演示

@Retention(RUNTIME) @Target(TYPE) @Inherited
@interface Desc { Color c() default Color.White; }

@Desc(c = Color.White) abstract class Bird { ... }
class Sparrow extends Bird { ... } // 未顯式標註 @Desc

// 運行時:Sparrow.class.getAnnotation(Desc.class) 返回父類 @Desc
// 輸出 "Bird's color is White" 而非 Sparrow 默認的 Grayish

利與弊權衡

優點缺點
統一性:父類標註一次,全系繼承自動生效隱式行為:閱讀子類代碼無法發現注解存在,導致邏輯「莫名其妙」生效
管理便利:批量修改父類注解即可影響所有子類層次越深越失控:多層繼承鏈下,難以追溯哪個祖先注解劫持了邏輯

最佳實踐:除非極簡單的單層繼承場景,默認不使用 @Inherited。改用顯式標註或組合模式,讓注解元數據可見、可追溯。

建議91:枚舉與注解結合使用威力更大

ACL 權限控制實戰案例

問題:注解不支持繼承/實現接口,無法直接在注解上定義鑒權邏輯。

解法:枚舉實現接口 + 注解引用枚舉,形成「委派模式」:

interface Identifier {
    String REFUSE_WORD = "您無權訪問";
    boolean identify();
}

enum CommonIdentifier implements Identifier {
    Reader, Author, Admin;
    public boolean identify() { /* 具體鑒權邏輯 */ return false; }
}

@Retention(RUNTIME) @Target(TYPE)
@interface Access {
    CommonIdentifier level() default CommonIdentifier.Admin;
}

@Access(level = CommonIdentifier.Author) class Foo { }

運行時:access.level().identify() 通過枚舉項委派執行鑒權,繞過注解無法攜帶邏輯的限制,實現「聲明式權限配置 + 靈活鑒權實現」完美解耦。

建議92:注意 @Override 不同版本的區別

版本演進時間軸

  • Java 1.5:嚴格遵循「覆寫」定義——父類必須是類(非接口),方法簽名完全一致。實現接口方法加 @Override 會編譯報錯:must override a superclass method
  • Java 1.6+:放寬規則,允許標註在實現接口方法上,防止拼寫錯誤(如 doSomeThing vs doSomething)

跨版本部署避坑指南

  1. 開發環境統一 JDK 版本(建議 1.8+ 或 11+)
  2. 若需部署到 1.5 環境,需移除接口實現方法上的 @Override
  3. Maven Compiler Plugin 配置 1.51.5 可提前發現兼容性問題

總結

枚舉與注解是 Java 元編程的基石。善用枚舉實現工廠、EnumSet 優化集合操作、謹慎對待 @Inherited、枚舉注解協作實現聲明式邏輯、關注 @Override 版本差異,能顯著提升代碼的類型安全性、運行性能與可維護性。這五條建議不僅是語法技巧,更體現了「面向接口編程」、「最少知識原則」、「編譯期錯誤優於運行時」等核心設計哲學。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved