java注解的前生今世

Java高级之注解

初识注解

概念入门

  • 在现实世界中,我们可以用沉鱼、落雁、闭月、羞花来形容女子容貌美丽;在动物世界中,我们可以用凶猛、强悍、睿智、威武来形容狮子老虎的地位;那么联系到在Java世界,我们应该用何来形容Java源码的类、方法、字段以及参数呢?答案是注解。

  • 注解Annotation,翻译过来便是:注释、释文。它可以为Java源码的类、方法、字段以及参数提供一些描述性的信息。

  • 元数据:我们将类、方法、字段以及参数当作是Java世界的数据,注解用来描述这些数据,那么,我们可以将注解理解为元数据Metadata,就是描述数据的数据。

  • 元注解:既然存在描述数据的数据,那么也应该存在描述注解的注解,例如我们常见的@Target@Retention等,它是用来修饰注解的注解。

简单理解

  • 我们可以将注解理解为代码中的特殊标记,这些标记并不会对代码的逻辑产生任何影响;这些对数据的描述可以在编译、加载以及运行时被读取,而如何使用这些注解,由各类工具比如代码分析工具、开发工具和部署工具等决定。

  • 举个不恰当的例子:如下,当子类重写了父类的hashCode()方法时,在重写的hashCode()方法上便会存在@Override注解,该注解并不会对代码的具体逻辑产生影响,但编译器在编译时,会检查该方法是否正确地实现了重写。也就是说,当编译器发现hashCode()方法上存在@Override,编译器会认为它重写了父类的方法,那么编译器就会去检查该方法的返回值、方法的参数等是否与父类的一致,以决定是否让该类编译通过。

    @Override
    public int hashCode() {
      return hash;
    }
    
  • 也就是说,通过使用注解,可以在不改变原有逻辑的情况下, 在源文件中嵌入一些补充信息。这些补充的信息可以被相应的工具识别。

  • 现在有这样的一种理解:在某种程度上,框架 = 注解 + 反射 + 设计模式。

注解的使用

  • java修饰符大家一定不陌生
    • 访问修饰符publicprotecteddefaultprivate可以保护对类、变量、方法和构造方法的访问。
    • 非访问修饰符static、final、abstract、synchronized、volatile等可以用来修饰类、方法、变量以及线程等,用以实现额外的一些需求。
  • 使用注解的方式很简单:
    1. 在该注解前面添加@符号;
    2. 将该注解当成一个修饰符来修饰它所支持的程序元素。

自定义注解

常见的注解

  • 编译时进行格式检查的注解
    • @Override:限定重写父类方法;
    • @Deprecated:表示所修饰的元素(类, 方法等)已过时;
    • @SuppressWarnings:抑制编译器警告。
  • 元注解:
    • @Target:用于指定被修饰的注解可以用于修饰哪些程序元素(类或接口、字段、方法、构造方法、方法参数);
    • @Retention:定义了注解的生命周期:SOURCECLASS(默认)、RUNTIME。如果想要通过反射获取注解,则必须将生命周期声明为RUNTIME
    • @Repeatable:注解是否可重复;
    • @Inherited:注解是否可继承。

自定义注解流程

  • 自定义一个注解,该注解名字为“MyExecute”,在运行时有效,作用范围在方法上;其中有两个成员变量:valueignore。代码如下:

    /**
     * 指定该注解的生命周期
     *   1、仅编译期:RetentionPolicy.SOURCE;
     *   2、仅class文件:RetentionPolicy.CLASS;
     *   3、运行期:RetentionPolicy.RUNTIME。
     *   只有在运行期才可以通过反射获取到
     */
    @Retention(RetentionPolicy.RUNTIME)
    /**
     * 指定该注解能够用于哪些程序元素
     *   1、类或接口:ElementType.TYPE;
     *   2、字段:ElementType.FIELD;
     *   3、方法:ElementType.METHOD;
     *   4、构造方法:ElementType.CONSTRUCTOR;
     *   5、方法参数:ElementType.PARAMETER。
     */
    @Target({ElementType.METHOD})
    //创建注解使用 @interface 关键字来声明
    public @interface MyExecute {
        /**
         * 1、成员变量无参数方法的形式来声明:方法名是该成员的名字、返回值是该成员的类型
         * 2、可以指定初始值,初始值用 default 关键字
         * 3、如果只要一个参数成员,建议使用参数名为value()
         * @return
         */
        String value() default "MyAnnotation";
        /**
         * 定义成员变量ignore()  类型为boolean  只有方法上该成员变量值为true时,该注解才起作用,
         * 需要反射来配合该注解。
         */
        boolean ignore() default false;
    }
    
  • 自定义一个类,类中有两个方法,在这两个方法上使用该注解:

    public class AnnotationTest {
        @MyExecute(value = "自定义的注解MyExecute,并且ignore为true!", ignore = true)
        public void ignoreTrue(){
            System.out.println("ignore为真,不调用,用来测试自定义注解!");
        }
        //不指定ignore的值,默认为false,会执行。
        @MyExecute(value = "自定义的注解MyExecute,并且ignore为默认值false!")
        public void ignoreFalse(){
            System.out.println("ignore为假,不忽视,调用方法,用来测试自定义注解!");
        }
    }
    
  • 测试自定义注解,自定义注解要有实际意义,需要配上注解的信息处理流程,也就是使用反射获取到注解并进行处理。

     public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
            //使用反射来使自定义注解有实际意义
            AnnotationTest annotationTest = new AnnotationTest();
            //1、获取当前类的Class实例 使用运行时类的.class属性来获取  
       			//也可以使用annotationTest.getClass()方法来获取
            Class clazz = AnnotationTest.class;
            //2、获取所有的方法,并拿到方法上的注解
            Method[] methods1 =clazz.getDeclaredMethods();
            for (Method method : methods1) {
                //判断这个方法是否存在自定义的注解
                if(method.isAnnotationPresent(MyExecute.class)){
                    //拿到该注解下的成员ignore,并判断是否为false
                    MyExecute myExecute = method.getAnnotation(MyExecute.class);
                    System.out.println("***************************************");
                    System.out.println("该方法名为:"+method.getName()+",它上面注解的value成员值为:"+myExecute.value());
                    if(!myExecute.ignore()){
                        System.out.println("当ignore为false时执行该方法!!!");
                        method.invoke(annotationTest,null);
                    }
                }
            }
        }
    
  • 运行结果

    image-20200802234539863

  • 注意

    1. 注解的成员如果没有默认值时,使用注解时,需要指定该成员的值,除非该成员有默认值;指定格式为:“参数名 = 参数值”;当只有一个成员且名称为value(),指定参数值时可以取消“value = ”;
    2. 自定义注解要想使其有意义,需要与注解的信息处理流程(反射)相配合。

原创不易,欢迎转载,转载时请注明出处,谢谢!
作者:潇~萧下
原文链接:https://www.cnblogs.com/manongxiao/p/13493759.html

posted @ 2020-08-12 23:30  潇~萧下  阅读(147)  评论(0编辑  收藏  举报