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

Java 註解實戰指南:標準註解、元註解、自定義註解與處理器完整教學

     編輯:Java 相關知識

前言

註解 是 Java 5 引入的元資料機制,不改變程式語義,但為工具/框架提供豐富語義線索。本文從「如何使用」切入,涵蓋內建註解、元註解配置、自定義註解設計、編譯期處理器原理、運行期反射讀取,並給出實戰最佳實踐。

一、 內建標準註解速查

註解用途保留策略典型場景
@Override編譯期驗證方法確實覆寫父類/介面SOURCE防止拼寫錯誤導致意外重載
@Deprecated標記不建議使用,編譯器警告RUNTIMEAPI 演進期平滑遷移
@SuppressWarnings抑制特定編譯器警告SOURCE@SuppressWarnings("unchecked") 消除泛型未檢查警告
@SafeVarargs (JDK7+)聲明可變參數方法不執行不安全操作RUNTIME消除堆污染警告
@FunctionalInterface (JDK8+)標記函數式介面 (單一抽象方法)RUNTIME編譯器驗證介面結構

二、 兩大關鍵元註解:@Target 與 @Retention

這兩個元註解決定自定義註解「能貼在哪裡」與「能活多久」。

1. @Target — 作用目標


@Target({ElementType.FIELD, ElementType.PARAMETER})
public @interface Resource {
    String id() default "";
}

常用目標類型:

  • TYPE:類、介面、註解、列舉
  • FIELD:欄位、列舉常量
  • METHOD:方法
  • PARAMETER:方法參數
  • CONSTRUCTOR:建構子
  • LOCAL_VARIABLE:區域變數
  • ANNOTATION_TYPE:註解定義 (元註解)
  • PACKAGE:套件聲明
  • TYPE_PARAMETER、TYPE_USE (JDK8+):泛型類型參數、類型使用位置

2. @Retention — 生命週期


@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME) // 關鍵:需反射讀取必須設 RUNTIME
public @interface Resource {
    String id() default "";
}
  • SOURCE:編譯期有效,.class 不保留 (如 @Override)。
  • CLASS:.class 保留,VM 載入時丟棄 (預設)。
  • RUNTIME:VM 載入並保留,**反射可讀** (框架掃描必備)。

三、 自定義註解設計全流程

1. 定義註解類型


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented // 納入 Javadoc
public @interface TestCase {
    String id();           // 必填元素
    String description() default ""; // 選填,有預設值
    int priority() default 1;
    String[] tags() default {};      // 陣列元素
}

規則:元素類型限於基本型別、String、Class、列舉、註解類型及其陣列;不可有參數、拋出異常、泛型 (除 Class)。

2. 使用註解


public class UserService {
    @TestCase(id = "TC-001", description = "正常登入", priority = 1, tags = {"smoke", "auth"})
    public void login_validCredentials() { ... }

    @TestCase(id = "TC-002", description = "密碼錯誤")
    public void login_invalidPassword() { ... }
}

3. 運行期反射讀取 (框架核心邏輯)


public class TestRunner {
    public static void runTests(Class<?> clazz) {
        for (Method m : clazz.getDeclaredMethods()) {
            if (m.isAnnotationPresent(TestCase.class)) {
                TestCase tc = m.getAnnotation(TestCase.class);
                System.out.printf("執行 %s [%s] 優先級:%d 標籤:%s%n",
                    m.getName(), tc.id(), tc.priority(), Arrays.toString(tc.tags()));
                try {
                    m.invoke(null); // 假設靜態方法
                    System.out.println("  → 通過");
                } catch (Exception e) {
                    System.err.println("  → 失敗: " + e.getCause());
                }
            }
        }
    }
}

四、 進階:標記註解與單元素簡寫

  • 標記註解:無元素,如 @interface Serializable {}。使用時可省略括號:@Serializable。
  • 單元素註解:慣用元素名 value,使用時可省略 value =:@Copyright("2024") 等同 @Copyright(value="2024")。

五、 何時需要自定義註解?

  1. 框架/庫開發者:提供聲明式 API (如 Spring @Transactional, JPA @Entity)。
  2. 代碼生成/靜態分析:配合 Annotation Processor 生成代碼 (Lombok, MapStruct) 或檢查規範 (Checker Framework)。
  3. 業務元資料標記:測試用例管理、API 文檔自動生成、審計日誌、權限控制等橫切關注點。

應用層開發者通常無需自定義註解,善用框架提供的註解即可。

六、 避坑指南

  • 忘記 @Retention(RUNTIME) → 反射讀不到,框架失效。
  • @Target 過寬 → 註解被濫用到不合理位置 (如類註解貼在區域變數)。
  • 元素無預設值但使用時未填 → 編譯錯誤。
  • 註解屬性使用可變對象:陣列/集合在註解中實為不可變,但回傳的陣列可被修改,建議回傳副本或用不可變集合。
  • 繼承性誤解:@Inherited 僅對 @Target(TYPE) 的註解在類繼承鏈生效,**不適用於方法/欄位覆寫**。

總結

註解是「程式碼上的便利貼」,配合編譯期處理器或運行期反射,能將聲明式意圖轉化為具體行為。掌握 @Target/@Retention 兩大元註解配置、元素類型限制、反射讀取 API,即可靈活設計自定義註解,或深度理解主流框架運作原理。

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