Java注解Annotation

框架的底层都是注解和反射。

1、什么是注解

英文:Annotation

官方:java注解用于为java代码提供元数据。

注解像类和接口一样新建,如建立一个叫ThisIsAnnotation的注解。

public @interface ThisIsAnnotation {
      ...
}

注解使用@+注解名可以标记在类、方法、属性甚至方法参数上。

其中的@interface是一个关键字,这个关键字表示:这个东西是继承了java.lang.annotation.Annotation接口,表示它是一个注解。

2、内置注解

@Override 表示重写

@Deprecated 表示已被废弃,不推荐使用

@SuppressWarnings 用来抑制编译时的警告信息

@FunctionalInterface 表示函数式接口

@SafeVarargs 专门为抑制“堆污染”警告提供的

3、元注解

元注解就是用来修饰注解的注解,用来使该注解附带别的功能。比如我们看@overrid的源码:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

上面就有两个元注解。元注解有五种:

  1. @Retention

    意为保留、保持。即指定注解保留到什么阶段,是源码(编译)、字节码(类加载)还是运行阶段。

    // 注解仅存在于源码中,在class字节码文件中不包含
    @Retention(RetentionPolicy.SOURCE)
    // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
    @Retention(RetentionPolicy.CLASS)
    // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
    @Retention(RetentionPolicy.RUNTIME)
    

    如overrid注解只是一个标记作用,只是给阅读代码的人看的,所以只需要保留在源码阶段即可。

  2. @Target

    即注解的目标是谁。有这些:

    @Target(ElementType.TYPE)          //作用接口、类、枚举、注解
    @Target(ElementType.FIELD)         //作用属性字段、枚举的常量
    @Target(ElementType.METHOD)        //作用方法
    @Target(ElementType.PARAMETER)     //作用方法参数
    @Target(ElementType.CONSTRUCTOR)   //作用构造函数
    @Target(ElementType.LOCAL_VARIABLE)//作用局部变量
    @Target(ElementType.ANNOTATION_TYPE)//作用于注解
    @Target(ElementType.PACKAGE)       //作用于包
    @Target(ElementType.TYPE_PARAMETER)//作用于泛型,即泛型方法类接口, 1.8加入
    @Target(ElementType.TYPE_USE)      //可以用于标注任意类型除了class, 1.8加入
    
  3. @Document

    它的唯一作用是能够将注解中的元素包含到 Javadoc 中去。

  4. @Inherited

    表示这个注解具有继承性。即,如果一个类被具有继承性的注解标记了,则子类会继承父类的注解。

  5. @Repeatable

    表示这个注解具有重复性,即可以重复作用于一个对象多次,但每次注解的属性不同。

4、自定义注解

通过@interface标记该类是一个注解,假设自定义了一个注解如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
      String name();
      int id() default 101;
      String[] friend() default {"Alis","Bob"};
}

4.1、属性

注解里可以有它的属性,如上面的name、id、friend。属性写法要加上括号,后面还可以加上默认值。如果没有默认值,使用该注解时就要指定该属性的值。

属性的类型可以为以下六种:基本数据类型,String,枚举类型,注解类型,Class类型,以上类型的一维数组类型。

4.2、使用

使用时在可以标注的位置使用该注解,并给出必要的参数。如上例,id、friend有默认参数,就可以只给出name。

@Test(name = "cc")

5、通过反射获取注解属性的值

要获取某个注解属性的值就要从它修饰的东西入手(类、方法或属性),先通过反射获取到注解修饰的东西。反射获取到的对象可以使用下面几个方法:

/**是否存在对应 Annotation 对象*/
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
      return GenericDeclaration.super.isAnnotationPresent(annotationClass);
  }
/**获取 Annotation 对象(如果只有一个)*/
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
    Objects.requireNonNull(annotationClass);

    return (A) annotationData().annotations.get(annotationClass);
}
/**获取所有 Annotation 对象*/   
public Annotation[] getAnnotations() {
    return AnnotationParser.toArray(annotationData().annotations);
}

仍以上述的Test注解为例,假设我们有一个User类,想通过注解给类传值进行参数设置:

@Test(name = "cui")
public class User {
    public String name;

    public User(){
        try {
	    //获取注解修饰的类
            Class cls = Class.forName("com.User");
	    //判断该类有没有注解修饰
            if(cls.isAnnotationPresent(JustTest.class)){
	        //拿到这个注解
                JustTest annotation = (JustTest) cls.getAnnotation(JustTest.class);
                //获取注解中的属性并为name赋值
                this.name = annotation.name();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
原文地址:https://www.cnblogs.com/cpcpp/p/14255042.html