JAVA反射_註解
Java註解,從名字上看是註釋,解釋。但功能卻不僅僅是註釋那麼簡單。
註解(Annotation)即元資料,就是原始碼的元資料
註解(Annotation)為我們在程式碼中新增資訊提供了一種形式化的方法,使我們可以在稍後某個時刻方便地使用這些資料(通過解析註解來使用這些資料).
註解(Annotation)是一種應用於類、方法、引數、變數、構造器及包宣告中的特殊修飾符。它是一種由JSR-175標準選擇用來描述元資料的一種工具。
1.作用
* 生成文件 * 跟蹤程式碼依賴性,實現替代配置檔案功能,減少配置。如Spring中的一些註解 * 在編譯時進行格式檢查,如@Override等 * 每當你建立描述符性質的類或者介面時,一旦其中包含重複性的工作,就可以考慮使用註解來簡化與自動化該過程。
包java.lang.annotation中包含所有定義自定義註解所需用到的原註解和介面。如介面java.lang.annotation.Annotation是所有註解繼承的介面,並且是自動繼承,不需要定義時指定,類似於所有類都自動繼承Object。
該包同時定義了四個元註解,Documented,Inherited,Target(作用範圍,方法,屬性,構造方法等),Retention(生命範圍,原始碼,class,runtime)。
(元註解:自定義註解的時候用到的,也就是自定義註解的註解,也就是自定義註解的註釋,解釋)
@Target
作用:用於描述註解的使用範圍(即:被描述的註解可以用在什麼地方)@Target說明了所修飾註解的範圍:該註解可被用於 packages、types(類、介面、列舉、Annotation型別)、 型別成員(方法、構造方法、成員變數、列舉值)、方法引數和本地變數(如迴圈變數、catch引數)。 在Annotation型別的宣告中使用了target可更加明晰其修飾的目標。
取值(ElementType)有:
型別 | 用途 |
CONSTRUCTOR | 用於描述構造器 |
FIELD | 用於描述域 |
LOCAL_VARIABLE | 用於描述區域性變數 |
METHOD | 用於描述方法 |
PACKAGE | 用於描述包 |
PARAMETER | 用於描述引數 |
TYPE | 用於描述類、介面 或enum宣告 |
@Retention定義了該Annotation被保留的時間長短:某些Annotation僅出現在原始碼中,而被編譯器丟棄;
而另一些卻被編譯在class檔案中;編譯在class檔案中的Annotation可能會被虛擬機器忽略,
而另一些在class被裝載時將被讀取(請注意並不影響class的執行,因為Annotation與class在使用上是被分離的)。
使用這個meta-Annotation可以對 Annotation的“生命週期”限制。
作用:表示需要在什麼級別儲存該註釋資訊,用於描述註解的生命週期(即:被描述的註解在什麼範圍內有效)
取值(RetentionPoicy)有:
型別 | 用途 | 說明 |
SOURCE | 在原始檔中有效 | 僅出現在原始碼中,而被編譯器丟棄 |
CLASS | 在原始檔中有效 | 被編譯在class檔案中 |
RUNTIME | 在執行時有效 | 編譯在class檔案中 |
@Documented
作用:將註解包含在javadoc中,它代表著此註解會被javadoc工具提取成文件。
@Inherited
自定義註解
格式:
public @interface 註解名{
定義體
}
註解引數的可支援資料型別:
所有基本資料型別(int,float,double,boolean,byte,char,long,short)
String 型別
Class型別
enum型別
Annotation型別
以上所有型別的陣列
規則:
* 修飾符只能是public 或預設(default)
* 引數成員只能用基本型別byte,short,int,long,float,double,boolean八種基本型別和
* String,Enum,Class,annotations及這些型別的陣列
* 如果只有一個引數成員,最好將名稱設為”value”
* 註解元素必須有確定的值,可以在註解中定義預設值,也可以使用註解時指定,非基本型別的值不可為null,常* 使用空字串或0作預設值
* 在表現一個元素存在或缺失的狀態時,定義一下特殊值來表示,如空字串或負值
最簡單的例子:
public @interface TestA {
//這裡定義了一個空的註解,它能幹什麼呢。我也不知道,但它能用。
}
在下面這個程式使用它:
@TestA //使用了類註解
public class UserAnnotation {
@TestA //使用了類成員註解
private Integer age;
@TestA //使用了構造方法註解
public UserAnnotation(){
}
@TestA //使用了類方法註解
public void a(){
@TestA //使用了局部變數註解
Map m = new HashMap(0);
}
public void b(@TestA Integer a){ //使用了方法引數註解
}
}
這個註解也太簡單了吧,好像什麼資訊也不能傳遞。別急下面就來一步步完善它,也該四位元註解依次開始上場了。
@Target(ElementType.TYPE)
public @interface TestA {
}
測試類那邊立馬出現了一堆錯誤,除了類註解。我想到這,聰明的你立刻明白了這個元註解的意義了。是不是想當然的偷起懶來了?難道還有意外?細心的朋友應該發現了,我們的測試類少了一個屬性沒用,就是ElemenetType.PACKAGE。在我們的註解加上這個屬性的元註解後,我們測試程式的元註解全部陣亡,不對,還有一個沒加呢,好加上。package 包,想當然是載入 package 前面。即
@TestA package com.king.annotation;
什麼,也報錯?這就搞不明白了,不加在這加哪去呢。我也不知道了,不過這是編譯錯誤,我們的IDE將錯誤給我們指出了,就是
Package annotations must be in file package-info.java ,E文雖然不好,但這個簡單的還是難不倒的,package註解必須定義在package-info.java中。package-info 又是什麼東西,百度..。
OK,到此,@Target元註解就全部完成了。
簡單演示下如何使用:
@Target(ElementType.PACKAGE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestA {
}
第三和第四個元註解就不再舉例了。
下面我們還是繼續來深入的探討下註解的使用。上面的例子都非常簡單,註解連屬性都沒有。
OK,下面我們就來定義一個有屬性的註解,並在例子程式中獲取到註解中定義的值。
開始之前講下定義屬性的規則:
@interface用來宣告一個註解,其中的每一個方法實際上是聲明瞭一個配置引數。
方法的名稱就是引數的名稱,返回值型別就是引數的型別(參考上面)。
可以通過default來宣告引數的預設值。
程式碼:
/*
* 自定義註解 TestA
* 為方便測試:註解目標為類方法,屬性及構造方法
* 註解中含有三個元素 id ,name和 gid;
* id元素 有預設值 0
*/
@Target({TYPE,METHOD,FIELD,CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestA {
String name();
int id() default 0;
Class<Long> gid();
}
下面改下我們的測試類:
@TestA(name="type",gid=Long.class) //類成員註解
public class UserAnnotation {
@TestA(name="param",id=1,gid=Long.class) //類成員註解
private Integer age;
@TestA (name="construct",id=2,gid=Long.class)//構造方法註解
public UserAnnotation(){
}
@TestA(name="public method",id=3,gid=Long.class) //類方法註解
public void a(){
Map<String,String> m = new HashMap<String,String>(0);
}
@TestA(name="protected method",id=4,gid=Long.class) //類方法註解
protected void b(){
Map<String,String> m = new HashMap<String,String>(0);
}
@TestA(name="private method",id=5,gid=Long.class) //類方法註解
private void c(){
Map<String,String> m = new HashMap<String,String>(0);
}
public void b(Integer a){
}
}
JAVA既然增加了註解,肯定就增加了相關讀取的API。
在java.lang.reflect包中新增了AnnotatedElement介面,JDK原始碼如下:
public interface AnnotatedElement {
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
Annotation[] getAnnotations();
Annotation[] getDeclaredAnnotations();
}
isAnnotationPresent:判斷是否標註了指定註解
getAnnotation:獲取指定註解,沒有則返回null
getAnnotations:獲取所有註解,包括繼承自基類的,沒有則返回長度為0的陣列
getDeclaredAnnotations:獲取自身顯式標明的所有註解,沒有則返回長度為0的陣列
public class ParseAnnotation {
/**
* 簡單打印出UserAnnotation 類中所使用到的類註解
* 該方法只打印了 Type 型別的註解
* @throws ClassNotFoundException
*/
public static void parseTypeAnnotation() throws ClassNotFoundException {
Class clazz = Class.forName("com.tmser.annotation.UserAnnotation");
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
TestA testA = (TestA)annotation;
System.out.println("id= \""+testA.id()+"\"; name= \""+testA.name()+"\"; gid = "+testA.gid());
}
}
/**
* 簡單打印出UserAnnotation 類中所使用到的方法註解
* 該方法只打印了 Method 型別的註解
* @throws ClassNotFoundException
*/
public static void parseMethodAnnotation(){
Method[] methods = UserAnnotation.class.getDeclaredMethods();
for (Method method : methods) {
/*
* 判斷方法中是否有指定註解型別的註解
*/
boolean hasAnnotation = method.isAnnotationPresent(TestA.class);
if (hasAnnotation) {
/*
* 根據註解型別返回方法的指定型別註解
*/
TestA annotation = method.getAnnotation(TestA.class);
System.out.println("method = " + method.getName()
+ " ; id = " + annotation.id() + " ; description = "
+ annotation.name() + "; gid= "+annotation.gid());
}
}
}
/**
* 簡單打印出UserAnnotation 類中所使用到的方法註解
* 該方法只打印了 Method 型別的註解
* @throws ClassNotFoundException
*/
public static void parseConstructAnnotation(){
Constructor[] constructors = UserAnnotation.class.getConstructors();
for (Constructor constructor : constructors) {
/*
* 判斷構造方法中是否有指定註解型別的註解
*/
boolean hasAnnotation = constructor.isAnnotationPresent(TestA.class);
if (hasAnnotation) {
/*
* 根據註解型別返回方法的指定型別註解
*/
TestA annotation =(TestA) constructor.getAnnotation(TestA.class);
System.out.println("constructor = " + constructor.getName()
+ " ; id = " + annotation.id() + " ; description = "
+ annotation.name() + "; gid= "+annotation.gid());
}
}
}
public static void main(String[] args) throws ClassNotFoundException {
parseTypeAnnotation();
parseMethodAnnotation();
parseConstructAnnotation();
}
}