1. 程式人生 > >Java反射:使用Annotation功能

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)