refer:来自:https://www.cnblogs.com/acm-bingzi/p/javaAnnotation.html
系列文章目录
1 什么是注解?
Java5之后可以在源代码中嵌入一些补充信息,这种补充信息称为注解(Annotation)。
例如在方法覆 盖中使用过的@Override
注解,注解都是@
符号开头的。
注解并不能改变程序运行的结果,不会影响程序运行的性能。有些注解可以在编译时给用户提示或警 告,有的注解可以在运行时读写字节码文件信息。
2 注解的原理:
注解本质都是一种 数据类型,是接口类型,你创建一个注解类源文件,实际上等价于==创建了一个继承了Annotation接口
的特殊接口。
3 注解分类——基本注解
到Java 8为止Java SE提供11种内置注解。
其中有5是基本注解,它们来自于java.lang
包。
基本注解包括:@Override、@Deprecated、@SuppressWarnings、@SafeVarargs和 @FunctionalInterface。
其余6个是元注解(Meta Annotation),它们来自于 java.lang.annotation
包,自定义注解时 会用到元注解。
元注解用来 注解其他的注解。
4 注解分类——元注解
元注解包括:@Documented
、@Target
、@Retention
、@Inherited
、@Repeatable
和@Native
。
元注解是 为其他注解进行说明的注解,当自定义一个新的注解类型时,其中可以使用元注解。
常用的自定义注解如下:
@Documented
:javadoc等工具可以提取这些注解信息@Target
:用来指定 新注解的适用位置。@Target注解有一个成员(value)用来设置适用 位置,value是java.lang.annotation.ElementType
枚举类型的数组,ElementType
描述Java程序元素 类型,它有10个枚举常量,如下:@Retention
:@Retention注解用来指定一个新注解的有效范围,@Retention注解有一个成员(value)用来设 置保留策略,value是java.lang.annotation.RetentionPolicy枚举类型,RetentionPolicy描述注解保留 策略,它有3个枚举常量,如下所示:
元注解还有如下:
4 自定义注解使用
4.1 声明一个基本注解
声明自定义注解可以使用@interface
关键字实现,最简单形式的注解示例代码如下:
// Marker.java文件
package com.a51work6;
public @interface Marker{
}
上述代码声明一个Marker注解,@interface声明一个注解类型。
Marker注解中不包含任何的成员,这种注解称为标记注解,基本注解中的@Override
就属于标记注解。
根据需要注解中可以包含一些成员,和成员的默认值,示例代码如下:
//Marker.java文件
package com.a51work6;
//带有默认值注解
@interface MyAnnotation1 {
String value() default "注解信息";
int count() default 0;
}
使用此注解如下:
//HelloWorld.java文件
package com.a51work6;
@Marker
public class HelloWorld {
@MyAnnotation(value = "Annotation")
private String info = "";
@MyAnnotation1(count = 10)
public static void main(String[] args) {
}
}
默认情况下注解可以修饰任意的程序元素(类、接口、成员变量、成员方法和数据类型等)。
- 代码第 ①行使用@Marker注解修饰类。
- 代码第②行是@MyAnnotation(value = “Annotation”)注解修饰成员变 量,其中value = "Annotation"是为value成员提供数值。
- 代码第③行是@MyAnnotation1(count = 10) 注解 修饰成员方法,@MyAnnotation1有两个成员,但是只为count成员赋值,另外一个成员value使用默认 值。
4.2 使用元注解声明新注解
上面4.1 声明注解只是最基本形式的注解,对于复杂的注解可以在声明注解时使用元注解。下面通过一 个案例介绍一下在自定义注解中使用元注解.
首先看看第一个注解MyAnnotation,它用来修饰类或接口,MyAnnotation代码如下:
//MyAnnotation.java文件
package com.a51work6;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Documented;
import java.lang.annotation.Target;
@Documented //1
@Target({ElementType.TYPE}) //2
@Retention(RetentionPolicy.RUNTIME) //3
public @interface MyAnnotation { //4
String description(); //5
}
其中使用了三个元注解修饰MyAnnotation注解,
- 代码 第①行使用@Documented指定MyAnnotation注解信息可以被javadoc工具读取。
- 代码第②行使用 @Target({ ElementType.TYPE })指定MyAnnotation注解用于修饰类和接口等类型
- 代码第③行 @Retention(RetentionPolicy.RUNTIME)指定MyAnnotation注解信息可以在运行时被读取。
- 代码第⑤行 的description是MyAnnotation注解的成员。
第二个注解MemberAnnotation,它用来类中成员变量和成员方法,MemberAnnotation代码如下:
//MemberAnnotation.java文件
package com.a51work6;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented @Retention(RetentionPolicy.RUNTIME) ①@Target({ElementType.FIELD, ElementType.METHOD}) ②
public @interface MemberAnnotation { ③
Class<?> type() default void.class; ④
String description(); ⑤
}
使用了MyAnnotation和MemberAnnotation注解是Person类,Person类代码如下:
Person.java文件
package com.a51work6;
@MyAnnotation(description = "这是一个测试类") ①
public class Person {
@MemberAnnotation(type = String.class, description = "名字") ②
private String name;
@MemberAnnotation(type = int.class, description = "年龄") ③
private int age;
@MemberAnnotation(type = String.class, description = "获得名字") ④
public String getName() {
return name;
}
@MemberAnnotation(type = int.class, description = "获得年龄") ⑤
public int getAge() {
return age;
}
@MemberAnnotation(description = "设置姓名和年龄") ⑥
public void setNameAndAge(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
读取运行时注解信息:
注解是为工具读取信息而准备的。有些工具可以读取源代码文件中的注解信息;有的可以读取字节码 文件中的注解信息;有的可以在运行时读取注解信息。但是读取这些注解信息的代码都是一样的,区 别只在于自定义注解中@Retention的保留策略不同。
读取注解信息需要反射相关API,Class类如下方法:
- A getAnnotation(Class annotationClass):如果此元素存在 annotationClass类型的注解,则返回注解,否则返回null。
- Annotation[] getAnnotations():返回此元素上存在的所有注解。
- Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注解。与getAnnotations() 区别在于该方法将不返回继承的注释。
- boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):如果此元素上存在 annotationClass类型的注解,则返回true,否则返回false。
- boolean isAnnotation():如果此Class对象表示一个注解类型则返回true。
运行时Person类中注解信息代码如下:
//HelloWorld.java文件
package com.a51work6;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class HelloWorld {
public static void main(String[] args) {
try {
Class<?> clz = Class.forName("com.a51work6.Person"); // 读取类注解
if (clz.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation ann = (MyAnnotation) clz.getAnnotation(MyAnnotation.class);
System.out.printf("类%s,读取注解描述: %s \n", clz.getName(), ann.description());
}// 读取成员方法的注解信息
Method[] methods = clz.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(MemberAnnotation.class)) {
MemberAnnotation ann = method.getAnnotation(MemberAnnotation.class);
System.out.printf("方法%s,读取注解描述: %s \n", method.getName(), ann.description());
}
}// 读取成员变量的注解信息
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(MemberAnnotation.class)) {
MemberAnnotation ann = field.getAnnotation(MemberAnnotation.class);
System.out.printf("成员变量%s,读取注解描述: %s \n", field.getName(), ann.description());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
上述代码第①行是创建Person类对应的Class对象,代码第②行是判断Person类是否存在MyAnnotation注 解,如果存在则通过代码第③行的getAnnotation方法将MyAnnotation注解实例返回。代码第④行中 ann.description()表达式读取MyAnnotation注解中description成员内容。
代码第⑤行是获得所有成员方法对象数组,通过遍历方法对象数组,在代码第⑥行判断方法中是否存 在MemberAnnotation注解,如果存在则通过代码第⑦行的getAnnotation方法将MemberAnnotation注解实 例返回。代码第⑧行中ann.description()表达式读取MemberAnnotation注解中description成员内容。
代码第⑨行是获得所有成员变量对象数组,代码第⑩行是判断成员变量中是否存在MemberAnnotation 注解。其他的处理与成员方法类似,这里不再赘述。