在上一篇文章中,我们介绍了注解(Annotation)
的相关概念,对于 java1.5 引入的这种新特性,我们可以称其为一种特殊的注释。之所以说它特殊是因为不同于普通注释(comment)
只能存在于源码,而且还能在程序的编译期跟运行期中存在,会最终编译成一个.class文件。理所应当,注解会比普通注解起到更多的作用。
注解为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻方便地使用这些数据(通过 解析注解 来使用这些数据)。要想深入的理解并使用注解,必须能定义自己的注解,所以了解java
提供的注解定义语法就非常重要了。
包java.lang.annotation
中包含所有定义自定义注解所需用到的元注解和接口。比如接口java.lang.annotation.Annotation
是所有注解都继承的接口,并且是自动继承,不需要定义时指定,类似于所有类都自动继承Object。首先我们先来了解什么是元注解。
元注解
元注解是 Java 本身提供,是专门用来定义注解的注解。被用来提供对其他注解类型作说明。Java提供了四个标准的元注解类型:
- @Target,
- @Retention,
- @Documented,
- @Inherited
@Target
@Target
标识 Annotation 可以被使用在什么地方。
使用方式:@Target(ElementType.METHOD)
。其可能的 ElementType 取值包括:
- CONSTRUCTOR --> 构造器(构造函数)的声明
- FIELD ---> 域声明,包括 ENUM(枚举)实例
- LOCAL_VIRIEBLE ---> 局部变量声明
- METHOD ---> 方法声明
- PACKAGE ---> 包声明
- PARAMETER ---> 参数声明
- *TYPE ---> 类,接口声明。包括注解类型和ENUM类型
@Retention
决定了 Annotation 的生命周期,即被保留的时间长短或者说在什么范围内有效。Annotation 一共有三种生命周期,有的注解只出现在源代码中;有的注解被被编译在class文件中,但被虚拟机忽略;有的则是被虚拟机加载,一直到程序运行都还存在。
使用方式:@Retention(RetentionPolicy = RUNTIME)。其可能的 **RetentionPolicy ** 取值包括:
- SOURCE -> 在源文件中有效(即源文件保留)
- CLASS -> 在class文件中有效(即class保留)
- RUNTIME -> 在运行时有效(即运行时保留)
@Documented
描述 Annotation 应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。被例如javadoc此类的工具文档。
@Inherited
这也是一个标记注解。
@Inherited
阐述了某个被标注的类型是被继承的。被标注过的 class 的子类所继承。类并不从它所实现的接口继承 Annotation,方法并不从它所重载的方法继承 Annotation。
当使用@Inherited
类型标注的 Annotation 的 Retention 是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。
在本文的开头我们就说过,定义注解时会自动继承java.lang.annotation.Annotation
接口。同时还要注意的是,在定义注解时不能继承其他注解和接口。
注解的定义相对简单,有点类似于定义接口:
public @interface TestAnnotation {
String field1() default "it's empty"; //default 设置默认值
}
其中的每个方法都相当于声明了一个配置参数,参数的名称就是方法名,参数的类型就是返回值类型。在自定义注解时有一些注意事项:
- 方法(配置参数)只能用 public 或者 默认(default)这两种访问权限修饰。
- 方法(配置参数)的返回值支持类型只包括以下几种:
- 所有的数据类型(
int,float,boolean,byte,double,char,long,short
) - String类型
- Class类型
- enum类型
- Annotation(注解)类型
- 以上所有类型的数组
- 所有的数据类型(
- 注解中有一个特殊的存在:value。当注解中只有一个方法(配置参数)时,最好是将方法命名为 value。这样在使用注解时,就可以省略参数名,比如之后的例子:
@Owner
。
简单的自定义注解使用例子
/**
* book相关的注解。
* 包括两个参数:名称和页数
**/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BookInfo {
public String name() default "";
public int pageSize() default 0;
}
/**
* 拥有人注解
* 只有一个参数,命名为 value
**/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Owner {
public String value() default "";
}
public class UserBook {
@BookInfo(name = "EngLish", pageSize = 80)
public EngLish engLish;
@Owner("testName")
public String ownerName;
}
注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null。
在注解的语法部分中,对于特殊的 value 参数,当只指定该参数时,可以省略参数名和等号。这也是上面说的注意事项中第三点的原因
根据注解参数的不同,可以将注解分为以下几类:
- 标注注解。没有参数成员,通过判断该注解是否存在获得相关的信息
- 单值注解。只有一个参数成员,一般为 value,这样可以省略参数名和等号
- 完整注解
网友评论