注解学习完整篇(Annotation learning complete chapter)


import java.lang.annotation.*;
import java.util.Arrays;

/**
 * 注解
 * 我个人认为其实就是接口,从接口变过来的,只不过他不可以定义方法,
 * 通过@interface限制了它包含了它独有的语法
 *
 * 一 要点
 *  1 注解属于静态类,其中内部类属于静态类
 *  2 默认继承了java.lang.annotation.Annotation接口,不可以继承其他接口(类似与枚举类默认继承了Enum类一样)
 *  3 成员数量(2个) 成员变量和静态内部类都是public修饰,没有构造器,代码块,方法
 *  4 创建注解对象,访问注解成员变量
 *      注解的实例不可通过new的方式,也不可以过Class对象的newInstance()的方式去获取只能通过Class对象的如下方法获取
 *                A	getAnnotation(Class<A> annotationClass)  获得多个重复注解注解对象时将报错
 *     Annotation[]	getAnnotations() 获取该类所有的注解(包括来自父类)
 *                A	getDeclaredAnnotation(Class<A> annotationClass) 获得多个重复注解注解对象时将报错
 *     Annotation[]	getDeclaredAnnotations()  返回多个类型的注解,只能是Annotation[]
 *              A[]	getAnnotationsByType(Class<A> annotationClass) 获取多个相同注解对象,可来自继承
 *              A[]	getDeclaredAnnotationsByType(Class<A> annotationClass) 取多个相同注解对象,完全来自本类的声明
 *   5 getAnnotation和getDeclaredAnnotation访问不存在的注解将报错
 二 注解的定义
 1  @Target 注解修饰,指定该注解可修饰的程序元素类型
 2  @Retention 指定该注解的生命周期
 3  default指定默认值
 4  有且一个成员变量名为value时,可如此使用@Anno("tom")
 5  当成员变量name的类型为数组,但给赋一个这样的数组元素时{"tom"},可如此@Anno(name = "tom")

 三 其他三个元注解可添加如下注解:
     @Documented 文档元注解
     @Inherited 所修饰的注解只能类继承,不可通过接口继承,子类的@Xxx注解会覆盖父类的@Xxx注解信息
     @Repeatable(修饰的子注解必须使用该注解指定父容器.class)

 ElementType枚举类有如下实例:(常用的程序元素: 包,注解,类,常量,构造器,形参,局部变量,)
 PACKAGE,ANNOTATION_TYPE,TYPE,FIELD,METHOD,CONSTRUCTOR,LOCAL_VARIABLE,PARAMETER,
  TYPE_USE(1.8),TYPE_PARAMETER(1.8),MODULE(9)

 RetentionPolicy枚举类有如下实例 SOURCE,CLASS,RUNTIME(生命周期从小到大)
 四  类型注解: 可以使用在任何地方出现的类型前面(TYPE_PARAMETER,TYPE_USE)
    1   创建对象
    2   类型转换
    3   使用implements实现接口,extends继承类
    4   throws 抛出异常
三 定义重复注解(Repeatable)
 1  定义子注解Hello和容器注解Hellos
 2  通过在子注解上生命@Repeatable(Hellos.class)并指定容器注解
 3 容器注解的生命周期必须大于等于子注解
 4 容器定义子容器数组时,必须指定value为变量名,子容器的上的注解和只能比容器注解多一个(@Repeatable)
 5 当多个重复注解修饰同一个元素时,应当获取容器注解对象content,通过content去访问多个重复注解
 (因为你无法通过getAnnotation(Class)和getDeclaredAnnotation(Class)来获取每个子注解对象
 因为重复注解只是表象,这多个重复注解其实会作为容器注解的数组对象存在,最后只存在编译后只存在容器注解)
 比如
 @Hello("a")
 @Hello("b")
 public class Test{}
 最后成了这样
 @Hellos({@Hello("a"),@Hello("b")})
 public class Test{}
 因此当出现重复注解后把它们合成一个注解,再去判断如何获取注解对象,就不会迷茫了

 */


