• 注解


    注解:

    一、 标准注解:@Override、@Deprecated、@SuppressWarnings

    二、 元注解:用于修饰注解的注解,通常用在注解的定义上

    1. @Target : 注解的作用目标

    • package、type(类、接口、枚举、Annotation类型)
    • 类型成员(方法、构造方法、成员变量、枚举值)
    • 方法参数和本地变量(如循环变量、catch参数)

    查看@Target注解源码:

    package java.lang.annotation;
    
    @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();
    }
    

    其中定义了一个value()的数组,查看数组是个枚举类:

    package java.lang.annotation;
    
    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中新增了两个(TYPE_PARAMETER和TYPE_USE)

    其中TYPE_USE包含以上所有类型,即如果想让注解标注在上面任何类型,就可以选用TYPE_USE了。而另一个TYPE_PARAMETER也是做参数标注的,和PARAMETER不同的是,TYPE_PARAMETER是作用在泛型参数上的。
    例如自定义TYPE_PARAMETER类型的注解@TypeParameterAnnotation:

    @Target(ElementType.TYPE_PARAMETER)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TypeParameterAnnotation {
    }
    
    //将@TypeParameterAnnotation作用于泛型参数之上
    public class TypeParameterDemo<@TypeParameterAnnotation T> {
    
        public <@TypeParameterAnnotation U> T foo(T t){
            return t;
        }
    }
    

    2. @Retention : 标注注解被保留时间的长短

    • 定义注解的生命周期

    查看@Retention注解源码:

    package java.lang.annotation;
    
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Retention {
        /**
         * Returns the retention policy.
         * @return the retention policy
         */
        RetentionPolicy value();
    }
    

    其中定义了一个类型为RetentionPolicy的value(),进一步查看RetentionPolicy是个枚举类:

    package java.lang.annotation;
    
    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
    }
    
    • SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
    • CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
    • RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;

    这三个可以简单的理解为Java源文件(.java文件) ---> .class文件 ---> 内存中的字节码

    3. @Documented : 注解是否应该被包含在JavaDoc文档中

    4. @Inherited : 是否允许子类继承该注解

    关于@Inherited介绍看这篇文章比较好 https://www.jianshu.com/p/7f54e7250be3

    三、 自定义注解

    1. 格式

    public @interface 注解名 {
        修饰符 返回值 属性名() 默认值;
        修饰符 返回值 属性名() 默认值;
    }
    

    2. 注解属性支持的类型

    • 所有基本类型(int float double boolean byte char short long)
    • String类型
    • Class类型
    • Enum类型
    • Annotation类型
    • 以上所有类型的数组

    3. 关于类的基本信息

    • Package

        public class Package implements java.lang.reflect.AnnotatedElement {
        //省略...
        }
      

    Package实现了AnnotatedElement

    • Class

        public final class Class<T> implements java.io.Serializable,
                                  GenericDeclaration,
                                  Type,
                                  AnnotatedElement {
        //省略...
        }
      

    查看Class发现其实现了AnnotatedElement

    • Constructor

        public final class Constructor<T> extends Executable {
        //省略...
        }
      
        public abstract class Executable extends AccessibleObject
            implements Member, GenericDeclaration {
        //省略...
        }
      
        public class AccessibleObject implements AnnotatedElement {
        //省略...
        }
      

    Constructor也是实现了AnnotatedElement,其间还继承了是否可执行以及是否可达两个类

    • Field

        public final class Field extends AccessibleObject implements Member {
        //省略...
        }
        
        public class AccessibleObject implements AnnotatedElement {
        //省略...
        }
      

    Field也实现了AnnotatedElement,其继承了是否可达这个类

    • Method

        public final class Method extends Executable {
        //省略...
        }
        
        public abstract class Executable extends AccessibleObject
            implements Member, GenericDeclaration {
        //省略...
        }
      
        public class AccessibleObject implements AnnotatedElement {
        //省略...
        }
      

    Method与Constructor类似,都存在是否可执行以及是否可达,而Field只存在是否可达情况(Field并不被执行),但是四个类都实现了AnnotatedElement,来查看一下其源码(注释太多都被我干掉了):

    public interface AnnotatedElement {
    
        default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
            return getAnnotation(annotationClass) != null;
        }
    
        <T extends Annotation> T getAnnotation(Class<T> annotationClass);
    
        Annotation[] getAnnotations();
    
        default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {
             /*
              * Definition of associated: directly or indirectly present OR
              * neither directly nor indirectly present AND the element is
              * a Class, the annotation type is inheritable, and the
              * annotation type is associated with the superclass of the
              * element.
              */
             T[] result = getDeclaredAnnotationsByType(annotationClass);
    
             if (result.length == 0 && // Neither directly nor indirectly present
                 this instanceof Class && // the element is a class
                 AnnotationType.getInstance(annotationClass).isInherited()) { // Inheritable
                 Class<?> superClass = ((Class<?>) this).getSuperclass();
                 if (superClass != null) {
                     // Determine if the annotation is associated with the
                     // superclass
                     result = superClass.getAnnotationsByType(annotationClass);
                 }
             }
    
             return result;
         }
    
        default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {
             Objects.requireNonNull(annotationClass);
             // Loop over all directly-present annotations looking for a matching one
             for (Annotation annotation : getDeclaredAnnotations()) {
                 if (annotationClass.equals(annotation.annotationType())) {
                     // More robust to do a dynamic cast at runtime instead
                     // of compile-time only.
                     return annotationClass.cast(annotation);
                 }
             }
             return null;
         }
    
        default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) {
            Objects.requireNonNull(annotationClass);
            return AnnotationSupport.
                getDirectlyAndIndirectlyPresent(Arrays.stream(getDeclaredAnnotations()).
                                                collect(Collectors.toMap(Annotation::annotationType,
                                                                         Function.identity(),
                                                                         ((first,second) -> first),
                                                                         LinkedHashMap::new)),
                                                annotationClass);
        }
    
        Annotation[] getDeclaredAnnotations();
    }
    

    根据单一职责原则,接口中只定义方法,实现都在实现类中实现的,所以Package、Class、Constructor、Field、Method都是有其对应的实现方法的,这里我们不关心他们是怎么实现的,而查看这几个方法都是做什么的,这里有getDeclaredAnnotation和getAnnotation两个方法,Declared是自获取自身的注解,不包括继承过来的,而getAnnotation是或者所有注解,包括继承过来的。

    //判断指定类型的注解是否存在于此元素上
    default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
    	return getAnnotation(annotationClass) != null;
    }
    //获取对象上单个指定注解
    <T extends Annotation> T getAnnotation(Class<T> annotationClass);
    //获取对象上面所有的注解
    Annotation[] getAnnotations();
    //获取对象上面指定类型的注解数组
    default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {
         T[] result = getDeclaredAnnotationsByType(annotationClass);
    
         if (result.length == 0 && // Neither directly nor indirectly present
             this instanceof Class && // the element is a class
             AnnotationType.getInstance(annotationClass).isInherited()) { // Inheritable
             Class<?> superClass = ((Class<?>) this).getSuperclass();
             if (superClass != null) {
                 // Determine if the annotation is associated with the
                 // superclass
                 result = superClass.getAnnotationsByType(annotationClass);
             }
         }
    
         return result;
     }
    

    前几个都没问题,后面getAnnotationsByType是JDK1.8新增的属性,我们来举例说明:

    首先定义两个注解

    @Repeatable(MultipleAnnotation.class)
    public @interface TypeTestAnnotation {
    
        String value();
    }
    
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MultipleAnnotation {
    
        TypeTestAnnotation[] value();
    }
    
    public class ZhangSan {
    
        @TypeTestAnnotation("第一个")
        @TypeTestAnnotation("第二个")
        @TypeTestAnnotation("第三个")
        String language;
    
        public static void main(String[] args) throws NoSuchFieldException {
    
            TypeTestAnnotation[] languages = ZhangSan.class.getDeclaredField("language").getDeclaredAnnotationsByType(TypeTestAnnotation.class);
            for (TypeTestAnnotation language : languages) {
                System.out.println(language);
            }
        }
    }
    
    out:
    @com.demo.annotation.TypeTestAnnotation(value=第一个)
    @com.demo.annotation.TypeTestAnnotation(value=第二个)
    @com.demo.annotation.TypeTestAnnotation(value=第三个)
    

    其中@Repeatable注解也是在JDK1.8中新增的,旨在扩展重复注解,这里的TypeTestAnnotation指向存储注解MultipleAnnotation,1.8之前如果想使用重复注解得这样定义:

    public @interface TypeTestAnnotation {
    
        String value();
    }
    
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MultipleAnnotation {
    
        TypeTestAnnotation[] value();
    }
    
    public class ZhangSan {
    
        @MultipleAnnotation({@TypeTestAnnotation("第一个"),@TypeTestAnnotation("第二个"),@TypeTestAnnotation("第三个")})
        String language;
    
        public static void main(String[] args) throws NoSuchFieldException {
    
            MultipleAnnotation language = ZhangSan.class.getDeclaredField("language").getDeclaredAnnotation(MultipleAnnotation.class);
            System.out.println(language);
        }
    }
    
    out:
    @com.demo.annotation.MultipleAnnotation(value=[@com.demo.annotation.TypeTestAnnotation(value=第一个), @com.demo.annotation.TypeTestAnnotation(value=第二个), @com.demo.annotation.TypeTestAnnotation(value=第三个)])
    

    这样做也是可以,但是很明显结合了@Repeatable的getDeclaredAnnotationsByType更加的人性化。

    讲完AnnotatedElement之后,进入以下实战测试:

    四、 自定义注解实战

    1. 先定义两个注解:

    //类和方法
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface CourseInfoAnnotation {
    
        //课程名称
        String courseName();
    
        //课程标签
        String courseTag();
    
        //课程简介
        String courseProfile();
    
        //课程序号
        int courseIndex() default 1503;
    
    }
    
    //字段
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface PersonInfoAnnotation {
    
        //名字
        String name();
    
        //年龄
        int age() default 18;
    
        //性别
        String gender() default "男";
    
        //开发语言
        String[] language();
    }
    

    2. 在定义一个用于标注的类English:

    @CourseInfoAnnotation(
            courseName = "类名字",
            courseTag = "类标签",
            courseProfile = "类简介")
    public class English {
    
        @PersonInfoAnnotation(name = "Joker", language = {"Java", "C++", "Python"})
        private String author;
    
        @CourseInfoAnnotation(
                courseName = "方法名字",
                courseTag = "方法标签",
                courseProfile = "方法简介",
        courseIndex = 200)
        public void getCourseInfo(){
    
        }
    }
    

    3. 编写测试类AnnotationParser:

    public class AnnotationParser {
    
        //解析类的注解
        public static void parseTypeAnnotation() throws ClassNotFoundException {
            Class<?> clazz = Class.forName("com.demo.annotation.English");
    
            //获取class的注解,而非成员或方法的注解
            Annotation[] annotations = clazz.getAnnotations();
            for (Annotation annotation : annotations) {
                System.out.println(annotation);
            }
    
            //获取单个注解
            CourseInfoAnnotation annotation2 = clazz.getAnnotation(CourseInfoAnnotation.class);
            System.out.println(annotation2);
        }
    
        //解析成员变量的注解
        public static void parseFieldAnnotation() throws ClassNotFoundException {
            Class<?> clazz = Class.forName("com.demo.annotation.English");
    
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                //是否被PersonInfoAnnotation注解修饰
                boolean hasAnnotation = declaredField.isAnnotationPresent(PersonInfoAnnotation.class);
                if(hasAnnotation){
                    PersonInfoAnnotation annotation = declaredField.getAnnotation(PersonInfoAnnotation.class);
                    System.out.println(annotation);
                }
            }
        }
    
        //解析方法的注解
        public static void parseMethodAnnotation() throws ClassNotFoundException {
            Class<?> clazz = Class.forName("com.demo.annotation.English");
    
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for (Method declaredMethod : declaredMethods) {
                boolean hasAnnotation = declaredMethod.isAnnotationPresent(CourseInfoAnnotation.class);
                if(hasAnnotation){
                    CourseInfoAnnotation annotation = declaredMethod.getAnnotation(CourseInfoAnnotation.class);
                    System.out.println(annotation);
                }
            }
        }
    
        public static void main(String[] args) throws ClassNotFoundException {
            parseTypeAnnotation();
            System.out.println("-----------------------------------------------------");
            parseFieldAnnotation();
            System.out.println("-----------------------------------------------------");
            parseMethodAnnotation();
        }
    }
    
    out:
    @com.demo.annotation.CourseInfoAnnotation(courseIndex=1503, courseName=类名字, courseTag=类标签, courseProfile=类简介)
    @com.demo.annotation.CourseInfoAnnotation(courseIndex=1503, courseName=类名字, courseTag=类标签, courseProfile=类简介)
    -----------------------------------------------------
    @com.demo.annotation.PersonInfoAnnotation(gender=男, age=18, name=Joker, language=[Java, C++, Python])
    -----------------------------------------------------
    @com.demo.annotation.CourseInfoAnnotation(courseIndex=200, courseName=方法名字, courseTag=方法标签, courseProfile=方法简介)
    

    结果无误,此篇结。

  • 相关阅读:
    VB.NET中对象的克隆 利用了内存流内象和序列化
    关于对象组件编写的一点想法
    虽然有人说什么和平第一, 经济第一, 可是我怎么能不因为愤怒而发抖?
    用C# 调用MS speech引擎, 让电脑读文本, 或是存到WAV文件里去.
    抽空看了一下 dockpanel suite, 知道如何用了, 立此存照
    dn081A
    如何列出某类型的所有成员
    上周买了毛爷爷传
    【转载】MySQL双主双从高可用集群架构
    【转载】MySQL和Keepalived高可用双主复制
  • 原文地址:https://www.cnblogs.com/zqm-sau/p/13402543.html
Copyright © 2020-2023  润新知