java:annotation注解
1 annotation
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。
annotation可用于类、构造方法、成员变量、方法、参数等的声明中,该功能并不影响程序的运行,但是会对编译器警告等辅助工具产生影响。
2 定义Annotation类型
在定义annotation类型时,也需要用到定义接口的interface关键字,但需要在interface关键字前加上1个"@"符号,即定义Annotation类型的关键字是@interface,这个关键字的隐含意思是继承了java.lang.annotation.Annotation接口。
定义形式:
public @interface TryAnnotation {
}
注意:上面定义的Annotation类型@TryAnnotation不包含任何成员,这样的Annotation类型被称为marker annotation。
下面定义一个只包含一个成员的Annotation类型:
public @interface TryAnnotation {
String val();
}
/*
String val();表示注解有一个名为 val 的可选参数。
不设置的话默认为""。
如果没有后面的 default "",则表示这是一个必须的参数。
不指定的话会报错。
*/
String:成员类型。可用的成员类型有String、Class、primitive(原始的)、enumerated和annotation,以及所列类型的数组(基本类型int\short\long\float\double\byte\boolean)。
val:成员名称。如果在所定义的Annotation类型中只包含一个成员,通常将成员名称命名为value(这里是val)。
下面是包含多个成员的Annotation类型:
@interface MyAn{
String describe();
int type();
}
@interface MyAn{
String describe() default "xiaoxu";
int type() default 28;
Class<? extends Annotation> annotation();
}
定义成员时,可以设置默认值。
@interface MyAn{
String describe() default "xiaoxu";
int type() default 28;
}
@Target - 标记这个注解应该是哪种 Java 成员
在定义Annotation类型时,还可以通过Annotation类型@Target来设置Annotation类型适用的程序元素种类。如果未设置@Target,则表示适用于所有程序元素。枚举类ElementType中的枚举常量用来设置@Target
public enum ElementType {
TYPE, /* 类、接口(包括注释类型)或枚举声明 */
FIELD, /* 字段声明(包括枚举常量) */
METHOD, /* 方法声明 */
PARAMETER, /* 参数声明 */
CONSTRUCTOR, /* 构造方法声明 */
LOCAL_VARIABLE, /* 局部变量声明 */
ANNOTATION_TYPE, /* 注释类型声明 */
PACKAGE /* 包声明 */
}
通过Annotation类型@Retention可以设置Annotation的有效范围,枚举类RetentionPolicy中的枚举常量用来设置@Retention。
public enum RetentionPolicy {
SOURCE, /* Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了 */
CLASS, /* 编译器将Annotation存储于类对应的.class文件中。默认行为 */
RUNTIME /* 编译器将Annotation存储于class文件中,并且可由JVM读入 */
}
枚举类RetentionPolicy(retention:保留)中的枚举常量:
SOURCE:表示不编译Annotation到类文件中,有效范围最小;
CLASS:表示编译Annotation到类文件中,但是在运行时不加载Annotation到JVM中。
RUNTIME:表示在运行时,加载Annotation到JVM中,有效范围最大。
定义一个注释构造方法的Annotation类型@Constructor_Annotation,有效范围为在运行时加载Annotation到JVM中,
tips:黄色的是注解
以上缩写就是:
然后定义一个用来注释字段、方法和参数的Annotation类型@Field_Method_Parameter_An,有效范围为在运行时加载Annotation到JVM中。
//用于字段、方法和参数
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
//在运行时加载Annotation到JVM中
@interface Field_Method_Parameter_An{
String describe(); //没有默认值的String型成员
Class type() default void.class; //有默认值的Class型成员
}
最后编写一个Record类,在该类中,运用前面定义的@ConstructorAn和@Field_Method_Parameter_An,对构造方法、字段、方法和参数进行注释。
import java.lang.annotation.*;
@Target(ElementType.CONSTRUCTOR)
//用于构造方法
@Retention(RetentionPolicy.RUNTIME)
//在运行时加载Annotation到JVM中
@interface ConstructorAn{
String value() default "默认构造方法";
}
//用于字段、方法和参数
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
//在运行时加载Annotation到JVM中
@interface Field_Method_Parameter_An{
String describe(); //没有默认值的String型成员
Class type() default void.class; //有默认值的Class型成员
}
class Record{
@Field_Method_Parameter_An(describe = "编号",type = int.class)
int id;
@Field_Method_Parameter_An(describe = "姓名",type=String.class)
String name;
@ConstructorAn()
public Record(){
}
@ConstructorAn("立即初始化构造方法")
public Record(//注释构造方法
@Field_Method_Parameter_An(describe = "构造-编号参数",type=int.class)int id,
@Field_Method_Parameter_An(describe = "构造-姓名参数",type=String.class)String name
){
this.id=id;
this.name=name;
}
@Field_Method_Parameter_An(describe = "获得编号方法",type=int.class)
public int getId(){//注释方法
return id;
}
@Field_Method_Parameter_An(describe = "设置编号方法")
public void setId(
//成员type采用默认值注释方法
//注释方法的参数
@Field_Method_Parameter_An(describe = "设置编号参数",type = int.class)int id
){
this.id=id;
}
@Field_Method_Parameter_An(describe = "获得姓名方法",type=String.class)
public String getName(){
return this.name;
}
@Field_Method_Parameter_An(describe = "设置姓名方法")
public void setName(
@Field_Method_Parameter_An(describe = "设置姓名参数",type = String.class)String name
){
this.name=name;
}
}
3 访问Annotation信息
如果在定义Annotation类型时,将@Retention设置为RetentionPolicy.RUNTIME,那么在运行程序时,通过反射就可以获取到相关的Annotation信息,如获取构造方法、字段和方法的Annotation信息。
类Constructor、Field和Method均继承了AccessibleObject类,在AccessibleObject中定义了3个关于Annotation的方法,其中方法isAnnotationPresent(Class<? extends Annotation>annotationClass)用来查看是否添加了指定类型的Annotation,如果是则返回true,否则false;方法getAnnotation(Class annotationClass)用来获得指定类型的Annotation,如果存在则返相应的对象,否则返回null;方法getAnnotations()用来获得所有的Annotation,该方法返回1个Annotation数组
在类Constructor和Method中还定义了方法getParameterAnnotations(),用来获得为所有参数添加的Annotation,将以Annotation类型的二维数组返回,在数组中的顺序与声明的顺序相同,如果没有参数则返回一个长度为0的数组;如果存在未添加Annotation的参数,将用一个长度为0的嵌套数组占位。
3.1 访问Annotation信息
改造如上Record,实现在程序运行时通过反射访问Record类中的Annotation信息。
package com.base;
import java.lang.annotation.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
public class Annota {
public static void main(String[] args) {
Record r=new Record();
Class<? extends Record> r1=r.getClass();
Constructor[] c=r1.getConstructors();
List<Constructor> l= Arrays.asList(c);
l.forEach(System.out::println);
for (Constructor constructor : l) {
System.out.println("----------------------");
System.out.println(constructor);
//查看是否具有指定类型的注释
if(constructor.isAnnotationPresent(ConstructorAn.class)){
//获得指定类型的注释
// Annotation annotation = constructor.getAnnotation(ConstructorAn.class);
ConstructorAn annotation = (ConstructorAn) constructor.getAnnotation(ConstructorAn.class);
System.out.println(annotation.value()); //获得注释信息
}
Annotation[][] parameterAnnotations = constructor.getParameterAnnotations();
for (int i = 0; i < parameterAnnotations.length; i++) {
//获得指定参数注释的长度
int length=parameterAnnotations[i].length;
if(length==0)
//长度为0,表示没有为该参数添加注释
System.out.println("未添加Annotation的参数");
else{
for (int i1 = 0; i1 < length; i1++) {
//获得参数的注释
Field_Method_Parameter_An pa=(Field_Method_Parameter_An) parameterAnnotations[i][i1];
System.out.println(pa.describe());//获得参数描述
System.out.println(pa.type());//获得参数类型
}
}
}
}
System.out.println("\nfield------");
Field[] f=r1.getDeclaredFields();
List<Field> fl=Arrays.asList(f);
fl.forEach(System.out::println);
for (Field field : fl) {
//查看是否具有指定类型的注释
if(field.isAnnotationPresent(Field_Method_Parameter_An.class)){
System.out.println("字段注解存在:");
Field_Method_Parameter_An annotation = field.getAnnotation(Field_Method_Parameter_An.class);
System.out.println(annotation.describe()); //获得字段的描述
System.out.println(annotation.type()); //获得字段的类型
}
}
System.out.println("\nmethod------");
//访问方法及其包含参数得Annotation信息:
Method[] m=r1.getDeclaredMethods(); //获得所有方法
for (Method method : m) {
if(method.isAnnotationPresent(Field_Method_Parameter_An.class)){
System.out.println("\n方法注解存在");
Field_Method_Parameter_An annotation = method.getAnnotation(Field_Method_Parameter_An.class);
System.out.println(annotation.describe());//获得方法的描述
System.out.println(annotation.type());//获得方法的返回值类型
}
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (int i = 0; i < parameterAnnotations.length; i++) {
int length=parameterAnnotations.length;
if(length==0)
System.out.println("方法没有Annotation的参数");
else{
System.out.println("~~~~~~~~~~~~~~~~~~");
for (int i1 = 0; i1 < length; i1++) {
//获得指定类型的注释
Field_Method_Parameter_An fa = (Field_Method_Parameter_An) parameterAnnotations[i][i1];
System.out.println(fa.describe());//获得参数描述
System.out.println(fa.type());//获得参数类型
}
}
}
}
}
}
@Target(ElementType.CONSTRUCTOR)
@Retention(RetentionPolicy.RUNTIME)
@interface ConstructorAn{
String value() default "默认构造方法";
}
@Target({ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@interface Field_Method_Parameter_An{
String describe();
Class type() default void.class;
}
class Record{
@Field_Method_Parameter_An(describe = "编号",type=int.class)
int id;
@Field_Method_Parameter_An(describe = "姓名",type=String.class)
String name;
@ConstructorAn
public Record() {
}
public Record(
@Field_Method_Parameter_An(describe = "构造-编号参数",type=int.class) int id,
@Field_Method_Parameter_An(describe = "构造-姓名参数",type=String.class) String name) {
this.id = id;
this.name = name;
}
@Field_Method_Parameter_An(describe = "获得编号方法",type=int.class)
public int getId() {
return id;
}
@Field_Method_Parameter_An(describe = "设置编号方法")
public void setId(
@Field_Method_Parameter_An(describe = "设置编号参数",type = int.class)int id) {
this.id = id;
}
@Field_Method_Parameter_An(describe = "获得姓名方法",type=String.class)
public String getName() {
return name;
}
@Field_Method_Parameter_An(describe = "设置姓名方法")
public void setName(
@Field_Method_Parameter_An(describe = "设置姓名参数",type=String.class) String name) {
this.name = name;
}
}
结果如下:
public com.base.Record()
public com.base.Record(int,java.lang.String)
----------------------
public com.base.Record()
默认构造方法
----------------------
public com.base.Record(int,java.lang.String)
构造-编号参数
int
构造-姓名参数
class java.lang.String
field------
int com.base.Record.id
java.lang.String com.base.Record.name
字段注解存在:
编号
int
字段注解存在:
姓名
class java.lang.String
method------
方法注解存在
获得姓名方法
class java.lang.String
方法注解存在
获得编号方法
int
方法注解存在
设置姓名方法
void
~~~~~~~~~~~~~~~~~~
设置姓名参数
class java.lang.String
方法注解存在
设置编号方法
void
~~~~~~~~~~~~~~~~~~
设置编号参数
int