註解&反射註解
1. 註解的定義
public interface Annotation 是所有的註解的父接口
1.1 註解屬性的類型
- 8種基本類型 byte short char int long float double boolean
- String
- Enum
- Class
- 註解類型
- 以上類型的一維數組類型
1.2 定義格式
public @interface MyAnno {
int age() default 22;
String name();
MyEnum myEnum();
Class clazz();
YourAnno anno();
String[] value();
}
定義註解時,可以給註解屬性指定默認值,使用時可以不給帶有默認值的屬性賦值。
1.3 註解的使用
@MyAnno(
name = "XiaoMing",
myEnum = MyEnum.RED,
clazz = Object.class,
anno = @YourAnno,
value = "Hello")
public class AnnoTest {
}
- 當使用註解時,如果只給(只有value屬性)名爲value的屬性賦值時,可以省略“value=”,例如: @MyAnno1(value=“hello”),可以書寫成 @MyAnno1(“hello”)
- 當給數組類型的屬性賦值時,若數組元素的個數爲1時,可以省略大括號
所以有簡寫:
public @interface MyAnno {
String[] value();
}
@MyAnno("Hello")
public class AnnoTest {
}
2. 作用目標限定以及保存策略限定
2.1 作用目標限定
這代碼中有這些位置可以使用註解
- 接口、類、枚舉
- 註解
- 字段、枚舉常量
- 方法
- 構造器
- 參數
- 局部變量
- 包
那我們如何限定只有某幾個位置可以使用該註解呢?答案給註解添加 @Target 註解來限定作用目標
@Target源碼:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value(); // 枚舉數組
}
ElementType
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
// 類,接口(包含註解)或 枚舉
TYPE,
/** Field declaration (includes enum constants) */
// 字段,包括枚舉常量
FIELD,
/** Method declaration */
// 方法聲明
METHOD,
/** Formal parameter declaration */
// 參數聲明
PARAMETER,
/** Constructor declaration */
// 構造方法聲明
CONSTRUCTOR,
/** Local variable declaration */
// 局部變量聲明
LOCAL_VARIABLE,
/** Annotation type declaration */
// 註解類型
ANNOTATION_TYPE,
/** Package declaration */
// 包
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
// 泛型參數聲明
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
// 泛型類型
TYPE_USE
}
其中有兩個是jdk1.8新加的屬性
把自定義的註解限定在類或方法上
@Target(value = {ElementType.TYPE, ElementType.METHOD})
public @interface MyAnno {
int age() default 22;
String name();
MyEnum myEnum();
Class clazz();
YourAnno anno();
String[] value();
}
2.2 保存策略限定
- 源代碼文件(SOURCE):註解只在源代碼中存在,當編譯時就被忽略了
- 字節碼文件(CLASS):註解在源代碼中存在,然後編譯時會把註解信息放到了class文件,但JVM在加載類時,會忽略註解
- JVM中(RUNTIME):註解在源代碼、字節碼文件中存在,並且在JVM加載類時,會把註解加載到JVM內存中(它是唯一可反射註解!)
使用 @Retention 註解保留策略
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
RetentionPolicy
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
給自定義註解加保留策略
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE, ElementType.METHOD})
public @interface MyAnno {
int age() default 22;
String name();
MyEnum myEnum();
Class clazz();
YourAnno anno();
String[] value();
}
這樣就可以使用反射獲取註解的值了,這也是很多框架註解常用的定義方式。
3. 註解反射
要想反射,註解的保留策略必須是 RUNTIME
3.1 作用目標上返回
- 類上的註解,需要使用Class來獲取
- 方法上的註解,需要Method來獲取
- 構造器上的註解,需要Construcator來獲取
- 成員上的,需要使用Field來獲取
- …
3.2 獲取方法:
- Annotation getAnnotation(Class),返回目標上指定類型的註解
- Annotation[] getAnnotations(),返回目標上所有註解
public class AnnoTestTest {
@Test
public void testAnnoReflect() {
Class<AnnoTest> clazz = AnnoTest.class;
MyAnno myAnno = clazz.getAnnotation(MyAnno.class);
System.out.println("age: " + myAnno.age());
System.out.println("name: " + myAnno.name());
System.out.println("myEnum: " + myAnno.myEnum());
System.out.println("clazz: " + myAnno.clazz());
System.out.println("myAnno: " + myAnno.anno());
System.out.println("value: " + Arrays.toString(myAnno.value()));
System.out.println("--------------------------------------");
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof MyAnno) {
System.out.println("是自定義註解MyAnno");
}
}
}
}
測試結果:
age: 22
name: XiaoMing
myEnum: RED
clazz: class java.lang.Object
myAnno: @com.yp.anno.YourAnno()
value: [Hello]
--------------------------------------
是自定義註解MyAnno