前言
注解是一种元数据形式,为编译器提供信息。
目录
一、注解是什么
Annotations (注解),又称 Java 标注,是 JDK5.0 引入的一种注释机制。元数据的一种形式,提供有关于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有直接影响。
二、如何使用
注解声明
使用@interface关键字来声明注解,需指定的元注解有两个 :@Target和@Retention
1 | //定义注解 |
Java中所有的注解,默认实现Annotation接口:
1 | package java.lang.annotation; |
元注解
适用于其他注解的注解称为 meta-annotations (元注解)。在 java.lang.annotation 中定义了几种元注解类型。
@Retention指定标记注解的存储方式,注解保留级别
RetentionPolicy.SOURCE- 标记的注解仅保留在源码级别中,并被编译器忽略。RetentionPolicy.CLASS- 标记的注解在编译时由编译器保留,但 Java 虚拟机(JVM)会忽略。RetentionPolicy.RUNTIME- 标记的注解由 JVM 保留,因此运行时环境可以使用它。
注:SOURCE < CLASS < RUNTIME,即CLASS包含了SOURCE,RUNTIME包含SOURCE、CLASS。
@Target指定标记注解的作用元素访问,以限制可以应用注解的 Java 元素类型
ElementType.ANNOTATION_TYPE可以应用于注解类型。ElementType.CONSTRUCTOR可以应用于构造函数。ElementType.FIELD可以应用于字段或属性。ElementType.LOCAL_VARIABLE可以应用于局部变量。ElementType.METHOD可以应用于方法级注解。ElementType.PACKAGE可以应用于包声明。ElementType.PARAMETER可以应用于方法的参数。ElementType.TYPE可以应用于类的任何元素。
@Documented表示无论何时使用指定的注解,都应使用 Javadoc 工具记录这些元素。
@Inherited表示注解类型可以从超类继承。(默认情况下不是这样。)当用户查询注解类型并且该类没有此类型的注解时,将查询类的超类以获取注解类型。此注解仅适用于类声明。
@Repeatable Java SE 8 中引入的注解表明标记的注解可以多次应用于同一声明或类型使用。
注解类型元素
在上文元注解中,允许在使用注解时传递参数,我们也能让自定义注解的主体包含 annotation type element (注解类型元素) 声明,它们看起来很像方法,可以定义可选的默认值。
1 | //定义注解 |
1 | //使用注解 |
注:在使用注解时,如果定义的注解中的类型元素无默认值,则必须进行传值。
三、运用场景
由注解的三个不同保留级别可知,注解可用于源码、字节码、运行时三个场景,具体案例:
源码-> APT(注解处理器),在编译期获取注解和注解声明的类信息,用于生成辅助类。语法检查@IntDef
字节码->字节码增强(字节码插桩),在编译出Class后,通过注解区分是否修改Class数据以实现修改代码逻辑目的。
运行时->反射,通过反射动态获取注解及其元素,从而完成不同的逻辑判断。
IDE语法检查
在Android开发中, support-annotations 与 androidx.annotation 中均有提供 @IntDef 注解,此注解的定义如下:
1 | @Retention(SOURCE) //源码级别注解 |
此注解的意义在于能够取代枚举,实现如方法入参限制。
如果使用枚举能够实现为:
1 | public enum Type{ |
Java中Enum(枚举)的实质是特殊单例的静态成员变量,在运行期所有枚举类作为单例,全部加载到内存中。 比常量多5到10倍的内存占用。
现在为了进行内存优化,我们现在不再使用枚举,
1 | public static final int TYPE_1 = 1; |
然而此时,调用 test 方法由于采用基本数据类型int,将无法进行类型限定。此时使用@IntDef增加自定义注解:
1 | public static final int TYPE_1 = 1; |
此时,我们再去调用 test 方法,如果传递的参数不是 TYPE_1 或者 TYPE_2 则会显示 Inspection 警告(编译不会报 错)。
总结
注解的保留级别有不同的应用场景:
作用于源码级别的注解,可提供给IDE语法检查、APT等场景使用。
定义为 CLASS 的注解,会保留在class文件中,但是会被虚拟机忽略(即无法在运行期反射获取注解)。应用场景为字节码操作。如:AspectJ、热修复Roubust中应用。
注解保留至运行期,意味着我们能够在运行期间结合反射技术获取注解中的所有信息。