一天寫一點,天天學一點,今天來扒拉扒拉java的反射以及自定義注解的事兒,其實就是前面做項目的時候自定義了一個注解,然后在struts2的攔截器里面通過反射的方式獲取到注解的內容,通過注解的內容來進行權限判斷,就是這么個事兒,反射是個很XX(此處省略兩個字)的問題,用的也多,但是學著卻很糾結,會的也不多,就隨便寫點我會的,還有自定義注解,也是那么回事,下面就簡單記錄一下吧,省的以后忘了還得去查資料,說了一堆廢話,下面開始。
二、自定義注解2.1 元注解要自定義注解就得知道元注解,什么是元注解呢?元注解就是注解其他注解的注解,說的比較繞,其實就是在定義其他注解的時候,需要用到的注解就是元注解。Java 5.0定義了4個元注解,用來對其他的注解類型進行說明,這4個元注解分別是:@Target,@Retention,@Documented,@Inherited,在API文檔中的位置如下所示:

指示注解修飾的對象范圍,如果注釋類型聲明中不存在 Target 元注釋,則聲明的類型可以用在任一程序元素上;如果存在Target 元注釋,那么就要標明作用范圍,參數為一個數組,數組元素為ElementType的屬性值。
ElementType的屬性值主要有以下幾種:
1.CONSTRUCTOR:用于描述構造器;
2..FIELD:用于描述域;
3.LOCAL_VARIABLE:用于描述局部變量
4.METHOD:用于描述方法
5.PACKAGE:用于描述包
6.PARAMETER:用于描述參數
7.TYPE:用于描述類、接口(包括注解類型) 或enum聲明
下面我們定義一個作用在方法上的注解:
@Target({ElementType.METHOD})public @interface Exam { String name() default ""; int age() default 0; }2.1.2@Retention指示注釋類型的注釋要保留多久。如果注釋類型聲明中不存在 Retention 注釋,則保留策略默認為 RetentionPolicy.CLASS。 有些注釋出現在源代碼中,有些注釋出現在class文件中,有些注釋則出現在class被裝載時被讀取,主要的就這三個,Retention注釋的參數也有三種,對應源碼、class文件和運行時,表示需要在什么級別保存該注釋信息,用于描述注解的聲明周期,取值如下:
1.SOURCE:在源文件中有效(即源文件保留)
2.CLASS:在class文件中有效(即class保留)
3.RUNTIME:在運行時有效(即運行時保留)
例子:
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface Exam { String name() default ""; int age() default 0; }2.1.3@Documented指示某一類型的注釋將通過javadoc和類似的默認工具進行文檔化。應使用此類型來注釋這些類型的聲明:其注釋會影響由其客戶端注釋的元素的使用。如果類型聲明是由@Documented來注釋的,則其注釋將成為注釋元素的公共API的一部分。
上面是JDK文檔上的解釋,什么意思呢?我的理解是使用@Documented注解的注解應該被javadoc工具記錄,默認情況下javadoc是不會記錄注解的,但是聲明注解時如果指定了@Documented,那么這個注解就會被javadoc處理,所以注解類型信息也會包括在生成的文檔中。如果注解信息不需要包括在文檔中,那么就可以不加這個注解。使用方式如下:
@Retention(RetentionPolicy.RUNTIME)@Documented@Target({ElementType.METHOD})
public @interface Exam { String name() default ""; int age() default 0; }2.1.4@Inherited指示注釋類型被自動繼承,如果在注釋類型聲明中存在@Inherited元注釋,并且用戶在某一類聲明中查詢該注釋類型,同時該類聲明中沒有此類型的注釋,則將在該類的超類中自動查詢該注釋類型。此過程會重復進行,直到找到此類型的注釋或到達了該類層次結構的頂層(Object)為止,如果沒有超類具有該類型的注釋,則查詢將指示當前類沒有這樣的注釋。注意,如果使用注釋類型注釋類以的任何事物,此元注釋類型是無效的,還要注意,此元注釋僅促成從超類繼承注釋,對已實現接口的注釋無效。
上述呢是文檔中的解釋,我自己說也說不太清楚,我直接舉個例子來說可能更容易理解:
首先定義一個自定義注解,上面沒有說定義注解時的注意事項,在這還是寫一下吧:
注意:在使用@interface自定義注解時,自動繼承了java.lang.annotation.Annotation接口,編譯程序會自動處理具體的細節。在定義注解時,不可以再繼承其他的注解或者接口。
注解的定義格式: public @interface 注解名稱 { 注解的參數 }
注解的參數主要有一下幾種類型:1.八中基本數據類型;
2.String類型;Class類型;enum類型;Annotations類型;
3.以上類型的數組;
注解的參數設定:
只能用public或默認(default)這兩個訪問權修飾.例如,String value();這里把方法設為defaul默認類型;
參數成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數據類型和 String,Enum,Class,annotations等數據類型,以及這一些類型的數組.例如,String value();這里的參數成員就為String;
如果只有一個參數成員,最好把參數名稱設為"value";
說完了定義注解的注意事項,下面繼續說@Inherited的事情,先定義一個注解:
@Inherited@Documented@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE})public @interface Exam { String name() default ""; int age() default 0;}注意紅色的部分,具體原因下面會說到。然后是一個父類:
@Exam(name="張三",age=10)public class User { public static String getUserAddress(){ String address = "北京市"; return address; } }再來個子類,子類中不加注解:
public class Son extends User{ public static int getAge(){ int age = 20; return age; }}看測試類和測試結果:
public static void main(String[] args) { Class<?> cla = null; try { cla = Class.forName("modal.Son"); boolean flag = cla.isAnnotationPResent(Exam.class); System.out.println("flag==="+flag); if(flag){ Exam exam = cla.getAnnotation(Exam.class); String name = exam.name(); System.out.println(name); Integer age = exam.age(); System.out.println(age); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } }測試結果:
flag===true張三10
測試類中使用反射的方式拿到子類,然后判斷子類中是否有注解,子類中我們是沒有寫注解的,但是isAnnotationPresent方法卻返回了true,我們取到注解的值后發現這個注解的值是父類的注解的值,這就是@Inherited元注解的作用,就是子類可以繼承父類的注解,當我把@Inherited注解去掉之后isAnnotationPresent返回false。
上邊留的紅字部分沒有解釋,什么原因呢?這是因為我在測試的發現只有把注解放在類級別上才可以讓子類繼承,如果放到方法上是不能繼承的,因此我用了@Target({ElementType.TYPE})注解,而沒有用@Target({ElementType.METHOD})注解。
自定義注解部分暫時就說到這,本來想寫反射的,但是又感覺無從下手,暫時就先不寫了,再研究研究,等熟練了在寫,不然寫出來的東西我都不知道什么意思,以后看的時候也是浪費時間。
新聞熱點
疑難解答