注解(Annotation)也被称为元数据,是JDK1.5开始引入的一个非常有用的特性。注解用来对类、接口、字段、方法等元素进行说明,可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。
注解是一个接口,它以标签的形式存在于代码中,只是作为标识存在,而不会直接影响到程序的语义;程序可以通过反射来获取指定元素的Annotation对象,然后通过Annotation对象来获取注解里面的元数据,并进行相关的操作。
1、定义
注解与类、接口、枚举处于同一层级,跟定义类使用class关键字、定义枚举使用enum关键字类似,注解使用@interface关键字进行定义
public @interface 自定义注解名称 {
}
以JDK内置系统注解@Override为例,@Override的作用是执行基本的编译时检查,用于限定重写父类方法。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
我们能看到@Override除了使用@interface进行定义之外,还使用到了元注解
元注解
用来描述数据的数据我们称之为元数据,那么元注解就是对其他注解进行标注的注解。元注解由Java API提供,是专门用于定义注解的注解,一共有四个
类型 | 说明 |
---|---|
@Target | 用于指定注解可以被用于什么地方,可用的值由ElementType进行定义。注意,注解没有明确指定Target值时可以被用于任何元素上 |
@Retention | 注解的保留策略,表示需要在什么级别保存该注解信息,由RetentionPolicy枚举定义。未指定Retention值时默认是CLASS |
@Documented | 表示注解会被包含在Java API文档中 |
@Inherited | 允许子类继承父类的注解 |
ElementType
TYPE:类、接口(包括注解类型)、enum声明
FIELD:属性声明 (不包括enum实例)
METHOD:方法声明
PARAMETER:参数声明
CONSTRUCTOR:构造器声明
LOCAL_VARIABLE:局部变量声明
ANNOTATION_TYPE:注解类型声明
PACKAGE:包声明
TYPE_PARAMETER:类型参数声明(1.8新增)
TYPE_USE:类型使用声明(1.8新增)
RetentionPolicy
SOURCE:注解将被编译器丢弃(该类型的注解信息只会保留在源码里,源码经过编译后,注解信息会被丢弃,不会保留在编译好的class文件里)
CLASS:注解在class文件中可用,但会被VM丢弃(该类型的注解信息会保留在源码里和class文件里,在执行的时候,不会加载到虚拟机(JVM)中)
RUNTIME:运行期也保留注解信息,因此可以通过反射机制读取注解的信息(源码、class文件和执行的时候都有注解的信息)
示例
定义一个可以用在类、接口、枚举上的自定义注解,该注解后期需要用来演示解析操作(需要反射机制能够读取注解信息),所以在运行期也需要保留
/**
* 自定义注解
*/
@Target( ElementType.TYPE)
@Retention( RetentionPolicy.RUNTIME)
public @interface CustomClassAnnotation {
}
除了注解名称、可用范围、保留级别之外,很多时候我们还需要注解附加一些属性,以便进一步对数据进行描述
需要注意的是,注解的属性类型只允许是基本数据类型、String、Enum、Annotation以及Class
/**
* 自定义类注解
*/
@Target( ElementType.TYPE)
@Retention( RetentionPolicy.RUNTIME)
@Documented
public @interface CustomClassAnnotation {
String name() default "";
}
/**
* 自定义属性注解
*/
@Target( ElementType.FIELD)
@Retention( RetentionPolicy.RUNTIME)
@Documented
public @interface CustomFiledAnnotation {
String name() default "";
long length();
CUSTOMTYPE type() default CUSTOMTYPE.VARCHAR;
}
/**
* 自定义注解属性type的可选值
*/
public enum CUSTOMTYPE {
VARCHAR,
BIGINT
}
2、使用
自定义注解的使用跟系统内置注解使用方式一致,注解的属性值如果有默认值可以不进行赋值
@CustomClassAnnotation( name = "customClass")
public class CustomTest {
@Column( name = "customField", type= CUSTOMTYPE.VARCHAR, length = 255)
private String field;
}
3、访问
注解本身是没有任何效果的,单纯的使用注解并不比注释更有用处。所以在自定义注解的过程中,很重要的一部分就是创建用于对注解进行处理的逻辑。JDK1.5扩展了反射机制API,在 java.lang.reflect 包下新增了Annotation相关接口,以便读取运行时Annotation信息
相关的方法可以自行查看API文档,这里我们直接上示例
Class<?> ct = CustomTest.class;
// 获取类上指定类型的注解CustomClassAnnotation
CustomClassAnnotation[] ccas = ct.getAnnotationsByType( CustomClassAnnotation.class);
if( ccas != null && ccas.length > 0){
CustomClassAnnotation cca = ccas[0];
String name = cca.name();
}
// 获取类上所有注解
Annotation[] ans = ct.getAnnotations();
for( Annotation an : ans){
// 判断是否CustomClassAnnotation注解
if( an instanceof CustomClassAnnotation){
}
}
Field[] fields = ct.getDeclaredFields();
if( fields != null && fields.length > 0){
for( Field field : fields){
// 获取属性上指定注解 CustomFiledAnnotation
CustomFiledAnnotation[] cfa = field.getAnnotationsByType( CustomFiledAnnotation.class);
}
}