美文网首页
Java注解详解

Java注解详解

作者: 阿龙的学与思 | 来源:发表于2018-10-07 21:25 被阅读0次

    虽然在平时开发中经常使用注解,却不知道如何自定义一个注解类型以及注解的实现原理。抽时间学习了一下,记录下来加深理解。

    1. 注解是什么

    之前看到一篇文章将注解理解为“标签”,感觉还是比较贴切的。我们可以把注解理解为给包、类、方法、字段打的一个标签,并利用java的反射机制对注解标注的类、方法或者字段进行相应的处理。

    2. 定义注解

    import java.lang.annotation.*;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Inherited
    public @interface AnnotationDemo {
        String name() default "";
    }
    

    利用关键字@interface声明一个注解类型,并利用@Retention、@Target、@Document、@Inherited等元注解对注解进行定义。

    • @Retention
      用于自定义注解类型的元注解。retention的意思是保留,@Retention用来定义自定义注解有效期。有三种取值:
    public enum RetentionPolicy {
        /**
         * 注解只在源码文件中保留,不会被编译器编译
         */
        SOURCE,
    
        /**
         * 会被编译到生成的class文件中,但不会在运行时保留
         */
        CLASS,
    
        /**
         * 运行时有效,可以通过反射机制读取到该注解定义
         */
        RUNTIME
    }
    
    • @Target
      用于定义注解的使用目标。我们知道注解可以用在包、类、方法、字段等字面量上,但不能将类的注解用到方法上,JDK使用@Target定义注解的使用目标。在java.lang.annotation.ElementType 枚举类型中定义了若干注解目标枚举常量:TYPE、FIELD、METHOD、PARAMETER、CONSTRUCTOR、LOCAL_VARIABLE等。
      ElementType.TYPE 用于定义类、接口、枚举注解;
      ElementType.METHOD 用于定义方法注解;
      ElementType.FIELD 用于定义字段注解;
      ElementType.CONSTRUCTOR 用于定义构造器注解;
    • @Document
      这个元注解比较简单,用于将该注解包含在Javadoc中
    • @Inherited
      在定义注解时加上@Inherited表示该注解具有"继承性",这里的继承性是指如果父类使用了自定义注解,则子类也继承了该自定义注解。举例说明:
    /**
     * 自定义注解AnnotationDemo、使用@Inherited表明该注解注解具有继承性
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Inherited
    public @interface AnnotationDemo {
        String name() default "";
    }
    
    /**
     * 使用自定义注解修饰的父类
     */
    @AnnotationDemo(name = "小明")
    public class Super {
        public void hello(){
            System.out.println("I am super!");
        }
    }
    
    /**
     * 子类继承Super超类
     * 由于Super被@AnnotationDemo注解修饰,Child子类自动被@AnnotationDemo修饰,
     *
     */
    public class Child extends Super {
        public void say(){
            System.out.println("I am child!");
        }
    }
    

    3. 注解的属性

    在自定义注解时可以为注解定义若干属性,如在上边定义的@AnnotationDemo注解中定义了一个名为name的属性,默认值为""。注解类型只有属性没有方法,且注解属性声明为属性名+(),属性类型可以为基本类型、String、Class、Enum等类型。注解的属性使用default关键字声明该属性的默认值,如果不为属性声明默认值,则必须在使用注解时为该属性赋值。
    特殊的:如果属性名为value,在使用注解时可以不指定属性名赋值。

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Inherited
    public @interface AnnotationDemo {
        String value() default "";
    }
    
    /**
     * @AnnotationDemo注解的属性名为value,使用时可以不指定属性名为属性赋值
     * 等价于@AnnotationDemo(value="super")
     */
    @AnnotationDemo("super")
    public class Super {
        public void hello(){
            System.out.println("hello world!");
        }
    }
    

    如果注解定义了多个属性,使用时不同的属性赋值用逗号分隔。

    4. 注解与反射

    正如在本文开头所说的注解相当于给类、方法、字段打的标签,对于运行时有效的注解需要通过JDK提供的反射机制来让标签起作用。下面通过一个简单的例子来演示如何利用反射机制使注解起作用。

    • 自定义一个注解@Run
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface Run {
        
    }
    
    • 使用定义的注解标注方法
    public class RunTest {
        @Run
        public void test1(){
            System.out.println("run test1");
        }
    
        public void test2(){
            System.out.println("run test2");
        }
    }
    
    • 通过反射API对@Run标记的方法执行调用
    public class Main {
    
        public static void main(String[] args) throws ClassNotFoundException{
            try {
                Class clazz = Class.forName("com.annotation.demo.test.RunTest");
                Method[] methods = clazz.getMethods();
                RunTest junitTest =(RunTest) clazz.newInstance();
                for (Method method : methods){
                    if (method.isAnnotationPresent(Run.class)){
                        method.setAccessible(true);
                        method.invoke(junitTest);
                    }
                }
            }catch (ClassNotFoundException e){
                e.printStackTrace();
            }catch (InstantiationException e){
                e.printStackTrace();
            }catch (IllegalAccessException e){
                e.printStackTrace();
            }catch (InvocationTargetException e){
                e.printStackTrace();
            }
        }
    }
    

    在这个例子中首先定义了一个@Run注解,并在RunTest中定义了两个方法。其中一个方法使用了@Run进行注解,另一个方法没有添加@Run注解,利用反射API对使用了@Run注解的方法进行调用。输出结果也正如我们所料:只有RunTest中的test1方法被执行。

    OK,java注解的介绍就到这里。

    你的关注是我持续更新的动力!

    相关文章

      网友评论

          本文标题:Java注解详解

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