• Java 注解浅析


    一、什么是注解

    注解(Annotation)即代码里的特殊标记,JDK5.0 之后引入了 Annotation 的概念来描述元数据,在 Java 中元数据以标签的形式存在于 Java 代码中,但是元数据标签的存在并不影响程序代码的编译和执行

    在 JavaSE 中,注解的使用目的比较简单,例如标记过时的功能(@Deprecated)、限定必须重写父类的方法(@Override)、抑制编译器警告(@SuppressWarning)等.但是在JavaEE 中注解占据了更加重要的角色,例如之前需要实现 AOP 时,需要繁冗的 XML 配置,有了注解之后实现 AOP 变得更加的简洁.注解是一种趋势,在一定程度上可以说 框架 = 注解 + 反射 + 设计模式  

      

    二、常见的注解示例

    1、文档注释相关注解

    @author: 说明开发该模块的作者,多个作者可以用逗号分割
    @version: 说明该模块的版本
    @since: 说明是从哪个版本开始增加的
    @param: 对方法中的参数进行说明,如果没有参数就不能使用该注解
    @return: 对方法返回值的说明,如果方法的返回值类型是 void ,则不能使用该注解
    @thorws: 对方法可能抛出的异常进行说明,如果方法上没有显示的用 throws 抛出异常,则不能使用该注解
     
    2、JDK 内置的三个基本注解
    @Override: 限定必须重写父类方法
    @Deprecated: 表示修饰的元素(类、方法等)已过时,通常是因为所修饰的结构存在危险或者已经有更好的选择方式
    @SuppressWarning: 抑制警告
     
    3、元注解
    元注解可以理解为一种基本注解,它就是可以标注在其它的注解上面的注解,元注解有五种,分别是 @Retention、@Documented、@Target、@Inherited、@Repeatable
    3.1、@Rentention
    Rentention 的英文为保留期的意思,当 @Rentention 应用到一个注解上的时候,它解释说明了这个注解的存活时间
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Retention {
        /**
         * Returns the retention policy.
         * @return the retention policy
         */
      // @Retention 的取值
        RetentionPolicy value();
    }

    @Rentention 的取值选项如下

    @Rentention(RententionPolicy.SOURCE / RententionPolicy.CLASS / RententionPolicy.RUNTIME) 

    RententionPolicy.SOURCE: 注解只在源码阶段保留,编译器编译的时候将会被丢弃

    RententionPolicy.CLASS: 注解只被保留至编译器编译后的字节码文件,它不会被虚拟机加载,如果没有注明使用其它的值,默认的取值就是 RententionPolicy.CLASS

    RententionPolicy.RUNTIME: 注解能够保留至运行期,通过反射可以获取该注解的信息

    3.2、@Target

    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
         */
      // @Target 的取值 ElementType[] value(); }
    @Target 的取值如下
    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 }

    3.3、@Documented

    如果某一个注解上面使用了 @Documented 注解,那么该注解将会被 JavaDoc 工具提取成文档,定义了 @Documented 的注解必须包含@Rentention(RetentionPolicy.RUNTIME)

    3.4、@Inherited

    某个注解如果使用了 @Inherited 元注解,那么该注解将具有继承性
    举个例子:@TestInheritedAnnotation 被 @Inherited 修饰,父类 Animal 使用了 @TestInheritedAnnotation 注解,那么 Animal 的子类都自动具有@TestInheritedAnnotation 这个注解
     
    @Inherited样例:
    // 自定义 @TestInheritedAnnotation 注解,然后在该注解上使用元注解 @Inherited 
    @Inherited
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface TestInheritedAnnotation {
        String value();
    }
    
    
    // 定义动物类作为父类,并且使用了 @TestInheritedAnnotation 注解
    @TestInheritedAnnotation(value = "xiaomaomao")
    public class Animal {
    }
    
    
    // 定义 Animal 的子类 Cow,测试 Cow 中是不是也有 @TestInheritedAnnotation 注解
    public class Cow extends Animal {
        public static void main(String[] args) {
            System.out.println(isHasInheritedAnnotation());
        }
    
        public static String isHasInheritedAnnotation() {
            String value = "";
            // 判断 Cow 类上是否使用了 @TestInheritedAnnotation 注解
            boolean hasAnnotation = Cow.class.isAnnotationPresent(TestInheritedAnnotation.class);
            if (hasAnnotation) {
                TestInheritedAnnotation annotation = Cow.class.getAnnotation(TestInheritedAnnotation.class);
                value = annotation.value();
            }
            return value;
        }
    }

    3.5、@Repeatable

    可重复注解, @Repeatable 注解是 JDK 8 之后才加入进来的,是 JDK 8 新特性,它的意思是注解的值可以取多个

    // 自定义容器注解 Person
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface Person {
        // @Person 注解是 @RepeatableAnnotation 的容器,定义类型为注解数组
        RepeatableAnnotation[] value();
    }
    
    // 自定义注解 RepeatableAnnotation
    // 使用 @Repeatable 注解的时候,必须要为当前注解定义一个容器注解,我们这里是 @Person
    @Repeatable(Person.class)
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface RepeatableAnnotation {
        String role() default "coder";
    }
    
    // 测试类
    @RepeatableAnnotation(role = "president")
    @RepeatableAnnotation(role = "product manager")
    @RepeatableAnnotation(role = "technology manager")
    @RepeatableAnnotation(role = "cleaner")
    public class RepeatableAnnotationDemo {
        public static void main(String[] args) {
            StringBuffer stringBuffer = new StringBuffer();
            // 判断 RepeatableAnnotationDemo 类上是否使用了@Person 注解
            boolean hasPersonAnnotation = RepeatableAnnotationDemo.class.isAnnotationPresent(Person.class);
            if (hasPersonAnnotation) {
    			// 获取 RepeatableAnnotationDemo 类上 Person 注解
                Person personAnnotation = RepeatableAnnotationDemo.class.getAnnotation(Person.class);
                // 通过 Person 注解容器获取到该容器内的所有 @RepeatableAnnotation 注解
                RepeatableAnnotation[] repeatableAnnotations = personAnnotation.value();
                // 获取所有的 @RepeatableAnnotation 中的 role 值并拼接输出
                for (RepeatableAnnotation repeatableAnnotation : repeatableAnnotations) {
                    stringBuffer.append(repeatableAnnotation.role() + "===>");
                }
                System.out.println(stringBuffer);
            }
        }
    }
    
    // 测试结果
    president===>product manager===>technology manager===>cleaner===>
    

      

    三、注解的属性

    注解的属性也叫做成员变量,注解只有成员变量,没有方法,注解的成员变量是以 无形式参数的方法 来声明的,其方法中定义了该成员变量的类型和成员方法的名字

    注意: 在注解中定义属性,属性的类型必须是八种基本数据类型、类、接口、注解以及它们的数组

    如下自定义的注解中有 int 类型的 id,它有一个默认值 10086、String 类型的 userName、以及 String 数组类型的 hobby

    @Retention(value=RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE_USE)
    public @interface TestAnnotation {
      // 注解中可以有默认值,使用 default 关键字来指定 int id() default 10086; String userName(); String[] hobby(); }

    1、如何给注解赋值

    // 根据 @TestAnnotation 的属性类型和属性名称依次给注解赋值,多个值用逗号隔开,由于 id 属性有默认值,
    // 那么这里可以不用给id赋值,如果显示的给id赋值的话,那么它会覆盖默认值
    @TestAnnotation(userName = "xiaomaomao",hobby={"film","novel","play computer games"})
    public class AnnotationDemo {
    }
    

    2、如果注解只有一个属性的情况

    @Retention(value=RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE_USE)
    public @interface TestAnnotation {
      // 如果注解只有一个属性,请使用 value 来命名该属性
       String value();
    }  

    取值的时候需要注意

    // 如果注解只有一个属性可以不写 value,直接 @TestAnnotation("xiaomaomao") 赋值也是一样的
    @TestAnnotation(value="xiaomaomao")
    public class AnnotationDemo {
    }
    

      

    四、自定义注解

    1、定义新的 Annotation 类型使用 @interface 关键字.

    2、自定义注解自动继承了java.lang.annotation.Annotation 接口.

    3、Annotation 的成员变量在 Annotation 定义中以无参数方法的形式来声明,其方法名和返回值定义了该成员的名字和类型,我们称为配置参数.类型只能是八种基本数据类型、String 类型、Class 类型、enum 类型、Annotation 类型、以上所有类型的数组.

    4、可以在定义 Annotation 的成员变量时为其指定初始值,指定成员变量的初始值可使用 default 关键字.

    5、如果只有一个参数成员,建议使用参数名为 value.

    6、如果定义的注解含有配置参数,那么使用时必须指定参数值,除非它有默认值.格式是 "参数名 = 参数值",如果只有一个参数成员,且名称为 value,可以省略 "value="

    7、没有成员定义的 Annotation 称为标记;包含成员变量的 Annotation 称为元数据 Annotation.

    特别注意:自定义注解必须配上注解的信息处理流程才有意义.

     
    自定义注解样例:
    // 自定义注解 TestAnnotation
    @Retention(value=RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE_USE)
    public @interface TestAnnotation {
        int id() default 10086;
        String userName();
        String[] hobby();
    }
    
    // 测试类
    @TestAnnotation(id=10001,userName = "xiaomaomao",hobby={"film","novel","play computer games"})
    public class AnnotationDemo {
        public static void main(String[] args) {
            // 判断 AnnotationDemo 类上是否使用了 @TestAnnotation 注解
            boolean hasAnnotation = AnnotationDemo.class.isAnnotationPresent(TestAnnotation.class);
            StringBuffer stringBuffer = new StringBuffer();
            if (hasAnnotation) {
                // 获取 AnnotationDemo 上的所有注解,这里只有一个 @TestAnnotation 注解
                Annotation[] annotations = AnnotationDemo.class.getAnnotations();
                for (Annotation annotation : annotations) {
                    // 获取 @TestAnnotation 注解中的属性并进行拼接
                    if (TestAnnotation.class.equals(annotation.annotationType())) {
                        TestAnnotation testAnnotation = (TestAnnotation) annotation;
                        int id = testAnnotation.id();
                        String userName = testAnnotation.userName();
                        String[] hobbys = testAnnotation.hobby();
                        stringBuffer.append(id);
                        stringBuffer.append("
    ");
                        stringBuffer.append(userName);
                        stringBuffer.append("
    ");
                        stringBuffer.append("[");
                        for (String hobby : hobbys) {
                            stringBuffer.append(hobby+"、");
                        }
                        stringBuffer.append("]");
                        stringBuffer.append("
    ");
                        // 获取 @TestAnnotation 上的注解类型并进行拼接
                        Annotation[] underTestAnnotations = testAnnotation.annotationType().getAnnotations();
                        for (Annotation underTestAnnotation : underTestAnnotations) {
                            Class<? extends Annotation> aClass = underTestAnnotation.annotationType();
                            stringBuffer.append(aClass);
                            stringBuffer.append("
    ");
                        }
    
                    }
                    System.out.println(stringBuffer);
                }
            }
        }
    }
    
    // 测试结果
    10001
    xiaomaomao
    [film、novel、play computer games、]
    interface java.lang.annotation.Retention
    interface java.lang.annotation.Target
    

      

  • 相关阅读:
    输入和输出--java的NIO
    Java的NIO
    Java的nio
    输入和输出--java序列化机制
    输入和输出--javase中的路径
    输入和输出--重定向标准输入和输出
    输入和输出--RandomAccessFile类
    输入和输出--IO流
    输入和输出--File类
    无废话XML--DOM4J
  • 原文地址:https://www.cnblogs.com/xiaomaomao/p/13474555.html
Copyright © 2020-2023  润新知