注解相当于一个标签,可以给这个标签上添加相关信息,然后插入到代码中,在运行该程序时,虚拟机会利用反射机制来处理这些代码,使它完成相应的功能。它可以出现在许多地方,比如说包、类、接口、方法、构造器、实例域、局部变量、参数变量、类型参数等,通常可以这样自定义一个注解:以@interface
声明一个注解,配置相关元注解,包括@Target
来定义该注解适用的场合,@Retention
来指明该注解保留多长时间,再配置一些参数;然后再添加一个方法去处理这个注解:先通过.getClass()方法获取Class对象,然后根据这个Class对象获得域、方法、构造器等;假如说我们限制获得了Class对象的域,那么就遍历所获得的域数组,获取域上的注解,然后使用Java Field.get()取得对象的Field属性值,接着和注解的参数值做相应的操作
注解是那些插入到源代码中使用其他工具可以对其进行处理的标签,它是一种形式化的方法,可以方便地在代码中添加信息,这些注解本身不会做任何事情,它们只是存在于源文件中,虚拟机会在运行时利用反射机制来处理注解,使之完成相应的功能。
在Java中,注解可以被当作一个修饰符来使用,置于注解项之前,中间没有分号。
注解可以出现在许多地方:包、类(包括Enum)、接口(包括注解接口)、方法、构造器、实例域(包括Enum常量)、局部变量、参数变量、类型参数
Java SE在java.lang、java.lang.annotation
和javax.annotation
包中定义了大量的注解接口。其中四个是元注解,用于描述注解接口的行为属性,其他的三个是规则接口,可以用它们来注解你的源代码中的项
标准注解
注解接口 | 应用场合 | 目的 |
---|---|---|
@Deprecated | 全部 | 将项标记为过时的 |
@SuppressWarnings | 除了包和注解之外的所有情况 | 阻止某个给定类型的警告信息 |
@SafeVarargs | 方法和构造器 | 断言varargs参数可安全使用 |
@Override | 方法 | 检查该方法是否覆盖了某一个超类方法 |
@FunctionalInterface | 接口 | 将接口标记为只有一个抽象方法的函数式方法 |
@PostConstruct | 方法 | 将标记的方法应该在构造之后立即被调用 |
@PreDestroy | 方法 | 将标记的方法应该在移除之前立即被调用 |
@Resource | 类、接口、方法、域 | 在类或接口上:标记为在其他地方要用到的资源。在方法或域上:为注入而标记 |
@Resources | 类、接口 | 一个资源数组 |
@Generated | 全部 | |
@Target | 注解 | 指明可以应用这个注解的那些项 |
@Rentention | 注解 | 指明这个注解可以保留多久 |
@Document | 注解 | 指明这个注解应该包含在注解项的文档中 |
@Interited | 注解 | 指明当这个注解应用于一个类时,能够自动被它的子类继承 |
@Repeatable | 注解 | 指明这个注解可以在同一个项上应用多次 |
元注解
@Target
元注解可以应用于一个注解,以限制该注解可以应用到哪些项上
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface BugReport
@Target
元注解的元素类型
元素类型 | 注解适用场合 |
---|---|
ANNOTATION_TYPE | 注解类型声明 |
PACKAGE | 包 |
TYPE | 类(包括Enum)及接口(包括注解类型) |
METHOD | 方法 |
CONSTRUCTOR | 构造器 |
FIELD | 成员域(包括Enum常量) |
LOCAL_VARIABLE | 局部变量 |
TYPE_PARAMETER | 类型参数 |
TYPE_USE | 类型用法 |
一条没有@Target
限制的注解可以应用于任何项上。编译器将检查是否将一条注解只应用到了某个允许的项上
@Retention
元注解用于指定一条注解应该保留多长时间
用于@Retention
注解的保留策略
保留规则 | 描述 |
---|---|
SOURCE | 不包括在类文件中的注解 |
CLASS | 包括在类文件中的注解,但是虚拟机不需要将它们载入 |
RUNTIME | 包括在类文件中的注解,并由虚拟机载入。通过反射API可获得它们 |
每个注解都必须通过一个注解接口来定义
@interface声明创建了一个真正的Java接口。处理注解的工具将接收那些实现了这个注解接口的对象
自定义注解
@Target(ElementType.METHOD)
public @interface Test {
String value() default “”;
}
每个注解都具有下面这种格式:@AnnotationName(elementName1=value1, elementName2=value2,...)
默认值并不是和注解存储在一起的;相反地,它们是动态计算而来的。
有两种特殊的快捷方式可以用来简化注解:
如果没有指定元素,要么是因为注解中没有任何元素,要么是因为所有元素都使用默认值,那么你就不需要使用圆括号了。
另外一种快捷方式是单值注解。如果一个元素具有特殊的名字value,并且没有指定其他元素,那么你就可以忽略掉这个元素以及等号
如果注解的作者将其声明为可重复的,那么你就可以多次重复使用同一个注解
对于类和接口,需要将注解放置在class和interface关键字的前面
@Entity public class User {...}
对于变量,需要将它们放在类型的前面
@SuppressWarnings(unchecked") List<User> users = ...;
泛化类或方法中的类型参数可以像下面这样被注解:
public class Cache<@Immutable V> {...}
注解类型用法:
声明注解提供了正在被声明的项的相关信息
定义注解:(以@interface
声明一个注解,配置相关元注解,包括@Target
来定义该注解适用的场合,@Retention
来指明该注解保留多长时间,再配置一些参数)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
int max() default 0;
int min() default 0;
String descrption() default "";
}
注解使用:
public class AnnoationTest {
@Test(min = 6, max = 10, descrption = "haha")
private String name;
@Test(min = 6, max = 10, descrption = "haha")
private String password;
public String getName() return name;
public void setName(String name) this.name = name;
public String getPassword() return password;
public void setPassword(String password) this.password = password;
}
注解处理器以及测试主方法:
先通过.getClass()方法获取Class对象
根据这个Class对象获得域、方法、构造器等
假如说获得了Class对象的域,遍历所获得的域数组
获取域上的注解
Java Field.get()取得对象的Field属性值
和注解的参数值做相应的操作
import java.lang.reflect.Field;
public class TestUtil {
public static void valid(Object obj) throws IllegalAccessException {
Class<?> clazz = obj.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field:fields){
Test test = field.getAnnotation(Test.class);//获取属性上的@Test注解
if(test != null){
field.setAccessible(true);//设置属性可访问
if("class java.lang.String".equals(field.getGenericType().toString())){//字符串类型的才判断长度
String value = (String)field.get(obj);
if(value != null && ((value.length() > test.max()) || value.length() < test.min())){
System.out.println(test.description());
}
}
}
}
}
public static void main(String[] args){
AnnoationTest annotation = new AnnoationTest();
annotation.setName("abcefg");
annotation.setPasdword("1234567890");
try {
valid(annotation);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}