美文网首页
Java基础回归之注解Annotation【基础篇】

Java基础回归之注解Annotation【基础篇】

作者: 林宥之 | 来源:发表于2016-10-31 10:22 被阅读0次

    Java注解(下文统称Annotation)是何方神圣?

    Java注解(Annotation)又称元数据,是一种代码级别的说明。我们经常见到的如: @Override(重写) 、@Deprecated(已废弃)......等等类似东西,就是Java注解。它是JDK1.5及以后版本引入的一个特性,与类class、接口interface、枚举enum是在同一个层次,它用@interface声明。它的作用是修饰编程元素。什么是编程元素呢?例如:包、类、构造方法、方法、成员变量等。

    ##为什么使用Annotation?
    笔者窃以为有以下几点:
    ~~- 简洁 ~~
    - 逼格
    - ...

    怎么使用Annotation?

    • Annotation语法
      1.以@interface关键字定义
      2.注解包含成员,成员以无参数的方法的形式被声明。其方法名和返回值定义了该成员的名字和类型。
      3.成员赋值是通过@Annotation(name=value)的形式赋值。
      4.注解需要标明注解的生命周期,注解的修饰目标等信息,这些信息是通过元注解实现。

    这里需要说明:注解分为两类,一类是元注解,另外一类是普通注解。所谓元注解就是修饰注解的注解。拿到一个注解,如何知道它是否是元注解呢?需要看它的元注解(无论是元注解还是普通注解都是有元注解的),如果看到这样的元注解:@Target(ElementType.ANNOTATION_TYPE),那么此时这个注解一定是元注解

    上面这4点就是Annotation的一般语法,估计很多同学会觉得太“官方”了。得,Talk is cheap,show me the f**king code。请看下面代码:

    @Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TestAnnotation {
        int[] value1();  
        String value2() default "默认string";
    }```
    上面代码段就是声明Annotation的一般语法,各位先别懵逼,接下来我根据上面代码段一一说明。
    1.```@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD })```元注解**@Target**,它代表我们当前定义的注解的**作用域**,即可以用来标记哪些编程元素(编程元素例如:包、类、构造方法、方法、成员变量等。)。**@Target**后面括号里面的几个**ElementType**参数就是它具体的作用域,**ElementType**可能取值有:
      (1) ElementType.**CONSTRUCTOR**:构造器上使用的注解
      (2) ElementType.**TYPE**:类,接口(包括注解)或者enum上使用的注解
      (3) ElementType.**FIELD**:在field(成员变量,包括静态非静态)属性,也包括enum常量上使用的注解
      (4) ElementType.**METHOD**:在方法声明上使用的注解
      (5) ElementType.**PARAMETER**:在参数上使用的注解
      (6) ElementType.**LOCAL_VARIABLE**:在局部变量上使用的注解
      (7) ElementType.**ANNOTATION_TYPE**:在注解上使用的**元注解**
      (8) ElementType.**PACKAGE**:在包上使用的注解
    根据上面讲解,我们现在可以知道```@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD })```就规定了我们定义的TestAnnotation只能标记在类|接口|enum|成员变量|方法上使用该注解。如果将该注解标记在其他未声明ElementType上,会报下图错。
    
    ![非法作用域.png](https://img.haomeiwen.com/i2954781/1aa2a50da07aad9e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    好了,继续讲解下一个!
    
    2.```@Retention(RetentionPolicy.RUNTIME)```注解需要标明注解的生命周期,这些信息是通过元注解**@Retention**实现。因此**@Retention**表示在什么级别保留此信息,相同的,它也有以下几种取值可能(按生命周期由短到长):
      (1)RetentionPolicy.**SOURCE**:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃
      (2)RetentionPolicy.**CLASS**:注解被保留到class文件,jvm加载class文件时候被遗弃。这是**默认的生命周期**(即未声明**@Retention**时默认的生命周期)。
      (3)RetentionPolicy.**RUNTIME**:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在,保存到class对象中,可以通过反射来获取。
    机智的你早已看透我们上面定义的TestAnnotation注解的生命周期了。
    
    3.```public @interface TestAnnotation```,这个不用多说,这是定义一个注解的关键字,类似于定义一个类:```public class HelloWorld```。
    4.最后看注解里面的```int[] value1();
       String value2() default "默认string";```,结合上文
    >注解包含成员,成员以无参数的方法的形式被声明
    
    我们应该明白这个语法,我们看到里面value2后面多了个default,default是默认值,表示如果你在使用该注解时,可以不手动为该成员value赋值,它会默认取default后面的默认值,其他不接default的成员value,则必须手动为其赋值。关于Annotation的基本语法到这里大概结束,下面说一下其他东西。
    
    > 一些细节:当成员value是个数组,用{}形式赋值,如:```@Target(value = { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})```;如果成员名称是value,在赋值过程中可以简写(注意,非名为value的不能简写)如:```@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})```;如果成员类型为数组,但是只赋值一个元素,则也可以简写如:```@Target(ElementType.FIELD)```
    
    
    - 举个栗子
    下面我们一起实现一个~~并没有什么卵用的~~注解栗子,来巩固一下注解的用法,后面我会用1-2篇文章来对注解做进阶的实战。我们先来实现获取注解的value值,然后打印出来。
    
    首先我们定义一个名为TestAnnotation注解
    ```java
    @Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})//声明作用域
    @Retention(RetentionPolicy.RUNTIME)//声明周期
    public @interface TestAnnotation {  
      int[] value1();  
      String value2() default "默认string";
    }
    

    然后我们写一个名为AnnotatedTarget类用来标记上面我们定义的注解

    @TestAnnotation(value1 = {1,3,5,7},value2 = "类上String类型的注解参数")
    public class AnnotatedTarget {  
    
      @TestAnnotation(value1 = {15,16},value2 = "Filed1上String类型的注解参数")  
      public String filed1; 
    
      @TestAnnotation(value1 = {17,18},value2 = "Filed2上String类型的注解参数") 
      public String filed2;  
    
      @TestAnnotation(value1 = {19,20},value2 = "私有Filed3上String类型的注解参数")  
      private String filed3; 
    
      @TestAnnotation(value1 = 2)  
      public static final int VALUE_FINAL = 2;  
    
      @TestAnnotation(value1 = 88)  
      public void method1 (){
          //no-op  
      }  
      @TestAnnotation(value1 = 88,value2 = "method2()私有方法注解String")  
      private void method2 (){
          //no-op  
      }
    }
    

    我们已经成功将我们定义的注解TestAnnotation标记在被标记类AnnotatedTarget上面了,相信看过上面那么详细(啰嗦)的讲解后稍微看一下就可以看明白了。接下来,我们如何将这些注解“扫描”出来,并且取出里面所有成员value的值并打印出来呢?这是我们需要编写一个处理这些注解的处理类,TestAnnotationProcessor,里面通过反射,将传入的类里面的TestAnnotation注解“扫描”出来。

    /**
     * TestAnnotation注解的处理器 
    */
    public class TestAnnotationProcessor {  
      public static void process(Class clazz) {    //类上注解    
        Annotation clazzAnnotation = clazz.getAnnotation(TestAnnotation.class);    
        if (null != clazzAnnotation) {      
          Log.e("TestAnnotationProcessor", "类上的TestAnnotation参数值:value1 = " + Arrays.toString(((TestAnnotation) clazzAnnotation).value1())+ " , value2 = " + ((TestAnnotation) clazzAnnotation).value2());    
      }   
    
     //方法上注解   
     for (Method method : clazz.getDeclaredMethods()) {     
       TestAnnotation annotation = method.getAnnotation(TestAnnotation.class);      
       if (annotation != null) {       
         Log.e("TestAnnotationProcessor", "方法上的TestAnnotation参数值:value1 = " + Arrays.toString(((TestAnnotation) annotation).value1()) + " , value2 = " + ((TestAnnotation) annotation).value2()); 
        }    
      }    
    //field上注解   
     Field[] declaredFields = clazz.getDeclaredFields();    
     for (Field field : declaredFields) {      
        TestAnnotation annotation = field.getAnnotation(TestAnnotation.class);      
        if (annotation != null) {        
          Log.e("TestAnnotationProcessor", "field上的TestAnnotation参数值:value1 = "+ Arrays.toString(annotation.value1()) + " , value2 = " + annotation.value2());      
          }    
        }  
      }
    }
    

    最后在MainActivity里面测试

    public class MainActivity extends AppCompatActivity {  
        @Override 
        protected void onCreate(Bundle savedInstanceState) { 
            //.....
            TestAnnotationProcessor.process(AnnotatedTarget.class);
        }
    }
    

    运行,可以看到打印出了下面的log:

    log.png

    最后请思考一下,如果我们把@Retention(RetentionPolicy.RUNTIME)生命周期改为另外两个,会有什么结果?为什么?

    别看这里,结果是没有打印任何东西

    结束

    到此关于Annotation基础篇结束,希望各位同学能有所收获,接下来会写两篇关于Annotation实战篇,敬请期待。也希望能指出文中写的不好或者不对的地方,欢迎转载但请尊重笔者劳动,转载请保留博文出处。谢谢大家。

    相关文章

      网友评论

          本文标题:Java基础回归之注解Annotation【基础篇】

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