美文网首页
1.8 Java 注解annotation

1.8 Java 注解annotation

作者: littlezan | 来源:发表于2018-10-19 14:11 被阅读11次

    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

          本文链接:https://www.haomeiwen.com/subject/tubyzftx.html