Annotation(注解)簡介:
注解大家印象最深刻的可能就是JUnit做單元測試,和各種架構裡的使用了。本文主要簡單介紹一下注解的使用方法,下篇文章再深入的研究。
annotation并不直接影響代碼語義,但是它能夠被看作類似程式的工具或者類庫,它會反過來對正在運作的程式語義有所影響。
annotation可以從源檔案,class檔案或者以在運作時反射的多種方式被讀取
Java注解系統自帶有主要以下幾個注解:
Override注解表示子類要重寫(override)父類的對應方法
Deprecated注解表示方法是不建議被使用的
Suppress Warnings注解表示抑制警告
如何自定義注解:
隻需要使用@interface來定義一個注解,例如:
[java] view plain copy print?
//使用@interface來聲明一個注解(實際上是自動繼承了java.lang.annotation.Annotation接口)
public @interface AnnotationTest {
String value1() default "hello"; //為注解設定String類型的屬性Value1,并使用defalut關鍵字設定預設值
EnumTest value2(); //設定枚舉類型的value2
String[] value3(); //設定數組類型的value3
}
如何來使用注解呢,如下:
@AnnotationTest(value2 = EnumTest.age, value3={""})
public class AnnotationUsage {
@AnnotationTest(value1 = "Test", value2 = EnumTest.name, value3={""})
String test;
public void method(){
System.out.println("usage of Annotation");
}
如上,注解可以标注在屬性,方法,類上。
需要使用name=value這種指派方式為屬性指派,因為value1設定了預設屬性,是以可以忽略,如果沒有設定預設值則所有的屬性都要一一指派。
還有一種特殊情況,如果注解裡隻定義了一個屬性,名字是value,那麼可以直接指派,不需要使用name=value這種指派方式,如下:
String value();
@AnnotationTest("test")
public void method(){
System.out.println("usage of Annotation");
修飾注解的“注解”
注解也可以添加注解的“注解”去修飾,常用的有以下兩個,一個是Retention,一個Target
Retention:
使用Retention必須要提供一個為java.lang.annotation.RetentionPolicy類型的的枚舉
RetentionPolicy枚舉有以下3個類型:
SOURCE:編譯程式時處理完Annotation資訊後就完成任務
CLASS:編譯程式将Annotation存儲于class檔案中,不可以由虛拟機讀入
RUNTIME:編譯程式将Annotation存儲于class檔案中,可以由虛拟機讀入
用這三種Retention的Prolicy可以決定注解是從源檔案,class檔案或者以在運作時反射被讀取
關于Retention的例子在最後
Target:
使用java.lang.annotation.Target可以定義注解被使用的位置
同樣,在使用時要指定一個java.lang.annotation.ElementType的枚舉值類型為他的“屬性”
ElementType枚舉的類型如下:
ANNOTATION_TYPE:适用于annotation
CONSTRUCTOR:适用于構造方法
FIELD:适用于field
LOCAL_VARIABLE:适用于局部變量
METHOD:适用于方法
PACKAGE:适用于package
PARAMETER:适用于method上的parameter
TYPE:适用于class,interface,enum
如下:定義一個注解MyTarget,設定Target類型為Method來修飾這個注解,這樣這個注解隻能标注在method的方法上
@Target(ElementType.METHOD)
public @interface MyTarget {
String hello() default "hello";
@MyTarget //這裡則會報錯,因為他标注在類上面了
public class MyTargetTest {
@MyTarget //标注在方法上不會報錯
public void doSomething(){
System.out.println("hello world");
使用反射調用注解
在以下的類中Class Constructor Field Method Package等類都實作了AnnotatedElement接口
在接口中有以下重要的方法:
getAnnotations(Class annotationType)擷取一個指定的annotation類型
getAnnotations() 擷取所有的Annotation
getDeclaredAnnotations() 擷取聲明過的所有Annotation
isAnnotationPresent(Class<? extends Annotation> annotationClass)這個annotation是否出現
通過這些方法,配合反射我們就可以在程式運作時拿到注解的内容了,例子如下:
@Retention(RetentionPolicy.RUNTIME) //定義一個注解,使用Retention标注為RUNTIME
public @interface MyAnnotation {
String hello() default "hello";
String world();
該注解被标示為runtime類型,表示該注解最後可以儲存在class檔案中,并為java虛拟機在運作時讀取到
@Retention(RetentionPolicy.CLASS) //定義一個注解,Retention标注為RUNTIME
public @interface MyAnnotation2 {
String hello() default "hello"; //設定預設值為hello
自定義的另一個注解Retention标示為class
public class MyTest {
@SuppressWarnings("unchecked") //java自帶的注解Retention的policy為SOURCE
@Deprecated //java自帶的注解Retention的policy為RUNTIME
@MyAnnotation(Name="Dean", Age="25") //自定義的注解Retention的policy為RUNTIME
@MyAnnotation2 //自定義的注解Retention的policy為CLASS
public void TestMethod() {
System.out.println("this is a method");
定義一個TestMethod方法,給他标示了4個注解,其中2個java自帶的,2個我們自定義的。注解的的Retention屬性各不相同。
下面定義一個測試類來驗證結果:
public static void main(String[] args) throws Exception {
MyTest myTest = new MyTest();
//通過反射得到TestMethod方法
Class<MyTest> c = MyTest.class;
Method method = c.getMethod("TestMethod", new Class[]{});
//AnnotatedElement接口中的方法isAnnotationPresent(),判斷傳入的注解類型是否存在
if (method.isAnnotationPresent(MyAnnotation.class)) {
method.invoke(myTest, new Object[]{});
//AnnotatedElement接口中的方法getAnnotation(),擷取傳入注解類型的注解
MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
//拿到注解中的屬性
String name = myAnnotation.Name();
String age = myAnnotation.Age();
System.out.println("name:"+name +" age:"+age);
}
System.out.println("-----------------------------------");
//AnnotatedElement接口中的方法getAnnotations(),擷取所有注解
Annotation[] annotations = method.getAnnotations();
//循環注解數組列印出注解類型的名字
for (Annotation annotation : annotations) {
System.out.println(annotation.annotationType().getName());
列印結果為:
this is a method
name:Dean age:25
-----------------------------------
java.lang.Deprecated
gxy.text.annotation.MyAnnotation
分割線上:介紹了如何使用AnnotatedElement接口中的方法和反射去調用注解
分割線下:證明了隻有定義了Retention的Policy為Runtime的注解才可以被反射讀取出來
下一篇文章分析一下在JUnit中反射與注解的使用和原理