1.8 Java 注解annotation

1.1 注解声明

Java注解Annotation,有声明注解和元注解

  • 元注解:Java提供的元注解,所谓元注解就是标记其他注解的注解(@Target,@Retention),即@Target(ElementType.ANNOTATION_TYPE)
  • 声明注解:用@interface声明的注解

@Target

@Target 用来约束注解可以应用的地方(如类,方法,字段)

/*
 * @since 1.5
 * @jls 9.6.4.1 @Target
 * @jls 9.7.4 Where Annotations May Appear
 */
@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();
}
public enum ElementType {
    /** 标明该注解可以用于类、接口(包括注解类型)或enum声明 */
    TYPE,

    /** 标明该注解可以用于字段(域)声明,包括enum实例 */
    FIELD,

    /** 标明该注解可以用于方法声明 */
    METHOD,

    /** 标明该注解可以用于参数声明 */
    PARAMETER,

    /** 标明注解可以用于构造函数声明 */
    CONSTRUCTOR,

    /** 标明注解可以用于局部变量声明 */
    LOCAL_VARIABLE,

    /** 标明注解可以用于注解声明(应用于另一个注解上)*/
    ANNOTATION_TYPE,

    /** 标明注解可以用于包声明 */
    PACKAGE,

    /**
     * 标明注解可以用于类型参数声明(1.8新加入)
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * 类型使用声明(1.8新加入)
     * @since 1.8
     */
    TYPE_USE
}

当声明的注解未指定Target值时,则此注解可以用于任何元素之上,多个值使用{}包含并用逗号隔开,如下:

@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})

@Retention

用来约束注解的保留的地方,有三个值,源码级别(source),类文件级别(class)或者运行时级别(runtime)

