本文目录
一、注解说明
注解(也被称为元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便地使用这些数据。
定义注解时,会需要一些元注解(meta--annotation),如@Target和@ Retention,这两个特性是我们必须要定义清楚的,一个是Target(注解目标),另一个就是Retention(注解生命周期,也叫声明周期)。
@Target用来定义你的注解将应用于什么地方(例如是一个方法或者一个域)。
@ Retention用来定义该注解在哪一个级别可用,在源代码中(SOURCE)、类文件中(CLASS)或者运行时(RUNTIME)。
在注解中,一般都会包含一些元素以表示某些值。当分析处理注解时,程序或工具可以利用这些值。注解的元素看起来就像接口的方法,唯一的区别是你可以为其指定默认值。
没有元素的注解称为标记注解(marker annotation),例如我在文章尾部自定义的注解。
Java目前只内置了三种标准注解(下一篇文章介绍),以及四种元注解。元注解专职负责注解其他的注解。
二、@Target注解
下面是注解@Target的源码,这里要一个ElementType[]数组。
/**
* @since 1.5
* @jls 9.6.4.1 @Target
* @jls 9.7.4 Where Annotations May Appear
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
而ElementType是枚举类型的,一共有10种取值类型,分别是(TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE)以下是ElementType的源码:
/**
* @author Joshua Bloch
* @since 1.5
* @jls 9.6.4.1 @Target
* @jls 4.1 The Kinds of Types and Values
*/
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
三、@Retention注解
3.1 注解源码
如下是@Retention注解的源码信息,它要求一个RetentionPolicy类型的取值。我们再看看RetentionPolicy是个什么类型对象。
/**
* @author Joshua Bloch
* @since 1.5
* @jls 9.6.3.2 @Retention
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
点击进入 RetentionPolicy 对象之后,发现它是一个枚举类型的,有三种取值(SOURCE, CLASS, RUNTIME),以下是 RetentionPolicy 枚举源码:
/**
* @author Joshua Bloch
* @since 1.5
*/
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
3.2 注解的作用
注解 @Retention 可以用来修饰注解,是注解的注解,这样的注解被称为元注解。
@Retention 注解有一个属性 value,是 RetentionPolicy 类型的,Enum RetentionPolicy 是一个枚举类型,从上面源码中可以看到。
这个枚举决定了 Retention 注解应该如何去保留,也可理解为 @Retention 搭配枚举类型 RetentionPolicy 使用。
按生命周期来划分可分为3类:
1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成 .class 文件的时候,被其标注的注解被遗弃;
2、RetentionPolicy.CLASS:注解被保留到class文件中,但jvm加载 .class 文件时候,被其标注的注解会被遗弃,这是默认的生命周期;
3、RetentionPolicy.RUNTIME:注解不仅被保留到 .class 文件中,jvm 加载 .class 文件之后,被其标注的注解仍然存在,所以这个时候才可能通过反射机制读取注解的信息,而前两个生命周期中,通过反射机制读取不到注解信息的;
这3个生命周期分别对应于:Java源文件(.java文件) ---> .class文件 ---> 内存中的字节码。
3.3 如何选择
那怎么来选择合适的注解生命周期呢?
首先要明确生命周期长度 RUNTIME > CLASS > SOURCE,所以后者能作用到的地方前者一定也能作用到,但是反过来,前者能作用到的地方后者就作用不到。
1、一般如果需要在运行时去动态获取注解信息,那只能用生命周期最长的 RUNTIME 标注了,比如 @Deprecated 就是使用 RetentionPolicy.RUNTIME 来标注的;
比如以下源码中我们常用到的注解:@Transient、@Deprecated、@Documented、@Inherited、@Retention、@Target ... 等等很多。
注解 @Deprecated,用来表示某个类或属性或方法已经过时,不想别人再用时,在属性和方法上用 @Deprecated 修饰。
2、如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解;
这个在源码中没有找到,但是参考其他博客写的 ButterKnife 就是被 RetentionPolicy.CLASS 标注的。
3、如果只是做一些检查性的操作,比如源码中的 @Override、@SuppressWarnings、@Native、@Generated 等就是被 RetentionPolicy.SOURCE 标注的。
使用注解 @Override 用在方法上,当我们想重写一个方法时,在方法上加 @Override,当我们方法的名字出错时,编译器就会报错。
使用注解 @SuppressWarnings 用来压制程序中出来的警告,比如在没有用泛型或是方法已经过时的时候。
四、自定义注解
看完源码之后,发现自定义注解其实也不难,比葫芦画瓢,我们也自定义注解试一下。
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface TestAnnotation {
}
定义完之后,就可以使用自己定义的注解了,例如:
@TestAnnotation
public class Sort {
public static void main(String[] args) {
System.out.println("自定义注解使用中.....");
}
}
【参考资料】
Thinking in Java(Fouth Edition) ---- Java编程思想(第四版):第20章 注解
Java注解:三种标准注解和四种元注解以及注解的元素:https://blog.csdn.net/weixin_44299027/article/details/105920418