Java注解(Annotation)详解

Java注解(Annotation)详解

1.Annotation的概念

An annotation is a form of metadata, that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated. Annotations have no direct effect on the operation of the code they annotate.java

  • 注解是一种能够添加到Java源代码的元数据.
  • 类,方法,变量,参数,包均可以被注解.
  • 注解对注解的代码并无直接的影响.
  • 注解仅仅是个标记.注解之因此起做用是对其解析后作了相应的处理

2.Annotation分类

  • 标准Annotation
    • 标准Annotation是指Java内置的三个Annnotaion:
    • @Override:用于修饰此方法覆盖了父类的方法.
    • @Deprecated:用于修饰已通过时的方法.
    • @SuppressWarnnings:用于通知java编译器禁止特定的编译警告.
  • 元Annotation(注解的注解)
    • 元Annotation是用来定义Annotation的Annotation
    • 元Annotation能够定义Annotation的做用范围,使用在什么元素上等
    • 元注解共有四种@Retention, @Target, @Inherited, @Documented
  • 自定义Annotation

3.元Annotation

  • @Retention:注在其余的注解A上,用来讲明A的保留范围,可选值 SOURCE(源码时),CLASS(编译时),RUNTIME(运行时),默认为 CLASS
    • SOURCE:A只保留在源码中,A会被编译期忽略.(源码可用)
    • CLASS:A会经过编译保存在CLASS文件中,但会被JVM在运行时忽略,运行时不可见.(源码+CLASS可用)
    • RUNTIME:A会被JVM获取,并在运行时经过反射获取.(源码+CLASS+运行时都可用)
  • @Target:注在其余的注解A上,用来限制A可用修饰那些程序元素.未标注Target表示无限制,可修饰全部元素.
    • ANNOTATION_TYPE: A能够应用到其余注解上
    • CONSTRUCTOR: A可使用到构造器上
    • FIELD: A可使用到域或属性上
    • LOCAL_VARIABLE: A可使用到局部变量上。
    • METHOD: A可使用到方法上。
    • PACKAGE: A可使用到包声明上。
    • PARAMETER: A可使用到方法的参数上
    • TYPE: A可使用到类,接口(包括注解),或枚举的声明上
  • @Inherited:默认状况下,父类的注解不会被子类继承.
    • Inherited注在其余的注解A上.
    • 只有当A是注解在类Class上面,Inherited才会起做用,其余任何状况下无效果.
    • 当A注解在类C上面,则C的全部子孙类,都会继承应用A注解;
  • @Documented:注在其余的注解A上,A将会做为Javadoc产生的文档中的内容。注解都默认不会成为成为文档中的内容。

4.自定义Annotation

  1. 建立自定义Annotation流程android

    • public @interface 自定义注解名称
      public @interface CustomAnnotation{***}
          
      复制代码
    • 设置自定义Annotation的保留范围和目标,Retention和Target是最重要的两个元Anitation.
      @Retention( RetentionPolicy.RUNTIME )
      @Target( ElementType.TYPE )
      public @interface CustomAnnotation{***}
      复制代码
    • 设置自定义Annotation的注解参数(注解成员)
      • 注解参数支持的数据类型
        • 全部基本数据类型(int,float,boolean,byte,double,char,long,short)
        • String类型
        • Class类型
        • enum类型
        • Annotation类型
        • 以上全部类型的一维数组
      • 注解参数声明方式
        @Retention( RetentionPolicy.RUNTIME )
        @Target( ElementType.TYPE )
        public @interface CustomAnnotation{
            //注解参数类型能够是1-6中任一种,包括枚举
            public enum Skill{JAVA,ANDROID,IOS}
            Skill mySkill() default Skill.ANDROID;
            String attr1();
            //可使用default设置默认值
            int attr2() default 100;
            //修饰符只能用public
            public boolean attr3() default false;
        }
        @Retention( RetentionPolicy.RUNTIME )
        @Target( ElementType.TYPE )
        public @interface CustomAnnotation{
            //只有一个注解参数,使用value()
            String value();
        }
        复制代码
        • 自定义Annotation的参数类型必须知足上一条1到6中的范围.
        • 自定义Annotation的参数访问方法只能是public,或不写.
        • 自定义Annotation的参数能够加 default 设置默认值.
        • 自定义Annotation若只有1个参数,使用value().
  2. 自定义Annotation的注解参数的默认值数组

    注解元素必须有肯定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null。所以, 使用空字符串或0做为默认值是一种经常使用的作法。这个约束使得处理器很难表现一个元素的存在或缺失的状态,由于每一个注解的声明中,全部元素都存在,而且都具备相应的值,为了绕开这个约束,咱们只能定义一些特殊的值,例如空字符串或者负数,一次表示某个元素不存在,在定义注解时,这已经成为一个习惯用法。bash

    示例:
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AnotherAnnotation{
        String author() default "";
        int age() default -1;
    }
    复制代码
  3. 使用刚刚建立的自定义注解app

    @CustomAnnotation(attr1 = "属性1", attr2 = 90, attr3 = true)
    public class AnnotationTestClass{
        ***
    }
    复制代码

