注解

注解:

一、 标准注解:@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=方法简介)

结果无误,此篇结。

原文地址:https://www.cnblogs.com/zqm-sau/p/13402543.html