zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    [编程题] 回文数问题
    [编程题] 以字符串的形式读入两个数字,再以字符串的形式输出两个数字的和
    [编程题] 合并有序链表
    [编程题] 借用栈实现链表反转
    [编程题] 二叉树求深度
    pat 甲级 1045 ( Favorite Color Stripe ) (动态规划 )
    pat 甲级 1034 ( Head of a Gang )
    PAT public bike management (30)
    PAT 甲练习 1003 Emergency
    vim 自动代码格式调整
  • 原文地址:https://www.cnblogs.com/manongxiao/p/13493759.html
Copyright © 2011-2022 走看看