5.Annotation解析

  • 运行时 Annotation 解析框架

    运行时 Annotation 指 @Retention 为 RUNTIME 的 Annotationide

    • Class,Method,Field中都有如下3个方法能够调用
      • public T getAnnotation(Class annotationClass) 按照传入的参数获取指定类型的注解。返回null说明当前元素不带有此注解。
      • public final boolean isAnnotationPresent(Class<? extends Annotation> annotationType) 检查传入的注解是否存在于当前元素。
      • public Annotation[] getAnnotations() 返回该元素的全部注解,包括没有显式定义该元素上的注解。
    • 运行时 Annotation 解析示例
      public void testCustomAnnotation() {
          try {
              Class cls = Class.forName("com.jet.annotation.AnnotationTestClass");
              CustomAnnotation customAnnotation = (CustomAnnotation)cls.getAnnotation(CustomAnnotation.class);
              System.out.println("customAnnotation mySkill:" + cus.mySkill());
              System.out.println("customAnnotation attr1:" + cus.attr1());
              System.out.println("customAnnotation attr2:" + cus.attr2());
          } catch (ClassNotFoundException e) {
              e.printStackTrace();
          }
      }
      复制代码
  • 编译时 Annotation 解析工具

    编译时 Annotation 指 @Retention 为 CLASS 的 Annotation,甴编译器自动解析性能


6.编译时Annotation解析

编译时Annotation解析 相对复杂,下面单独进行分析ui

首先申明:下面内容仅仅讨论 编译时Annotation的解析

  1. 编译时Annotation的解析,是由Annotation Processor完成
  2. Annotation Processor(注解处理器)
    • 注解处理器是一个在javac中的,用来在编译时扫描和处理注解的工具
    • 咱们能够为特定的注解,注册自定义的注解处理器
    • 在编译期间,JVM会自动运行注册过的注解处理器
    • 一个注解的Annotation Processor,以Java代码(或者编译过的class)为输入,生成.java文件做为输出.这意味着咱们能够生成新的Java代码!这些生成的Java代码是在生成的.java文件中,新生成的.java文件会和普通的手动编写的Java源代码同样被javac编译
  3. 每个注解处理器都是继承于AbstractProcessor,须要关注的有如下4个方法
public abstract class AbstractProcessor implements Processor {

    //对一些工具进行初始化
    public synchronized void init(ProcessingEnvironment processingEnv)
    
    //你在这里定义你的注解处理器注册到哪些注解上,必须指定;
    //它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称
    public Set<String> getSupportedAnnotationTypes()
    
    //指定该注解处理器使用的JAVA版本,一般返回SourceVersion.latestSupported()
    public SourceVersion getSupportedSourceVersion()
    
    //真正生成java代码的地方
    //annotations:请求处理的注解类型集合
    //roundEnv:可让你查询出包含特定注解的被注解元素,至关于“有关全局源码的上下文环境”
    //若是返回 true,则这些注解已声明而且不要求后续 Processor 处理它们;
    //若是返回 false,则这些注解未声明而且可能要求后续 Processor 处理它们
    public abstract boolean process(Set<? extends TypeElement> annotations,RoundEnvironment roundEnv)
    
}
复制代码
  1. 自定义注解处理器,就是继承AbstractProcessor并重写上述4个方法

关于编译时Annotation解析,这里推荐一篇文章【Android】注解框架(三)-- 编译时注解,手写ButterKnife,按照文章上面流程敲一遍代码,相信能够对自定义注解的建立及解析有一个深刻的了解!

7.注解对App的影响

  1. 运行时注解对性能有影响,编译时注解对App的性能没有影响.
  2. 运行时注解的解析彻底依赖于反射,反射的效率比直接调用慢,只有过多使用运行时注解时才对效率有必定影响
  3. java文件编译成.class文件。再对class文件进行打包等一系列处理。生成apk。最终才运行到咱们的手机上。而编译时注解,就是在java编译生成.class文件这一步进行的操做。根本和咱们的apk运行无关,不存在影响性能的问题;
  4. 编译时注解库,在app的引用通常以下:
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:27.1.1'
    //API模块:android library
    implementation project(':butterknife')
    //Annotation模块:java library
    implementation project(':butterknife-annotations')
    //Annotation解析模块:java library
    annotationProcessor project(':butterknife-compiler')
}
复制代码

实际在打包生成APK的过程当中,只有 API模块和Annotation模块 会被打包进APK,Annotation解析模块是提供给IDE使用的,在咱们APK中并不存在.

相关文章
相关标签/搜索