Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。
注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。包含在 java.lang.annotation 包中。
一、元注解
元注解是指注解的注解。包括 @Retention @Target @Document @Inherited四种。
1.1 @Retention
定义了该Annotation被保留的时间长短
Retention允许的值
public enum RetentionPolicy {
/**
* 注解仅存在于源码中,在class字节码文件中不包含
*/
SOURCE,
/**
* 默认的保留策略,在class文件有效,可能会被虚拟机忽略
*/
CLASS,
/**
* 注解会在class字节码文件中存在,在运行时有效
*/
RUNTIME
}
作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
}
1.2 @Target
说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
Target允许的值
public enum ElementType {
/** 用于描述类、接口(包括注解类型) 或enum声明 */
TYPE,
/** 用于描述域(包含枚举常量) */
FIELD,
/** 用于描述方法 */
METHOD,
/** 用于描述参数 */
PARAMETER,
/** 用于描述构造器 */
CONSTRUCTOR,
/** 用于描述局部变量 */
LOCAL_VARIABLE,
/** 用于描述注解类型 */
ANNOTATION_TYPE,
/** 用于描述包 */
PACKAGE,
/**
* 用于描述注解的使用场景
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* 用于描述注解的使用场景
*
* @since 1.8
*/
TYPE_USE
}
作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
@Target(ElementType.METHOD)
public @interface Log {
}
1.3 @Documented
@Documented 将此注解包含在 javadoc 中 ,它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同。
Documented是一个标记注解,没有成员。
@Documented
public @interface Log {
}
1.4 @Inherited
@Inherited 说明子类可以继承父类中的该注解,声明的此注解使用了Inherited元注解,表示此注解用在类上时,会被子类所继承
Inherited是一个标记注解,没有成员。
@Inherited
public @interface Log {
}
二、自定义注解
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。
在定义注解时,不能继承其他的注解或接口。
@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。
定义注解格式:public @interface 注解名 { 定义体 }
注解参数的可支持数据类型:
- 所有基本数据类型(int,float,boolean,byte,double,char,long,short)
- String类型
- Class类型
- enum类型
- Annotation类型
- 以上所有类型的数组
Annotation类型里面的参数设定:
1. 只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型;
1. 参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和 String,Enum,Class,annotations等数据类型,以及这一些类型的数组.例如,String value();这里的参数成员就为String;
1. 如果只有一个参数成员,最好把参数名称设为”value”,后加小括号.例:下面的例子FruitName注解就只有一个参数成员。
/**
* 自定义宠物注解
*
* @author Peng
*/
@Documented
@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PetAnnotation {
String value() default "Cat";
}
/**
* 自定义颜色注解
*
* @author Peng
*/
@Documented
@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ColorAnnotation {
enum Color {WHITE, BLACK, GRAY}
Color petColor() default Color.WHITE;
}
自定义注解
/**
* 自定义注解测试
*
* @author Peng
*/
public class Pet {
@PetAnnotation
private String petName;
@ColorAnnotation(petColor = ColorAnnotation.Color.BLACK)
private String color;
public String getPetName() {
return petName;
}
public void setPetName(String petName) {
this.petName = petName;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
测试类
Field[] fields = Pet.class.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(PetAnnotation.class)) {
PetAnnotation pet = field.getAnnotation(PetAnnotation.class);
System.out.println("Pet Name : " + pet.value());
}
if(field.isAnnotationPresent(ColorAnnotation.class)){
ColorAnnotation color = field.getAnnotation(ColorAnnotation.class);
System.out.println("Pet Color : " + color.petColor());
}
}
输出:
Pet Name : Cat
Pet Color : BLACK