1. 程式人生 > 實用技巧 >註解之初認識

註解之初認識

什麼是註解(Annotation)?註解是放在Java原始碼的類、方法、欄位、引數前的一種特殊“註釋”:

註釋會被編譯器直接忽略,註解則可以被編譯器打包進入class檔案,因此,註解是一種用作標註的“元資料”。

舉個栗子,對以下程式碼:

 1 public class Hello {
 2     @Check(min=0, max=100, value=55)
 3     public int n;
 4 
 5     @Check(value=99)
 6     public int p;
 7 
 8     @Check(99) // @Check(value=99)
 9     public
int x; 10 11 @Check 12 public int y; 13 }

@Check就是一個註解。第一個@Check(min=0, max=100, value=55)明確定義了三個引數,第二個@Check(value=99)只定義了一個value引數,它實際上和@Check(99)是完全一樣的。最後一個@Check表示所有引數都使用預設值。

註解的作用

從JVM的角度看,註解本身對程式碼邏輯沒有任何影響,如何使用註解完全由工具決定。

Java的註解可以分為三類:

第一類是由編譯器使用的註解,例如:

  • @Override:讓編譯器檢查該方法是否正確地實現了覆寫;
  • @SuppressWarnings
    :告訴編譯器忽略此處程式碼產生的警告。

這類註解不會被編譯進入.class檔案,它們在編譯後就被編譯器扔掉了。

第二類是由工具處理.class檔案使用的註解,比如有些工具會在載入class的時候,對class做動態修改,實現一些特殊的功能。這類註解會被編譯進入.class檔案,但載入結束後並不會存在於記憶體中。這類註解只被一些底層庫使用,一般我們不必自己處理。

第三類是在程式執行期能夠讀取的註解,它們在載入後一直存在於JVM中,這也是最常用的註解。例如,一個配置了@PostConstruct的方法會在呼叫構造方法後自動被呼叫(這是Java程式碼讀取該註解實現的功能,JVM並不會識別該註解)。

定義註解

Java語言使用@interface語法來定義註解(Annotation),它的格式如下:

public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

註解的引數類似無引數的介面方法,可以用default設定一個預設值(強烈推薦)。最常用的引數應當命名為value

元註解

有一些註解可以修飾其他註解,這些註解就稱為元註解(meta annotation)。Java標準庫已經定義了一些元註解,我們只需要使用元註解,通常不需要自己去編寫元註解。

@Target

最常用的元註解是@Target使用@Target可以定義Annotation能夠被應用於原始碼的哪些位置:

  • 類或介面:ElementType.TYPE
  • 欄位:ElementType.FIELD
  • 方法:ElementType.METHOD
  • 構造方法:ElementType.CONSTRUCTOR
  • 方法引數:ElementType.PARAMETER

例如,定義註解@Report可用在方法上,我們必須新增一個@Target(ElementType.METHOD)

@Target(ElementType.METHOD)
public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

定義註解@Report可用在方法或欄位上,可以把@Target註解引數變為陣列{ ElementType.METHOD, ElementType.FIELD }

@Target({
    ElementType.METHOD,
    ElementType.FIELD
})
public @interface Report {
    ...
}

實際上@Target定義的valueElementType[]陣列,只有一個元素時,可以省略陣列的寫法。

@Retention

另一個重要的元註解@Retention定義了Annotation的生命週期:

  • 僅編譯期:RetentionPolicy.SOURCE
  • 僅class檔案:RetentionPolicy.CLASS
  • 執行期:RetentionPolicy.RUNTIME

如果@Retention不存在,則該Annotation預設為CLASS。因為通常我們自定義的Annotation都是RUNTIME,所以,務必要加上@Retention(RetentionPolicy.RUNTIME)這個元註解:

@Retention(RetentionPolicy.RUNTIME)
public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

如何定義Annotation

我們總結一下定義Annotation的步驟:

第一步,用@interface定義註解:

public @interface Report {
}

第二步,新增引數、預設值:

public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

把最常用的引數定義為value(),推薦所有引數都儘量設定預設值。

第三步,用元註解配置註解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

其中,必須設定@Target@Retention@Retention一般設定為RUNTIME,因為我們自定義的註解通常要求在執行期讀取。一般情況下,不必寫@Inherited@Repeatable

小結

Java使用@interface定義註解:

可定義多個引數和預設值,核心引數使用value名稱;

必須設定@Target來指定Annotation可以應用的範圍;

應當設定@Retention(RetentionPolicy.RUNTIME)便於執行期讀取該Annotation