Java注解 Annotation
Annotation的由來
從JDK5.0發布以來,5.0平台提供了一個正式的annotation功能:允許開發者定義、使用自己的annotation類型。
此功能由一個定義annotation類型的語法和一個描述annotation聲明的語法,讀取annotation的API,一個使用annotation修飾的class文件,一個annotation處理工具(apt)組成。
Annotation工作方式
Annotation並不直接影響代碼語義,但是它能夠被看作類似程序的工具或者類庫,它會反過來對正在運行的程序語義有所影響。
Annotation可以從源文件、class文件、或者以在運行時反射的多種方式被讀取。
JDK中內置的注解
Java注解Annotation:
Override注解(@Override)表示子類要重寫父類的對應方法。
Deprecated注解(@Deprecated)表示方法是不建議被使用的。
SuppressWarnings注解(如:@SuppressWarnings("unchecked"))表示抑制警告。
括號中的參數(一個字符串數組)表示要壓制的警告類型。單個字符串的時候可以用也可以不用花括號,但是多個字符串的時候需要用花括號包起來表示,
比如:
@SuppressWarnings({"unchecked","deprecation"})
如果注解一個類去壓制一種警告,再注解類中的方法取壓制另一種警告,則方法中會同時壓制這兩種警告。
Annotation的定義方式
自己定義注解:
新建->Annotation.
可以看到注解定義的形式與interface類似,不同的是在interface關鍵字前面加了一個@符號。
自定義注解:當注解中的屬性名為value時,在對其賦值時可以不指定屬性的名稱而直接寫上屬性值即可;除了value以外的其他值都需要使用name=value這種顯式賦值方式,即明確指定給誰賦值。
程序示例1:

