注解(Annotation)是放在Java源码的类、方法、字段、参数前的一种特殊“注释”。
注解是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在类、方法等的前面,用来对这些元素进行说明,注释。
与注释不同的是,注释会被编译器直接忽略,注解则可以被编译器打包进入class文件,因此,注解是一种用作标注的“元数据”。
从JVM的角度看,注解本身对代码逻辑没有任何影响,如何使用注解完全由工具决定。
Java的注解可以分为三类:
第一类是由编译器使用的注解,例如:
@Override:让编译器检查该方法是否正确地实现了覆写;
@SuppressWarnings:告诉编译器忽略此处代码产生的警告。
@Deprecated:该注解标注的内容,表示已过时
这类注解不会被编译进入.class文件,它们在编译后就被编译器扔掉了。
第二类是由工具处理.class文件使用的注解,
比如有些工具会在加载class的时候,对class做动态修改,实现一些特殊的功能。
这类注解会被编译进入.class文件,但加载结束后并不会存在于内存中。这类注解只被一些底层库使用,一般我们不必自己处理。
第三类是在程序运行期能够读取的注解,它们在加载后一直存在于JVM中,这也是最常用的注解。
例如,一个配置了@PostConstruct的方法会在调用构造方法后自动被调用(这是Java代码读取该注解实现的功能,JVM并不会识别该注解)。
自定义注解:
格式:
元注解
public @interface 注解名称{
属性列表;
}
其中,必须设置@Target和@Retention,@Retention一般设置为RUNTIME,因为我们自定义的注解通常要求在运行期读取。
注解本质上就是一个接口,该接口默认继承Annotation接口
public interface MyAnno extends java.lang.annotation.Annotation {}
属性的返回值类型有下列取值
1.基本数据类型
2.String
3.枚举
4.注解
5.以上类型的数组
定义了属性,在使用时需要给属性赋值
1. 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
2. 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
3. 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略
比如:
public class Hello {
@Check(min=0, max=100, value=55)
public int n;
@Check(value=99)
public int p;
@Check(99) // @Check(value=99)public int x;
@Check
public int y;
}
@Check就是一个注解。第一个@Check(min=0, max=100, value=55)明确定义了三个参数,
第二个@Check(value=99)只定义了一个value参数,它实际上和@Check(99)是完全一样的,
最后一个@Check表示所有参数都使用默认值。
元注解:
有一些注解可以修饰其他注解,这些注解就称为元注解(meta annotation)
@Target:描述注解能够作用的位置
ElementType.TYPE:可以作用于类上
ElementType.METHOD:可以作用于方法上
ElementType.FIELD:可以作用于成员变量上
ElementType.CONSTRUCTOR:可以作用在构造方法上
ElementType.PARAMETER:可以作用在方法的参数上
@Retention:定义了Annotation的生命周期
RetentionPolicy.SOURCE:仅编译期
RetentionPolicy.CLASS:仅class文件
RetentionPolicy.RUNTIME:运行期
SOURCE类型的注解主要由编译器使用,因此我们一般只使用,不编写。
CLASS类型的注解主要由底层工具库使用,涉及到class的加载,一般我们很少用到。
只有RUNTIME类型的注解不但要使用,还经常需要编写。
@Inherited:定义子类是否可继承父类定义的Annotation。
@Inherited仅针对@Target(ElementType.TYPE)类型的annotation有效,并且仅针对class的继承,对interface的继承无效。
@Documented:描述注解是否被抽取到api文档中
@Repeatable:描述这个元注解修饰的Annotation是否可重复。
使用注解:
判断某个注解是否存在于Class、Field、Method或Constructor:
Class.isAnnotationPresent(Class)
Field.isAnnotationPresent(Class)
Method.isAnnotationPresent(Class)
Constructor.isAnnotationPresent(Class)
使用反射API读取Annotation:
Class.getAnnotation(Class)
Field.getAnnotation(Class)
Method.getAnnotation(Class)
Constructor.getAnnotation(Class)
例子:
/*** 我们来看一个@Range注解,我们希望用它来定义一个String字段的规则:字段长度满足@Range的参数定以*/
package demo.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Range {
int min() default 0;
int max() default 255;
}
/*** 在Person这个JavaBean中使用Range注解*/
package demo.annotation;
public class Person {
@Range(min = 3,max = 10)
public String name;
public int age;
@Range(min = 5,max = 20)
public String address;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
//写一个测试类package demo.annotation;
import org.junit.Assert;
import org.junit.Test;
import java.lang.reflect.Field;
import java.util.ArrayList;
public class PersonTest {
@Test
public void testPersonRange() throws Exception {
/*** 测试 isAnnotationPresent()和getAnnotation()*/
Field name = Person.class.getField("name");
Field age = Person.class.getField("age");
Field address = Person.class.getField("address");
/**判断某个注解是否存在于Class、Field、Method或Constructor* Field.isAnnotationPresent(Class)* 这里判断 Range 注解是否存在于 Field 中*/
Assert.assertEquals(true,name.isAnnotationPresent(Range.class));
Assert.assertEquals(false,age.isAnnotationPresent(Range.class));
Assert.assertEquals(true,address.isAnnotationPresent(Range.class));
/**通过Class、Field、Method或Constructor来获取修饰它的注解* Field.getAnnotation(Class)*这里我们获取修饰 Field 的注解 Range */
Range rangeName = name.getAnnotation(Range.class);
System.out.println(rangeName);
Assert.assertEquals(3,rangeName.min());
Assert.assertEquals(10,rangeName.max());
Range rangeAddress = address.getAnnotation(Range.class);
System.out.println(rangeAddress);
Assert.assertEquals(5,rangeAddress.min());
Assert.assertEquals(20,rangeAddress.max());
Range rangeAge = age.getAnnotation(Range.class);
Assert.assertEquals(null,rangeAge);
}
/**这里,我们编写一个Person实例的检查方法check,它可以检查Person实例的String字段长度是否满足@Range的定义*/
public ArrayList check(Person person) throws Exception{
ArrayList fieldList = new ArrayList<>();
// 遍历所有Fieldfor (Field field : person.getClass().getFields()) {
// 获取Field定义的@RangeRange range = field.getAnnotation(Range.class);
// 如果@Range存在if (range != null){
// 获取Field的值Object fieldValue = field.get(person);
// 如果Field的值是String,因为我们只对Person类中String类型的字段修饰了range注解if (fieldValue instanceof String){
//将Object强转为StringString value = (String)fieldValue;
//不符合Range注解的判断条件if (value.length() < range.min() || value.length() > range.max()){
System.out.println("Illegal field: " + field.getName());
fieldList.add(field.getName());
}
}
}
//如果@Range不存在,那么该Field没有注解else{
System.out.println("field do not have Annotation: : " + field.getName());
}
}
System.out.println(fieldList);
return fieldList;
}
@Test
public void testCheck() throws Exception {
Person person = new Person();
person.name = "tom";
person.age = 23;
person.address = "china beijing beijing";
ArrayList actualList = check(person);
//我们期望得到的结果是["address"]ArrayList expectedList = new ArrayList();
expectedList.add("address");
//断言通过,说明我们的测试通过Assert.assertEquals(expectedList,actualList);
}
}
输出:
@demo.annotation.Range(max=10, min=3)
@demo.annotation.Range(max=20, min=5)
field do not have Annotation: : age
Illegal field: address
[address]
本文参考:注解www.liaoxuefeng.com