註解之初認識
什麼是註解(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 publicint 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
定義的value
是ElementType[]
陣列,只有一個元素時,可以省略陣列的寫法。
@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
。