註解&反射註解

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章