美文网首页
《重学Java系列》之 注解基础

《重学Java系列》之 注解基础

作者: 倔脾气的皮皮虾啊 | 来源:发表于2020-02-21 19:02 被阅读0次

    不诗意的女程序媛不是好厨师~
    转载请注明出处,From李诗雨---[https://blog.csdn.net/cjm2484836553/article/details/104432728]

    按照惯例我们先来看下本篇的思维导图:


    在这里插入图片描述

    本篇来和大家先学习一下注解基础,后期再一步一步深入学习。

    本篇的主要目的就是先对注解有些基本的了解,后期会进行大量的代码实战,这样循序渐进,相信我们就可以很好的掌握注解了。

    1.什么是注解?

    【注解的定义】:

    Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java5 开始添加到 Java 的。

    【通俗点理解】:

    注解即标签。

    如果把代码想象成一个具有生命的个体,注解就是给这些代码的某些个体打标签。

    2.自定义注解及使用

    • 注解通过 @interface关键字进行定义。
    public @interface Test {
    }
    

    它的形式跟接口很类似,不过前面多了一个 @ 符号。

    上面的代码就创建了一个名字为 Test 的注解。你可以简单理解为创建了一张名字为 Test的标签。

    • 使用注解
    @Test
    public class TestAnnotation {
    }
    

    创建一个类 TestAnnotation,然后在类定义的地方加上 @Test就可以用 Test注解这个类了。

    你可以简单理解为将 Test 这张标签贴到 TestAnnotation这个类上面。

    3.元注解

    3.1元注解的理解

    元注解:即注解的注解。

    元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。

    如果难于理解的话,你可以这样理解。元注解也是一张标签,但是它是一张特殊的标签,它的作用和目的就是给其他普通的标签进行解释说明的。

    3.2 常见的5种元注解。

    元注解有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 种。

    下面分别一一来说明:

    @Retention --- 指定了注解的存活时间

    Retention 的英文意为保留期的意思。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间。

    它的取值如下:

    1. RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。 ----生命周期最短的注解
    2. RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
    3. RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们

    我画一张图来帮助大家理解 这三个取值下 注解 所能保留到的时期:

    在这里插入图片描述

    @Target ---- 指定注解用在什么地方

    Target 是目标的意思,@Target 指定了注解运用的地方
    你可以这样理解,当一个注解被 @Target 注解时,这个注解就被限定了运用的场景。
    类比到标签,原本标签是你想张贴到哪个地方就到哪个地方,但是因为 @Target 的存在,它张贴的地方就非常具体了,比如只能张贴到方法上、类上、方法参数上等等。@Target 有下面的取值

    1. ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
    2. ElementType.CONSTRUCTOR 可以给构造方法进行注解
    3. ElementType.FIELD 可以给属性进行注解
    4. ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
    5. ElementType.METHOD 可以给方法进行注解
    6. ElementType.PACKAGE 可以给一个包进行注解
    7. ElementType.PARAMETER 可以给一个方法内的参数进行注解

    @Inherited --- 允许子类继承父类的注解。

    Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。

    这么说比较抽象,举个栗子给大家看看:

    现在有一个 美女类Beauty,还有一个它的子类 BeautyChild。

    那么当Beauty类被打上了“美女”的标签后,她的孩子BeautyChild默认也会拥有“美女标签”。

    (PS:也就是美女的孩子还是美女。)

    //1.自定义一个使用了@Inherited的标签
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    public @interface InheritedTest {
        String value();
    }
    
    //2.美女类Beauty,并且拥有一个“美女”标签
    @InheritedTest("美女")
    public class Beauty {
    }
    
    //3.美女类的孩子类BeautyChild,并没有特地指定任何标签
    class BeautyChild extends Beauty {
    }
    
    //4.测试:看从子类中能否取到标签
    public class DemoTest {
        public static void main(String[] args) {
            Class<BeautyChild> beautyChildClass = BeautyChild.class;
            if (beautyChildClass.isAnnotationPresent(InheritedTest.class)) {
             System.out.println(beautyChildClass.getAnnotation(InheritedTest.class).value());
            }
        }
    }
    

    结果是:从子类中确实取到了父类所拥有的标签。


    在这里插入图片描述

    @Repeatable --- 注解的值可以同时取多个

    Repeatable 是可重复的意思。@Repeatable 是 Java 1.8 才加进来的,所以算是一个新的特性。

    什么样的注解会多次应用呢?通常是注解的值可以同时取多个。

    还是来举个栗子来方便大家理解吧:

    比如说我吧,在工作上,我拥有“程序媛”的标签;在朋友眼中,我拥有“疯一样的女子”的标签;在陌生人眼中,我又拥有“淑女”的标签。根据不同的场合,我可以拥有很多不同的标签,这完全没有问题。

    //1.先声明一个Persons类用来包含所有的身份
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Persons {
        Person[] value();//由于会有多个身份,所以此处是数组
    }
    
    //2.定义一个Person注解,它使用到了@Repeatable。
    //@Repeatable括号内的就相当于用来保存该注解内容的容器。
    @Repeatable(Persons.class)
    public @interface Person {
        String role() default "";
    }
    
    //3.声明一个Shiyu类,给该类加上一些身份。
    @Person(role = "程序媛")
    @Person(role = "疯一样的女子")
    @Person(role = "淑女")
    public class Shiyu {
    }
    
    //4.测试看看能否获取这多个标签
    public class DemoTest2 {
        public static void main(String[] args) {
            if (Shiyu.class.isAnnotationPresent(Persons.class)) {
                Persons p2 = Shiyu.class.getAnnotation(Persons.class);
                for (Person t : p2.value()) {
                    System.out.println(t.role());
                }
            }
        }
    }
    

    结果标签全部可以打印出来,则正确:


    在这里插入图片描述

    【注意事项】:

    大家在测试@repeatable的代码时,别忘了配置gradle哈。要加上下边这个配置才可以:


    在这里插入图片描述

    即要加上:

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    

    如果不加的话,编译时会报错:


    在这里插入图片描述

    所以,切记这些小细节哈~

    @Documented---用到的很少

    顾名思义,这个元注解肯定是和文档有关。它的作用是能够将注解中的元素包含到 Javadoc 中去。ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举

    4.注解的属性

    注解的属性也叫做成员变量。

    注解只有成员变量,没有方法。

    【需要注意】的是:

    • 在注解中定义属性时它的类型是有要求的。

      只能用8种基本数据类型( byte,short,char,int,long,float,double,boolean),外加 类(Class),接口(interface),注解(annotation), String,以及这一些类的数组。

    • 注解中属性可以有默认值,默认值需要用 default 关键值指定

      如:

      @Target(ElementType.TYPE)
      @Retention(RetentionPolicy.RUNTIME)
      public @interface Test{
          int id() default -1;
          String msg() default "Hello";
      }
      

      上面代码定义了 TestAnnotation 这个注解中拥有 id 和 msg 两个属性。

      在使用的时候,我们应该给它们进行赋值。

      赋值的方式是在注解的括号内以 value="" 形式,多个属性之前用 ,隔开:

      @Test(id=1,msg="hello annotation")
      public class TestAnnotation {
      }
      

    5.注解的提取

    1.运行期注解(RunTime)---利用反射去获取信息。
    2.编译期(Compile time)注解,以及处理编译期注解的手段是APT和Javapoet,@Retention(RetentionPolicy.CLASS)。

    今天我们主要讲一下运行期注解的提取:

    首先可以通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解

    public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}
    

    然后通过 getAnnotation() 方法来获取 Annotation 对象。

     public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}
    

    或者是 getAnnotations() 方法。

    public Annotation[] getAnnotations() {}
    

    前一种方法返回指定类型的注解,后一种方法返回注解到这个元素上的所有注解。

    如果获取到的 Annotation 如果不为 null,则就可以调用它们的属性方法了。比如

    public class TestDemo {
        public static void main(String[] args) {
            boolean hasAnnotation = TestDemo.class.isAnnotationPresent(Test.class);
            if (hasAnnotation) {
                TestAnnotation testAnnotation = TestDemo.class.getAnnotation(Test.class);
                System.out.println("id:" + testAnnotation.id());
                System.out.println("msg:" + testAnnotation.msg());
            }
        }
    }
    

    6.注解的使用场景

    • 提供信息给编译器: 编译器可以利用注解来探测错误和警告信息
    • 编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
    • 运行时的处理: 某些注解可以在程序运行的时候接受代码的提取
      值得注意的是,注解不是代码本身的一部分。

    积累点滴,做好自己~

    相关文章

      网友评论

          本文标题:《重学Java系列》之 注解基础

          本文链接:https://www.haomeiwen.com/subject/nolhqhtx.html