/*
 * @author  Joshua Bloch
 * @since 1.5
 * @jls 9.6.3.2 @Retention
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}
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
}

1.2 注解元素

如何声明一个注解元素:

@Target(ElementType.TYPE)//只能应用于类上
@Retention(RetentionPolicy.RUNTIME)//保存到运行时
public @interface DBTable {
    String name() default "";
}

上面这个例子说明,DBTable这个注解声明了一个String类型的name的元素。

default关键字 表示默认值

name() 方法不能传参,否则编译出错:

@interface members may not have parameter

在使用注解元素的时候,使用键值对的方式,以方法名作为key,value表示元素的返回值

//在类上使用该注解
@DBTable(name = "MEMBER")
public class Member {
    //.......
}

注解支持的元素数据类型除了String,还支持如下数据类型:

  • 所有基本类型(使用基本类型但不允许使用任何包装类型)
  • String
  • Class
  • enum
  • Annotation
  • 上面类型的数组

示例:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Reference{
    boolean next() default false;
}

public @interface AnnotationElementDemo {
    //枚举类型
    enum Status {FIXED,NORMAL};

    //声明枚举
    Status status() default Status.FIXED;

    //布尔类型
    boolean showSupport() default false;

    //String类型
    String name()default "";

    //class类型
    Class testCase() default Void.class;

    //注解嵌套
    Reference reference() default @Reference(next=true);

    //数组类型
    long[] value();
}

编译器对元素的默认值有些过分挑剔。

  • 元素必须要有值:要么定义元素的时候设置默认值,要么在使用注解的时候提供元素的值
  • 元素的值不能为null。

1.3 注解不支持继承

注解是不支持继承的,因此不能使用关键字extends来继承某个@interface,但注解在编译后,编译器会自动继承java.lang.annotation.Annotation接口,这里我们反编译前面定义的DBTable注解:

import java.lang.annotation.Annotation;
//反编译后的代码
public interface DBTable extends Annotation
{
    public abstract String name();
}

虽然反编译后发现DBTable注解继承了Annotation接口,请记住,即使Java的接口可以实现多继承,但定义注解时依然无法使用extends关键字继承@interface。

1.4 注解快捷方式

快捷方式就是注解中定义了名为value的元素,并且在使用该注解时,如果该元素是唯一需要赋值的一个元素,那么此时无需使用key=value的语法,而只需在括号内给出value元素所需的值即可。这可以应用于任何合法类型的元素,记住,这限制了元素名必须为value,简单案例如下:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface IntegerVaule{
    int value() default 0;
    String name() default "";
}

//使用注解
public class QuicklyWay {

    //当只想给value赋值时,可以使用以下快捷方式
    @IntegerVaule(20)
    public int age;

    //当name也需要赋值时必须采用key=value的方式赋值
    @IntegerVaule(value = 10000,name = "MONEY")
    public int money;

}

2.1 Java内置注解与其它元注解

Java常用内置注解三个:
  • @Override:用于标明此方法覆盖了父类的方法
  • @Deprecated:用于标明已经过时的方法或类
  • @SuppressWarnnings:用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告.其内部有一个String数组,主要接收值如下:
    • deprecation:使用了不赞成使用的类或方法时的警告;
    • unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型;
    • fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告;
    • path:在类路径、源文件路径等中有不存在的路径时的警告;
    • serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告;
    • finally:任何 finally 子句不能正常完成时的警告;
    • all:关于以上所有情况的警告。
Java常用内置元注解:
  • @Target
  • @Retention
  • @Documented 被修饰的注解会生成到javadoc中
  • @Inherited 可以让注解被继承,但这并不是真的继承,只是通过使用@Inherited,可以让子类Class对象使用getAnnotations()获取父类被@Inherited修饰的注解

3. 注解与反射机制

经过反编译后, 知道Java所有注解都继承了Annotation接口。也就是说 Java使用Annotation接口代表注解元素。
为了运行时能准确获取到注解的相关信息,Java在java.lang.reflect 反射包下新增了AnnotatedElement接口,它主要用于表示目前正在 VM 中运行的程序中已使用注解的元素,通过该接口提供的方法可以利用反射技术地读取注解的信息,如反射包的Constructor类、Field类、Method类、Package类和Class类都实现了AnnotatedElement接口

示例:

注解类:DocumentA

import java.lang.annotation.*;

@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DocumentA {
}

被注解修饰类A:

@DocumentA
public class A {
}

测试类DocumentDemo:

public class DocumentDemo extends  A{


    public static void main(String[] args) {
        Class clazz = DocumentDemo.class;
        //根据指定注解类型获取该注解
        DocumentA documentA = clazz.getAnnotation(DocumentA.class);
        System.out.println("A:"+documentA);

        //获取该元素上的所有注解,包含从父类继承
        Annotation[] annotations = clazz.getAnnotations();
        System.out.println("an:"+ Arrays.toString(annotations));

        //获取该元素上的所有注解,但不包含继承!
        Annotation[] an2 = clazz.getDeclaredAnnotations();
        System.out.println("an2:"+ Arrays.toString(an2));

        //判断注解DocumentA是否在该元素上
        boolean b=clazz.isAnnotationPresent(DocumentA.class);
        System.out.println("b:"+b);

    }

}


-----------------

输入结果:
A:@com.littlezan.test.testannotation.DocumentA()
an:[@com.littlezan.test.testannotation.DocumentA()]
an2:[]
b:true

4. 运行时注解处理器

通过注解Api和反射包中与注解相关的Api,解析注解内容来组装需要的数据

5. Java 8中注解增强

5.1 元注解@Repeatable

元注解@Repeatable是JDK1.8新加入的,它表示在同一个位置重复相同的注解。在没有该注解前,一般是无法在同一个类型上使用相同的注解的

//Java8前无法这样使用
@FilterPath("/web/update")
@FilterPath("/web/add")
public class A {}

Java8前如果是想实现类似的功能,我们需要在定义@FilterPath注解时定义一个数组元素接收多个值如下

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface FilterPath {
    String [] value();
}

//使用
@FilterPath({"/update","/add"})
public class A { }

但在Java8新增了@Repeatable注解后就可以采用如下的方式定义并使用了

//使用Java8新增@Repeatable原注解
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(FilterPaths.class)//参数指明接收的注解class
public @interface FilterPath {
    String  value();
}

参考链接:

理解Java注解

你可能感兴趣的:(1.8 Java 注解annotation)