Java反射:使用Annotation功能
Java中提供了Annotaion(註釋)功能,該功能可用於類、構造方法、成員變數、方法、引數等的宣告中。該功能並不影響程式的執行,但是會對編譯器警告等輔助工具產生影響。
1、定義Annotation型別
在定義Annotation型別時,也需要用到用來定義介面的interface關鍵字,不過需要在interface關鍵字前加一個“@”符號,即定義Annotation型別的關鍵字為@interface,這個關鍵字的隱含意思是繼承了java.lang.annotation.Annotation介面。例如,下面的程式碼就是定義一個Annotation型別。
public @interface NoMemberAnnotation{ }
下面定義一個包含一個成員的Annotation型別。
public @interface OneMemberAnnotation{
String value();
}
String:成員型別。
value:成員名稱。如果在所定義的Annotation型別中只包含一個成員,通常將成員名稱命名為value。
下面定義一個包含多個成員的Annotation型別。
public @interface MoreMemberAnnotation
{
String describe();
Class type();
}
在為Annotation型別定義成員是,也可以為成員設定預設值。下面程式碼在定義Annotation型別時就為成員設定了預設值。
public @interface DefaultValueAnnotation
{
String describe() default "預設值";
Class type() default void.class;
}
在定義Annotation型別時,還可以通過Annotation型別@Target來設定Annotation型別適用的程式元素種類。如果未設定@Target,則表示適用於所有程式元素。列舉類ElementType中的列舉常量用來設定@Target。
列舉類ElementType中的列舉常量:
列舉常量 | 說明 |
---|---|
ANNOTATION_TYPE | 表示用於Annotation型別 |
TYPE | 表示用於類、介面和列舉,以及Annotation型別 |
CONSTRUCTOR | 表示用於構造方法 |
FIELD | 表示用於成員變數和列舉常量 |
METHOD | 表示用於方法 |
PARAMETER | 表示用於引數 |
LOCAL_VARIABLE | 表示用於區域性變數 |
PACKAGE | 表示用於包 |
通過Annotation型別@Retention可以設定Annotation型別的有效範圍。列舉類RetentionPolicy中的列舉常量用來設定@Retention。如果未設定@Retention,Annotation的有效範圍為列舉常量CLASS表示的範圍。
列舉類RetentionPolicy中的列舉常量:
列舉常量 | 說明 |
---|---|
SOURCE | 表示不編譯Annotation型別到類檔案中,有效範圍最小 |
CLASS | 表示編譯Annotation到類檔案中,但是在執行時不載入Annotation到JVM中 |
RUNTIME | 表示在執行時載入Annotation到JVM中,有效範圍最大 |
示例:定義並使用Annotation型別。
首先定義一個用來註釋構造方法的Annotation型別@Constructor_Annotation,有效範圍為在執行時載入Annotation到JVM中。
import java.lang.annotation.*;
//用於構成方法
@Target(ElementType.CONSTRUCTOR)
//在執行時載入Annotation到JVM
@Retention(RetentionPolicy.RUNTIME)
public @interface Constructor_Annotation
{
// 定義一個具有預設值的String型成員
String value() default "預設構造方法";
}
然後定義一個用來註釋欄位、方法和引數的Annotation型別@Field_Method_Parameter_Annotation,有效範圍為在執行時載入Annotation到JVM中。
import java.lang.annotation.*;
//用於欄位、方法和引數
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
//在執行時載入Annotation到JVM中
@Retention(RetentionPolicy.RUNTIME)
public @interface Field_Method_Parameter_Annotation
{
String describe(); // 定義一個沒有預設值的String型別成員
Class type() default void.class; // 定義一個具有預設值的Class型別的成員
}
最後編寫一個Record類,在該類中執行前面定義的Annotation型別對構造方法、欄位、方法和引數進行註釋。
public class Record
{
// 註釋欄位
@Field_Method_Parameter_Annotation(describe = "編號", type = int.class)
int id;
@Field_Method_Parameter_Annotation(describe = "姓名", type = String.class)
String name;
// 採用預設值註釋構造方法
@Constructor_Annotation()
public Record()
{
}
// 註釋構造方法
@Constructor_Annotation("立即初始化構造方法")
public Record(@Field_Method_Parameter_Annotation(describe = "編號", type = int.class) int id,
@Field_Method_Parameter_Annotation(describe = "姓名", type = String.class) String name)
{
this.id = id;
this.name = name;
}
// 註釋方法
@Field_Method_Parameter_Annotation(describe = "獲取編號", type = int.class)
public int getId()
{
return id;
}
@Field_Method_Parameter_Annotation(describe = "設定編號", type = int.class)
public void setId(@Field_Method_Parameter_Annotation(describe = "編號", type = int.class) int id)
{
this.id = id;
}
@Field_Method_Parameter_Annotation(describe = "獲取姓名", type = String.class)
public int getName()
{
return id;
}
@Field_Method_Parameter_Annotation(describe = "設定姓名", type = String.class)
public void setName(@Field_Method_Parameter_Annotation(describe = "姓名", type = String.class) String name)
{
this.name = name;
}
}
2、訪問Annotation資訊
如果在定義Annotation型別時將@Retention設定為RetentionPolicy.RUNTIME,那麼在執行程式時通過反射就可以獲取到相關的Annotation資訊,如獲取構造方法、欄位和方法的Annotation資訊。
類Constructor、Field和Method均繼承了AccessibleObject類,在AccessibleObject中定義了3個關於Annotation的方法,其中方法isAnnotationPresent(Class<? extends Annotation> annotationClass)用來檢視是否添加了指定型別的Annotation,如果是則返回true,否則返回false;方法getAnnotation(Class<T> annotationClass)用來獲得指定型別的Annotation,如果存在則返回相應的物件,否則返回null;方法getAnnotations()用來獲得所有的Annotation,該方法將返回一個Annotation陣列。
在類Constructor和Method中還定義了方法getParameterAnnotations(),用來獲得為所有引數新增的Annotation,將以Annotation型別的二維陣列返回,在陣列中的順序與宣告的順序相同,如果沒有引數則返回一個長度為0的陣列;如果存在未新增Annotation的引數,將用一個長度為0的巢狀陣列佔位。
示例:訪問Annotation資訊。
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
public class AnnotationTest
{
public static void main(String[] args)
{
Class recordC = null;
try
{
recordC = Class.forName("com.helloJava.reflect.Record");
} catch (ClassNotFoundException e)
{
e.printStackTrace();
}
System.out.println("------ 構造方法的描述如下 ------");
Constructor[] declaredConstructors = recordC.getDeclaredConstructors(); // 獲得所有構造方法
for (int i = 0; i < declaredConstructors.length; i++)
{
Constructor constructor = declaredConstructors[i]; // 遍歷構造方法
// 檢視是否具有指定型別的註釋
if (constructor.isAnnotationPresent(Constructor_Annotation.class))
{
// 獲得指定型別的註釋
Constructor_Annotation ca = (Constructor_Annotation) constructor
.getAnnotation(Constructor_Annotation.class);
System.out.println(ca.value()); // 獲得註釋資訊
}
Annotation[][] parameterAnnotations = constructor.getParameterAnnotations(); // 獲得引數的註釋
for (int j = 0; j < parameterAnnotations.length; j++)
{
// 獲得指定引數註釋的長度
int length = parameterAnnotations[j].length;
if (length == 0) // 如果長度為0則表示沒有為該引數添加註釋
System.out.println(" 未新增Annotation的引數");
else
for (int k = 0; k < length; k++)
{
// 獲得引數的註釋
Field_Method_Parameter_Annotation pa = (Field_Method_Parameter_Annotation) parameterAnnotations[j][k];
System.out.print(" " + pa.describe()); // 獲得引數描述
System.out.println(" " + pa.type()); // 獲得引數型別
}
}
System.out.println();
}
System.out.println();
System.out.println("-------- 欄位的描述如下 --------");
Field[] declaredFields = recordC.getDeclaredFields(); // 獲得所有欄位
for (int i = 0; i < declaredFields.length; i++)
{
Field field = declaredFields[i]; // 遍歷欄位
// 檢視是否具有指定型別的註釋
if (field.isAnnotationPresent(Field_Method_Parameter_Annotation.class))
{
// 獲得指定型別的註釋
Field_Method_Parameter_Annotation fa = field.getAnnotation(Field_Method_Parameter_Annotation.class);
System.out.print(" " + fa.describe()); // 獲得欄位的描述
System.out.println(" " + fa.type()); // 獲得欄位的型別
}
}
System.out.println();
System.out.println("-------- 方法的描述如下 --------");
Method[] methods = recordC.getDeclaredMethods(); // 獲得所有方法
for (int i = 0; i < methods.length; i++)
{
Method method = methods[i]; // 遍歷方法
// 檢視是否具有指定型別的註釋
if (method.isAnnotationPresent(Field_Method_Parameter_Annotation.class))
{
// 獲得指定型別的註釋
Field_Method_Parameter_Annotation ma = method.getAnnotation(Field_Method_Parameter_Annotation.class);
System.out.println(ma.describe()); // 獲得方法的描述
System.out.println(ma.type()); // 獲得方法的返回值型別
}
Annotation[][] parameterAnnotations = method.getParameterAnnotations(); // 獲得引數的註釋
for (int j = 0; j < parameterAnnotations.length; j++)
{
int length = parameterAnnotations[j].length; // 獲得指定引數註釋的長度
if (length == 0) // 如果長度為0表示沒有為該引數添加註釋
System.out.println(" 未新增Annotation的引數");
else
for (int k = 0; k < length; k++)
{
// 獲得指定型別的註釋
Field_Method_Parameter_Annotation pa = (Field_Method_Parameter_Annotation) parameterAnnotations[j][k];
System.out.print(" " + pa.describe()); // 獲得引數的描述
System.out.println(" " + pa.type()); // 獲得引數的型別
}
}
System.out.println();
}
}
}
分享一篇很好的學習資料:Java基礎加強總結(一)——註解(Annotation)