天天看點

Java反射學習總結五(Annotation(注解)-基礎篇)

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中反射與注解的使用和原理