//定义容器注解Hellos
@Inherited
@Documented
@Target( ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Hellos{
    Hello[] value();
}
//定义子注解Hello
@Documented
@Repeatable(Hellos.class)
@Target( ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface Hello {
    String value();
}

//定义注解World
@Inherited
@Target( ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface World{
    String value() default "World的默认信息";
}
@World("World-1")
@Hello("Hello-1")
class China  {}

@World("World-2")
@Hello("Hello-2")
@Hello("Hello-3")
@Hello("Hello-4")
public  class 注解  extends  China  {
    public static   void main(String[] args) throws  Exception{
/**
 * 现在对如下这六个方法进行测试,编写了6个对应的静态方法
 *                A    getAnnotation(Class<A> annotationClass)  获得多个重复注解注解对象时将报错
 *     Annotation[]    getAnnotations() 获取该类所有的注解(包括来自父类)
 *                A    getDeclaredAnnotation(Class<A> annotationClass) 获得多个重复注解注解对象时将报错
 *     Annotation[]    getDeclaredAnnotations()  返回多个类型的注解,只能是Annotation[]
 *              A[]    getAnnotationsByType(Class<A> annotationClass) 获取多个相同注解对象,可来自继承
 *              A[]    getDeclaredAnnotationsByType(Class<A> annotationClass) 取多个相同注解对象,完全来自本类的声明
 */
        //获取本类的Class对象,注意: forName要引用完整的包名
       Class clazz = Class.forName("com.china.school.oop.注解");

        //hello实例其实是父类China类的@Hello注解对象,这说明了@Hello注解继承性
        // 本类也声明了3个Hello注解,他们最终合并成Hellos注解对象(class文件里)
        //以后看到多个重复注解修饰同一个元素,就把他们想象成一个容器注解就不会乱。
        Hello hello = (Hello)clazz.getAnnotation(Hello.class);
        System.out.println(hello.value());// Hello-1

        Hellos hellos = (Hellos) clazz.getAnnotation(Hellos.class);
        System.out.println(Arrays.toString(hellos.value()));
        //本例可以很好的解释上面的疑惑,输出结果如下
        //[@com.china.school.oop.Hello(value="Hello-2"),
        // @com.china.school.oop.Hello(value="Hello-3"),
        // @com.china.school.oop.Hello(value="Hello-4")]

        World world = (World)clazz.getDeclaredAnnotation(World.class);
        System.out.println(world.value());
        //输出结果: World-2,获取本类声明的@World注解对象

        Annotation[] anns = clazz.getAnnotations();
        System.out.println(Arrays.toString(anns));
        //获取到了数组中包含三个对象,一个本类的@World对象,一个父类的@Hello对象
        //再一个本类的@Hellos容器对象
        //[@com.china.school.oop.World(value="World-2"),
        // @com.china.school.oop.Hello(value="Hello-1"),
        // @com.china.school.oop.Hellos(
        // value={@com.china.school.oop.Hello(value="Hello-2"),
        // @com.china.school.oop.Hello(value="Hello-3"),
        // @com.china.school.oop.Hello(value="Hello-4")})]

        Annotation[] anns2 = clazz.getDeclaredAnnotations();
        System.out.println(Arrays.toString(anns2));
        //获取到了数组中包含2个对象,一个本类的@World对象,再一个本类的@Hellos容器对象(它又包含了3个@Hello对象)
        //[@com.china.school.oop.World(value="World-2"),
        // @com.china.school.oop.Hellos(
        // value={@com.china.school.oop.Hello(value="Hello-2"),
        // @com.china.school.oop.Hello(value="Hello-3"),
        // @com.china.school.oop.Hello(value="Hello-4")})]

        Hello[] helloArr1 = (Hello[])clazz.getDeclaredAnnotationsByType(Hello.class);
        System.out.println(Arrays.toString(helloArr1));
        //该数组中都是本类的3个重复注解,虽然父类China中也声明了@Hello,但被子类的@Hello给覆盖了
        //如果@Hello注解的定义中去掉@Inherited注解,还是输出3个@Hello对象
        //也就是说获取重复注解永远都是计算本类的声明的个数
        //[@com.china.school.oop.Hello(value="Hello-2"),
        // @com.china.school.oop.Hello(value="Hello-3"),
        // @com.china.school.oop.Hello(value="Hello-4")]

        var anonymous = new 注解(){};
        Hello[] helloArr2 = (Hello[])anonymous.getClass().getAnnotationsByType(Hello.class);
        System.out.println(Arrays.toString(helloArr2));
        //先创建了一个匿名内部类对象anonymous,因为该匿名内部类肯定没有,也没办法声明@Hello注解
        // 通过这个anonymous的Class对象的getAnnotationsByType()获取父类的@Hello重复注解对象,结果如下
        //[@com.china.school.oop.Hello(value="Hello-2"),
        // @com.china.school.oop.Hello(value="Hello-3"),
        // @com.china.school.oop.Hello(value="Hello-4")]

    }
}

输出结果如下:  

Hello-1[@com.china.school.oop.Hello(value=”Hello-2″), @com.china.school.oop.Hello(value=”Hello-3″), @com.china.school.oop.Hello(value=”Hello-4″)]World-2[@com.china.school.oop.World(value=”World-2″), @com.china.school.oop.Hello(value=”Hello-1″), @com.china.school.oop.Hellos(value={@com.china.school.oop.Hello(value=”Hello-2″), @com.china.school.oop.Hello(value=”Hello-3″), @com.china.school.oop.Hello(value=”Hello-4″)})][@com.china.school.oop.World(value=”World-2″), @com.china.school.oop.Hellos(value={@com.china.school.oop.Hello(value=”Hello-2″), @com.china.school.oop.Hello(value=”Hello-3″), @com.china.school.oop.Hello(value=”Hello-4″)})][@com.china.school.oop.Hello(value=”Hello-2″), @com.china.school.oop.Hello(value=”Hello-3″), @com.china.school.oop.Hello(value=”Hello-4″)][@com.china.school.oop.Hello(value=”Hello-2″), @com.china.school.oop.Hello(value=”Hello-3″), @com.china.school.oop.Hello(value=”Hello-4″)]

————————

import java.lang.annotation.*;
import java.util.Arrays;

/**
 * 注解
 * 我个人认为其实就是接口,从接口变过来的,只不过他不可以定义方法,
 * 通过@interface限制了它包含了它独有的语法
 *
 * 一 要点
 *  1 注解属于静态类,其中内部类属于静态类
 *  2 默认继承了java.lang.annotation.Annotation接口,不可以继承其他接口(类似与枚举类默认继承了Enum类一样)
 *  3 成员数量(2个) 成员变量和静态内部类都是public修饰,没有构造器,代码块,方法
 *  4 创建注解对象,访问注解成员变量
 *      注解的实例不可通过new的方式,也不可以过Class对象的newInstance()的方式去获取只能通过Class对象的如下方法获取
 *                A	getAnnotation(Class<A> annotationClass)  获得多个重复注解注解对象时将报错
 *     Annotation[]	getAnnotations() 获取该类所有的注解(包括来自父类)
 *                A	getDeclaredAnnotation(Class<A> annotationClass) 获得多个重复注解注解对象时将报错
 *     Annotation[]	getDeclaredAnnotations()  返回多个类型的注解,只能是Annotation[]
 *              A[]	getAnnotationsByType(Class<A> annotationClass) 获取多个相同注解对象,可来自继承
 *              A[]	getDeclaredAnnotationsByType(Class<A> annotationClass) 取多个相同注解对象,完全来自本类的声明
 *   5 getAnnotation和getDeclaredAnnotation访问不存在的注解将报错
 二 注解的定义
 1  @Target 注解修饰,指定该注解可修饰的程序元素类型
 2  @Retention 指定该注解的生命周期
 3  default指定默认值
 4  有且一个成员变量名为value时,可如此使用@Anno("tom")
 5  当成员变量name的类型为数组,但给赋一个这样的数组元素时{"tom"},可如此@Anno(name = "tom")

 三 其他三个元注解可添加如下注解:
     @Documented 文档元注解
     @Inherited 所修饰的注解只能类继承,不可通过接口继承,子类的@Xxx注解会覆盖父类的@Xxx注解信息
     @Repeatable(修饰的子注解必须使用该注解指定父容器.class)

 ElementType枚举类有如下实例:(常用的程序元素: 包,注解,类,常量,构造器,形参,局部变量,)
 PACKAGE,ANNOTATION_TYPE,TYPE,FIELD,METHOD,CONSTRUCTOR,LOCAL_VARIABLE,PARAMETER,
  TYPE_USE(1.8),TYPE_PARAMETER(1.8),MODULE(9)

 RetentionPolicy枚举类有如下实例 SOURCE,CLASS,RUNTIME(生命周期从小到大)
 四  类型注解: 可以使用在任何地方出现的类型前面(TYPE_PARAMETER,TYPE_USE)
    1   创建对象
    2   类型转换
    3   使用implements实现接口,extends继承类
    4   throws 抛出异常
三 定义重复注解(Repeatable)
 1  定义子注解Hello和容器注解Hellos
 2  通过在子注解上生命@Repeatable(Hellos.class)并指定容器注解
 3 容器注解的生命周期必须大于等于子注解
 4 容器定义子容器数组时,必须指定value为变量名,子容器的上的注解和只能比容器注解多一个(@Repeatable)
 5 当多个重复注解修饰同一个元素时,应当获取容器注解对象content,通过content去访问多个重复注解
 (因为你无法通过getAnnotation(Class)和getDeclaredAnnotation(Class)来获取每个子注解对象
 因为重复注解只是表象,这多个重复注解其实会作为容器注解的数组对象存在,最后只存在编译后只存在容器注解)
 比如
 @Hello("a")
 @Hello("b")
 public class Test{}
 最后成了这样
 @Hellos({@Hello("a"),@Hello("b")})
 public class Test{}
 因此当出现重复注解后把它们合成一个注解,再去判断如何获取注解对象,就不会迷茫了

 */


//定义容器注解Hellos
@Inherited
@Documented
@Target( ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Hellos{
    Hello[] value();
}
//定义子注解Hello
@Documented
@Repeatable(Hellos.class)
@Target( ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface Hello {
    String value();
}

//定义注解World
@Inherited
@Target( ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface World{
    String value() default "World的默认信息";
}
@World("World-1")
@Hello("Hello-1")
class China  {}

@World("World-2")
@Hello("Hello-2")
@Hello("Hello-3")
@Hello("Hello-4")
public  class 注解  extends  China  {
    public static   void main(String[] args) throws  Exception{
/**
 * 现在对如下这六个方法进行测试,编写了6个对应的静态方法
 *                A    getAnnotation(Class<A> annotationClass)  获得多个重复注解注解对象时将报错
 *     Annotation[]    getAnnotations() 获取该类所有的注解(包括来自父类)
 *                A    getDeclaredAnnotation(Class<A> annotationClass) 获得多个重复注解注解对象时将报错
 *     Annotation[]    getDeclaredAnnotations()  返回多个类型的注解,只能是Annotation[]
 *              A[]    getAnnotationsByType(Class<A> annotationClass) 获取多个相同注解对象,可来自继承
 *              A[]    getDeclaredAnnotationsByType(Class<A> annotationClass) 取多个相同注解对象,完全来自本类的声明
 */
        //获取本类的Class对象,注意: forName要引用完整的包名
       Class clazz = Class.forName("com.china.school.oop.注解");

        //hello实例其实是父类China类的@Hello注解对象,这说明了@Hello注解继承性
        // 本类也声明了3个Hello注解,他们最终合并成Hellos注解对象(class文件里)
        //以后看到多个重复注解修饰同一个元素,就把他们想象成一个容器注解就不会乱。
        Hello hello = (Hello)clazz.getAnnotation(Hello.class);
        System.out.println(hello.value());// Hello-1

        Hellos hellos = (Hellos) clazz.getAnnotation(Hellos.class);
        System.out.println(Arrays.toString(hellos.value()));
        //本例可以很好的解释上面的疑惑,输出结果如下
        //[@com.china.school.oop.Hello(value="Hello-2"),
        // @com.china.school.oop.Hello(value="Hello-3"),
        // @com.china.school.oop.Hello(value="Hello-4")]

        World world = (World)clazz.getDeclaredAnnotation(World.class);
        System.out.println(world.value());
        //输出结果: World-2,获取本类声明的@World注解对象

        Annotation[] anns = clazz.getAnnotations();
        System.out.println(Arrays.toString(anns));
        //获取到了数组中包含三个对象,一个本类的@World对象,一个父类的@Hello对象
        //再一个本类的@Hellos容器对象
        //[@com.china.school.oop.World(value="World-2"),
        // @com.china.school.oop.Hello(value="Hello-1"),
        // @com.china.school.oop.Hellos(
        // value={@com.china.school.oop.Hello(value="Hello-2"),
        // @com.china.school.oop.Hello(value="Hello-3"),
        // @com.china.school.oop.Hello(value="Hello-4")})]

        Annotation[] anns2 = clazz.getDeclaredAnnotations();
        System.out.println(Arrays.toString(anns2));
        //获取到了数组中包含2个对象,一个本类的@World对象,再一个本类的@Hellos容器对象(它又包含了3个@Hello对象)
        //[@com.china.school.oop.World(value="World-2"),
        // @com.china.school.oop.Hellos(
        // value={@com.china.school.oop.Hello(value="Hello-2"),
        // @com.china.school.oop.Hello(value="Hello-3"),
        // @com.china.school.oop.Hello(value="Hello-4")})]

        Hello[] helloArr1 = (Hello[])clazz.getDeclaredAnnotationsByType(Hello.class);
        System.out.println(Arrays.toString(helloArr1));
        //该数组中都是本类的3个重复注解,虽然父类China中也声明了@Hello,但被子类的@Hello给覆盖了
        //如果@Hello注解的定义中去掉@Inherited注解,还是输出3个@Hello对象
        //也就是说获取重复注解永远都是计算本类的声明的个数
        //[@com.china.school.oop.Hello(value="Hello-2"),
        // @com.china.school.oop.Hello(value="Hello-3"),
        // @com.china.school.oop.Hello(value="Hello-4")]

        var anonymous = new 注解(){};
        Hello[] helloArr2 = (Hello[])anonymous.getClass().getAnnotationsByType(Hello.class);
        System.out.println(Arrays.toString(helloArr2));
        //先创建了一个匿名内部类对象anonymous,因为该匿名内部类肯定没有,也没办法声明@Hello注解
        // 通过这个anonymous的Class对象的getAnnotationsByType()获取父类的@Hello重复注解对象,结果如下
        //[@com.china.school.oop.Hello(value="Hello-2"),
        // @com.china.school.oop.Hello(value="Hello-3"),
        // @com.china.school.oop.Hello(value="Hello-4")]

    }
}

The output results are as follows:

Hello-1[@com.china.school.oop.Hello(value=”Hello-2″), @com.china.school.oop.Hello(value=”Hello-3″), @com.china.school.oop.Hello(value=”Hello-4″)]World-2[@com.china.school.oop.World(value=”World-2″), @com.china.school.oop.Hello(value=”Hello-1″), @com.china.school.oop.Hellos(value={@com.china.school.oop.Hello(value=”Hello-2″), @com.china.school.oop.Hello(value=”Hello-3″), @com.china.school.oop.Hello(value=”Hello-4″)})][@com.china.school.oop.World(value=”World-2″), @com.china.school.oop.Hellos(value={@com.china.school.oop.Hello(value=”Hello-2″), @com.china.school.oop.Hello(value=”Hello-3″), @com.china.school.oop.Hello(value=”Hello-4″)})][@com.china.school.oop.Hello(value=”Hello-2″), @com.china.school.oop.Hello(value=”Hello-3″), @com.china.school.oop.Hello(value=”Hello-4″)][@com.china.school.oop.Hello(value=”Hello-2″), @com.china.school.oop.Hello(value=”Hello-3″), @com.china.school.oop.Hello(value=”Hello-4″)]