注解学习完整篇

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)

注意:
     1 不可以这样 public @World class ABC{}
     2 如果给实现的匿名内部接口添加注解必须添加到该匿名内部类上, public class ClassTest  implements  ABC.@World("good")InnerInterface
     3 尽管如上不可,但我们还是认为TYPE_USE,可以让注解使用在 任何类型出现的地方
     
 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)来获取每个子注解对象
 因为重复注解只是表象,这多个重复注解其实会作为容器注解的数组对象存在,最后只存在编译后只存在容器注解)
 6 用法: 先合成法再继承法(解决当面对多个或一个重复注解同时或单独修饰子类和父类时)
     1 合成法 永远的记住多个重复注解将合成一个去看待.可以解决无限迷茫.所以反射一个元素上的多个重复注解中的一个将报错.
     2 继承法: 子类的重复注解覆盖父类重复注解,子类没有声明重复注解,则不会覆盖.(如果没有继承性只考虑本类即可)
     比如
 @Hello("a")
 @Hello("b")
 public class Test{}
 最后成了这样
 @Hellos({@Hello("a"),@Hello("b")})
 public class Test{}
 因此当出现重复注解后把它们合成一个注解,再去判断如何获取注解对象,就不会迷茫了

 */
@Target(ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
@interface Good{}

//定义容器注解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的默认信息";
    String name = "我是World注解的静态成员";
    class StaticInnerClass{      }
    enum InnerEnum{SPRING;}
    @InnerAnnotation("反射获取内部注解的实例对象的value值")
    interface InnerInterface{}

    @Target( ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface InnerAnnotation{String value();}
}
@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")]

        //获取注解静态成员
        System.out.println(World.name);//我是World注解的静态成员
        //创建注解内部类实例
        System.out.println(new World.StaticInnerClass());//com.china.school.oop.World$StaticInnerClass@6fb554cc
        System.out.println(World.InnerEnum.SPRING);//SPRING
        System.out.println(new World.InnerInterface() { });//com.china.school.oop.注解$2@68837a77
        System.out.println(World.InnerInterface.class.getDeclaredAnnotation(World.InnerAnnotation.class).value());//反射获取内部注解的实例对象的value值
        System.out.println(World.class.getAnnotation(World.InnerAnnotation.class).value());//good,甚至将自己的内部注解修饰本类,也可以反射获取


    }
}

输出结果如下:  

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")]
我是World注解的静态成员
com.china.school.oop.World$StaticInnerClass@6fb554cc
SPRING
com.china.school.oop.注解$2@68837a77
反射获取内部注解的实例对象的value值
good

posted @ 2022-05-15 00:14  -和时间赛跑-  阅读(55)  评论(0编辑  收藏  举报