public @interface AnnotationTest { String value() default "hello"; //不加()就出錯了 //屬性名字叫作value時可以不加名字直接賦值 //屬性名字不叫作value時給屬性賦值必須顯式指定名字 //可以通過default關鍵字設置默認值 }

//使用自定義的注解: @AnnotationTest(value = "hello") // 自定義注解中加了String value屬性之后需要對屬性進行賦值,當屬性有默認值之后可以不賦值 public class AnnotationUsage { // 使用自定義的注解: @AnnotationTest("world") // 不加value=也是可以的 public void method() { System.out.println("usage of annotation!"); } public static void main(String[] args) { AnnotationUsage usage = new AnnotationUsage(); usage.method(); } }
可以使用關鍵字default給屬性設定默認值,如:String value() default "hello";
程序示例2:

public @interface AnnotationTest { String[] value1() default "hello"; // 不加()就出錯了 // 屬性名字叫作value時可以不加名字直接賦值 // 屬性名字不叫作value時給屬性賦值必須顯式指定名字 // 可以通過default關鍵字設置默認值 EnumTest value2(); } enum EnumTest { Hello, World, Welcome; } //使用自定義的注解: @AnnotationTest(value2 = EnumTest.Hello) // 自定義注解中加了屬性之后需要對屬性進行賦值,當屬性有默認值之后可以不賦值 public class AnnotationUsage { // 使用自定義的注解: @AnnotationTest(value1={"Hello","world"}, value2 = EnumTest.World) //value1現在是數組,所以可以賦多個值 public void method() { System.out.println("usage of annotation!"); } public static void main(String[] args) { AnnotationUsage usage = new AnnotationUsage(); usage.method(); } }
使用@interface自行定義注解時,實際上是自動繼承了java.lang.annotation.Annotation接口,由編譯程序自動為您完成其他產生的細節。
在定義Annotation型態時,不能繼承其他的Annotation型態或是接口。
要想定義注解只能通過@interface關鍵字來定義,手動繼承Annotation接口並不能定義一個注解類型,即如果定義一個接口繼承了Annotation,那么該接口仍然只是一個接口而不是注解。注意Annotation接口本身並不是注解。
定義Annotation型態時也可以使用包來管理類別,方式類同於類的導入功能。
告知編譯程序如何處理@Retention
java.lang.annotation.Retention型態可以在您定義Annotation型態時,指示編譯程序該如何對待您自定義的Annotation型態。
預設上編譯程序會將Annotation信息留在.class檔案中,但不被虛擬機讀取,而僅用於編譯程序或工具程序運行時提供信息。
在使用Retention型態時,需要提供
java.lang.annotation.RetentionPolicy的枚舉型態:
package java.lang.annotation; public enum RetentionPolicy { SOURCE, //編譯程序處理完Annotation信息后就完成任務,注解只會存在於源文件當中,編譯器將其丟棄,不會把注解編譯到class文件當中, CLASS, //編譯程序將Annotation存儲於class檔中,缺省,即默認情況是這種行為 RUNTIME //編譯程序將Annotation存儲於class檔中,可由VM讀入,可以通過反射的方式讀取到 }
RetentionPolicy為SOURCE的例子是@SuppressWarnings。
僅在編譯時期告知編譯程序來抑制警告,所以不必將這個信息存儲於.class檔案。
RetentionPolicy為RUNTIME的時機,可以像是您使用Java設計一個程序代碼分析工具,您必須讓VM能讀出Annotation信息,以便在分析程序時使用。搭配反射(Reflection)機制,就可以達到這個目的。
java.lang.reflect.AnnotatedElement接口。其中包含了這樣幾個方法:
<T extends Annotation> T getAnnotation(Class<T> annotationClass)
Annotation[] getAnnotations()
Annotation[] getDeclaredAnnotations()
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
Class, Constructor, Method, Field, Package等類別,都實現了AnnotatedElement接口。
程序示例3——通過反射獲取注解信息
首先自定義注解類型:
import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String hello() default "shengqishi"; String world(); }
之后在類中使用自定義的注解:
@MyAnnotation(hello="beijing",world="tianjin") public class MyTest { @MyAnnotation(hello="shanghai",world="guangzhou") @Deprecated @SuppressWarnings("unchecked") public void output() { System.out.println("Output something."); } }
通過反射獲取注解信息:
import java.lang.annotation.Annotation; import java.lang.reflect.Method; //通過反射獲取注解信息 public class MyReflection { public static void main(String[] args) throws Exception { MyTest myTest = new MyTest(); // 獲取Class對象 Class<MyTest> c = MyTest.class; // 獲取Method對象 Method method = c.getMethod("output", new Class[] {}); // 判斷是否存在指定類型的注解 if (method.isAnnotationPresent(MyAnnotation.class)) { // 如果存在該類型注解,這執行這個方法 method.invoke(myTest, new Object[] {}); // 如果MyAnnotation前面是@Retention(RetentionPolicy.RUNTIME),則執行 // 如果MyAnnotation前面是@Retention(RetentionPolicy.CLASS),或SOURCE,則括號內語句不執行 // 因為只有為RUNTIME時,注解信息會被讀取過來,其他兩種情況注解不能被反射讀取過來 // 返回注解 MyAnnotation myAnnotation = method .getAnnotation(MyAnnotation.class); // 獲取並打印注解中的信息 String hello = myAnnotation.hello(); String world = myAnnotation.world(); System.out.println(hello + ", " + world); } // 獲取方法的全部的Annotation Annotation[] annotations = method.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation.annotationType().getName()); } // 運行結果為: // com.learnjava.annotation.MyAnnotation // java.lang.Deprecated // 因為只能獲取到RetentionPolicy為RUNTIME的注解 } }
限定annotation使用對象@Target
使用java.lang.annotation.Target可以定義其使用之時機。即表示注解可以修飾什么(修飾類、方法、注解等)。
在定義時要指定java.lang.annotation.ElementType的枚舉值之一。
package java.lang.annotation; public enum ElementType { TYPE, //適用class,interface,enum FIELD, //適用field METHOD, //適用method PARAMETER, //適用method上之parameter CONSTRUCTOR, //適用constructor LOCAL_VARIABLE, //適用局部變量 ANNOTATION_TYPE, //適用annotation型態 PACKAGE, //使用package }
程序示例4——設定注解目標

import java.lang.annotation.ElementType; import java.lang.annotation.Target; @Target(ElementType.METHOD) public @interface MyTarget { String value(); } public class MyTargetTest { @MyTarget("hello") // 此注解只能被用於修飾方法,放在別的地方會出錯 public void doSomething() { System.out.println("Do something!"); } }
要求為API文件@Documented
想要在使用者制作JavaDoc文件的同時,也一並將Annotation的訊息加入API文件中,使用
java.lang.annotation.Documented
程序示例5——注解加入文檔

import java.lang.annotation.Documented; @Documented // 表示注解DocumentedAnnotation將會生成到文檔里面去 public @interface DocumentedAnnotation { String hello(); } public class DocumentedTest { @DocumentedAnnotation(hello="welcome") public void method() { System.out.println("Hello World!"); } }
子類是否繼承父類@Inherited
缺省狀態下,父類中的annotation並不會被子類繼承。
可以在定義Annotation型態時加上java.lang.annotation.Inherited型態的Annotation。
比較簡單,不再舉例。
參考資料
張龍老師Java SE視頻教程。
另:官方文檔是最好的參考資料。