一、什么是注解?
從 JDK5 開始,Java增加對元數據的支持,也就是注解。簡單理解就是代碼里的特殊標志,這些標志可以在編譯,類加載,運行時被讀取,並執行相應的處理,以便於其他工具補充信息或者進行部署。
二、為什么要使用注解?
注解可以被其他程序(比如:編譯器等)讀取,開發人員可以在不改變原有代碼和邏輯的情況下在源代碼中嵌入補充信息。
三、注解的相關概述
3.1 注解的格式
注解就是以 @XXX 形式在代碼中存在的,我們還可以為注解添加一些參數值,例如 @SuppressWarnings(value = "unchecked") 。
3.2 元注解
元注解就是負責注解其它注解的注解。
下圖為Java定義的標准的 元注解類型,他們用來對其他的注解進行說明,可以在 Java API 的 java.lang.annotation 包中找到。
3.2.1 @Document
官方示例:
// since 1.5 @Documented @Retention(RUNTIME) @Target(ANNOTATION_TYPE) public @interface Documented
官方描述:
- 如果注釋@Documented出現在注釋類型A的聲明中,那么元素上的任何@A注釋都被認為是元素公共契約的一部分。
- 更詳細地說,當使用Documented對注釋類型A進行注釋時,類型A的注釋的存在和值是A注釋的元素的公共契約的一部分。相反,如果注釋類型B沒有被文檔化注釋,那么B注釋的存在和值就不是B注釋元素的公共契約的一部分。
- 具體地說,如果注釋類型是用Documented注釋的,那么默認情況下,像javadoc這樣的工具將在其輸出中顯示該類型的注釋,而沒有Documented的注釋類型的注釋將不會顯示。
間而言之:
如果使用了 @Documented ,就說明此類(或方法、字段等)的文檔化注釋就會被包含在JavaDoc中。
3.2.2 @Inherited
官方示例:
// since 1.5 @Documented @Retention(RUNTIME) @Target(ANNOTATION_TYPE) public @interface Inherited
官方描述:
- 指示自動繼承批注類型。
- 如果在注釋類型聲明上存在繼承的元注釋,並且用戶在類聲明上查詢注釋類型,而該類聲明對此類型沒有注釋,則將自動在類的超類中查詢注釋類型。將重復此過程,直到找到此類型的注釋,或到達類層次結構(對象)的頂部。
如果沒有超類具有此類型的注釋,則查詢將指示所討論的類沒有此類注釋。 - 請注意,如果注釋類型用於注釋類以外的任何內容,則此元注釋類型無效。還要注意,這個元注釋只會導致注釋從超類繼承;對實現接口的注釋沒有影響。
簡而言之:
子類可以繼承父類(超類)中的該注解。這個注解指定被他修飾的注解將具有繼承性——如果某個類使用了@XXX,則其子類將自動被@XXX修飾
代碼示例:
1 public class test2 { 2 3 public static void main(String[] args) { 4 Person person = new Person(); 5 Student student = new Student(); 6 7 Annotation[] parentAnnotation = person.getClass().getAnnotations(); 8 Annotation[] studentAnnotation = student.getClass().getAnnotations(); 9 for (Annotation annotation : parentAnnotation) { 10 System.out.println(annotation); 11 } 12 System.out.println("---------------------"); 13 for (Annotation annotation : studentAnnotation) { 14 System.out.println(annotation); 15 } 16 17 // 輸出結果 18 // @com.ruiyicloud.bbfbusiness.demo.annotation.MyAnnotation3() 19 // --------------------- 20 // @com.ruiyicloud.bbfbusiness.demo.annotation.MyAnnotation3() 21 22 } 23 24 25 } 26 27 @Target({ElementType.TYPE}) 28 @Retention(RetentionPolicy.RUNTIME) 29 @Inherited 30 @interface MyAnnotation3{ 31 32 } 33 @MyAnnotation3 34 class Person{ 35 String name; 36 } 37 38 class Student extends Person{ 39 int age; 40 }
注意:
如果 MyAnnotation3 注解 去掉 @Inherited,則student.getClass().getAnnotations() 將輸入空。
3.2.3 @Native
官方代碼:
// since 1.8 @Documented @Target(FIELD) @Retention(SOURCE) public @interface Native
官方描述:
指示定義常量值的字段可以從本機代碼引用。生成本機頭文件的工具可以使用該注釋作為提示,以確定是否需要頭文件,如果需要,還應該包含哪些聲明。
間而言之:
Navtive是指一些變量和平台相關的linux系統和window操作系統的本地文件頭。
使用本地方法,我們可以用java與底層系統的交互,如果使用Java獲取不到我們想要的內容,我們可以選擇使用本地方法。
使用 @Native 注解修飾變量值的字段,則表示這個變量可以被本地代碼引用。
代碼示例:
附上Integer的部分源碼
1 public final class Integer extends Number 2 implements Comparable<Integer>, Constable, ConstantDesc { 3 /** 4 * A constant holding the minimum value an {@code int} can 5 * have, -2<sup>31</sup>. 6 */ 7 @Native public static final int MIN_VALUE = 0x80000000; 8 9 10 11 // 比較值的大小 12 public static int compareUnsigned(int x, int y) { 13 return compare(x + MIN_VALUE, y + MIN_VALUE); 14 }
3.2.4 @Repeatable
官方代碼:
// since 1.8 @Documented @Retention(RUNTIME) @Target(ANNOTATION_TYPE) public @interface Repeatable
官方描述:
注釋類型java.lang.annotation注釋.Repeatable用於指示它(meta-)注釋其聲明的注釋類型是可重復的。@Repeatable的值表示可重復注釋類型的包含注釋類型。
間而言之:
使用@Repeatable這個聲明的注解是可重復的。@Repeatable的值是另一個注解,其可以通過這個另一個注解的值來包含這個可重復的注解。
代碼示例:
FooContainer 作用於范圍只能在注解類型上,所以作用於接口上時會報錯
1 import java.lang.annotation.ElementType; 2 import java.lang.annotation.Repeatable; 3 import java.lang.annotation.Target; 4 5 public class test03 { 6 7 } 8 9 10 @Target({ElementType.TYPE,ElementType.ANNOTATION_TYPE}) 11 @Repeatable(FooContainer.class) 12 @interface Foo {} 13 14 @Target(ElementType.ANNOTATION_TYPE) 15 @interface FooContainer { 16 Foo[] value(); 17 } 18 19 //正確的 20 @Foo @Foo 21 @interface FooContainer1 { 22 Foo[] value(); 23 } 24 25 // 錯誤的 26 @Foo @Foo 27 interface aa { 28 29 }
注意事項:
- Foo的保留時間至少與FooContainer一樣長,其中保留用@Retention 注釋顯式或隱式表示。特別:
- 如果Foo的保留為java.lang.annotation.RetentionPolicy.SOURCE,則FooContainer的保留為java.lang.annotation.RetentionPolicy.SOURCE。
- 如果Foo的保留值為java.lang.annotation.RetentionPolicy.CLASS,則FooContainer的保留值為java.lang.annotation.RetentionPolicy.CLASS或 java.lang.annotation.RetentionPolicy.SOURCE。
- 如果保留Foo是java.lang.annotation.RetentionPolicy.RUNTIME,則保留FooContainer是java.lang.annotation.RetentionPolicy.SOURCE, java.lang.annotation.RetentionPolicy.CLASS,或java.lang.annotation.RetentionPolicy.RUNTIME。
3.2.5 @Retention
官方示例:
// since 1.5 @Documented @Retention(RUNTIME) @Target(ANNOTATION_TYPE) public @interface Retention
官方描述:
- 指示帶批注類型的批注要保留多長時間。如果批注類型聲明上不存在保留批注,則保留策略默認為保留策略.CLASS.
- 只有當元注釋類型直接用於注釋時,保留元注釋才有效。如果元注釋類型被用作另一個注釋類型中的成員類型,則沒有效果。
枚舉常量 | 描述 |
---|---|
CLASS |
枚舉常量將由編譯器記錄在類文件中,但不需要在運行時由VM保留.
|
RUNTIME |
注釋將由編譯器記錄在類文件中,並在運行時由VM保留,因此可以反射地讀取它們.
|
SOURCE |
注釋將被編譯器丟棄.
|
間而言之:
此注解主要作用為在什么級別保存該注解信息,用於描述注解的聲明周期,需要主要的是 SOURCE < CLASS < RUNTIME 。
3.2.6 @Target
官方示例:
// since 1.5 @Documented @Retention(RUNTIME) @Target(ANNOTATION_TYPE) public @interface Target
官方描述:
- 指示批注類型適用的上下文。JLS 9.6.4.1中指定了可應用注釋類型的聲明上下文和類型上下文,並在源代碼中用java.lang.annotation注釋.ElementType。
- 如果@Target元注釋不存在於注釋類型T上,那么類型T的注釋可以作為除類型參數聲明之外的任何聲明的修飾符來編寫。
- 如果存在@Target元注釋,編譯器將根據JLS 9.7.4強制執行由ElementType enum常量指示的使用限制。
簡而言之:
@Target注解主要用於描述注解的使用范圍(例如,添加某些屬性的時候注解可以使用在類上,添加某些屬性的時候注解可以使用在方法上等)
3.3 注解的使用范圍?
ElementType枚舉常量 |
作用范圍 |
TYPE | 類、接口(包括注釋類型)、枚舉或記錄 |
FIELD | 字段聲明(包括枚舉常量) |
METHOD | 方法聲明 |
PARAMETER | 形式參數聲明 |
CONSTRUCTOR | 構造函數聲明 |
LOCAL_VARIABLE | 局部變量聲明 |
ANNOTATION_TYPE | 注解類型聲明 |
PACKAGE | 包聲明 |
TYPE_PARAMETER | 類型參數聲明(since 8) |
TYPE_USE | 類型的使用(since 8) |
MODULE | 模塊聲明 (since 9) |
RECORD_COMPONENT | Java語言的一種預覽功能(since 14) |
3.4 常用的幾個注解
@Override :
限定父類重寫方法,當子類重寫父類方法時,子類可以加上這個注解,可以確保子類確實重寫了父類的方法,避免出現低級錯誤。
@FunctionalInterface:
函數式接口,注解保證這個接口只有一個抽象方法,注意這個只能修飾接口。(函數式接口是指 接口中只有一個抽象方法(可以包含多個默認方法或多個static方法),接口體內只能聲明常量字段和抽象方法,並且被隱式聲明為public,static,final。接口里面不能有私有的方法或變量。)
@Deprecated:
標示已過時,這個注解用於表示某個程序元素類,方法等已過時,當其他程序使用已過時的類,方法時編譯器會給出警告。
@SuppressWarning:
抑制編譯器警告,被該注解修飾的元素以及該元素的所有子元素取消顯示編譯器警告。
四、自定義注解實戰
本例創建一個簡單的注解,並在方法上、類上進行使用
@MyAnnotation public class Main { @MyAnnotation public static void main(String[] args) { } } // Target 標識注解可以在什么地方使用 @Target({ElementType.METHOD,ElementType.TYPE}) // 標識此注解在什么地方還有效 @Retention(RetentionPolicy.RUNTIME) // 標識是否將我們的注解生成在JavaDoc中 @Documented // 子類可以繼承父類的注解 @Inherited @interface MyAnnotation{ }
五、總結
總的來說,注解還是比較簡單的。