Annotation
元数据(data about data):定义数据的数据。比如,有一条学生信息记录,里面字段包括姓名(name)、年龄(age)等,那么name、age就是元数据,通过这些元数据就可以描述出一个学生。
Annotation能够被用来为程序元素(类、方法、成员变量等)设置元数据。Annotation是一个接口,通过反射来获取指定程序元素的Annotation对象,然后通过Annotation对象来取得注释里的元数据。
基本Annotation
-
三个基本Annotation
-
- @Override
-
- @Deprecated
-
- @SuppressWarnings
@Override
@Override就是用来指定方法覆盖的,它们可以强制一个子类必须覆盖父类的方法。
作用:告诉编译器检查这个方法,并从父类查找是否包含一个被重写的方法,否则编译错误。Annotation就可以帮助我们避免一些低级错误。
作用范围:只能作用于方法,不能作用于其他元素。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
@Deprecate
作用:标志某元素已经过时,不建议使用。若使用过时元素编译器会发出警告。
作用范围:类和方法
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
@SuppressWarnings
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
作用:取消显示指定的编译器警告。
作用范围:类,接口、方法,属性
例如:如果没有泛型限制的集合会引起编译器警告
//取消单类型的警告
@SuppressWarnings(value = "unchecked")
ArrayList arrayList=new ArrayList();
//取消多类型的警告
@SuppressWarnings(value={"unchecked", "rawtypes"})
public void addItems(String item){
List items = new ArrayList();
items.add(item);
}
//取消所有类型的警告
@SuppressWarnings("all")
public void addItems(String item){
List items = new ArrayList();
items.add(item);
}
自定义Annotation
定义Annotation
定义Annotation会使用到关键字@interface
(在原来接口定义关键字interface前加@符号)。Annotation类型与接口的定义相似。
语法
public @interface Annotation名{
}
默认情况下,Annotation可以修饰任何元素,包括类。接口、方法,属性等。
Annotation分类
- 标记Annotation:一个没有成员定义的Annotation类型称为标记。这种Annotation仅使用自身的存在与否来为我们提供信息。如@Override、@Test。
- 元数据Annotation:包含成员变量的Annotation,因为他们可以接受更多元数据,因此称为元数据Annotation。
例1:标记Annotation:不带成员变量的Annotation
public @interface MyAnnotation {
}
@MyAnnotation
public class TestAnnotation {
}
例2:元数据Annotation:带成员变量
public @interface MyAnnotation {
int age();
String name();
}
@MyAnnotation(age = 1,name = "小明")
public class TestAnnotation {
}
提取Annotation信息
Java使用Annotation接口来代表程序前面的注释,该接口是所有Annotation类型的父接口。在java.lang.reflect包下新增了一个AnnotatedElement接口,该接口代表程序中接受注释的程序元素,该接口的几个实现类:
- Class:类定义
- Constructor:构造器定义
- Field:类的成员变量定义
- Method:类的方法定义
- Package:包定义
java.lang.reflect包下主要包含一些实现反射功能的工具类。
使用例子
//java.lang.annotation.Retention;指定MyAnnotation注释可以保留多久
@Retention(RetentionPolicy.RUNTIME)
//java.lang.annotation.Target;指定MyAnnotation的注释对象(这里只能是方法)
@Target(ElementType.METHOD)
public @interface MyAnnotation {
int age();
String name();
}
仅仅使用注释来标识程序是不会有任何影响的,这也是Java注释的一条重要原则,为了让程序起作用,我必须为这些注释提供一个注释处理工具。
示例1:使用一个测试类的注释
模拟Junit单元测试@Test注解。
-
结构:
-
package annotation1;
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.METHOD)
public @interface Testable {
}
package annotation1;
/**
* @author Archer
* @date 2019-04-04 8:35
**/
public class MyTest {
@Testable
public static void test1(){
System.out.println("test1");
}
@Testable
public static void test2(){
System.out.println("test2");
}
@Testable
public static void test3(){
throw new RuntimeException("test3测试异常");
}
}
package annotation1;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author Archer
* @date 2019-04-04 8:38
**/
public class TestProcessor {
public static void process(String clazz) throws ClassNotFoundException {
int passed=0;
int fialed=0;
//遍历obj对象的所有方法,通过反射获取
for (Method m: Class.forName(clazz).getMethods()){
//如果使用Testable标记注释
if (m.isAnnotationPresent(Testable.class)){
//调用m方法
try {
m.invoke(null);
//passed加一
passed++;
} catch (IllegalAccessException e) {
System.out.println("运行异常"+e.getMessage());
fialed++;
} catch (InvocationTargetException e) {
System.out.println("运行异常"+e.getMessage());
fialed++;
}
}
}
//统计结果
System.out.println("共运行了:"+(passed+fialed)+"个方法\npassed:"+passed+" fail:"+fialed);
}
public static void main(String[] args) throws ClassNotFoundException {
TestProcessor.process("annotation1.MyTest");
}
}
-
运行结果:
-
JDK的元Annotion(Meta Annotion)
@Retention
@Retention
只能用于修饰一个Annotation定义,用于指定可以保留多长时间。
@Retention包含一个RetentionPolicy类型的value成员变量,所以使用@Retention是必须为该value成员变量指定值。
value可以为的值:
①RetentionPolicy.CLASS
:编译器将注释记录在class文件中。当运行Java程序时,JVM不再保留注释。这是默认值。
②RetentionPolicy.RUNTIME
:编译器将把注释记录在class文件中。当运行Java程序时,JVM也会保留注释。
③RetentionPolicy.SOURCE
:编译器直接丢弃这种策略的注释。
@Target
@Target
用于修饰一个Annotion定义,用于指定被修饰的Annotion只能用于此时哪些程序元素。
@Tartget Annotion也包含一个名为value的成员变量,
该成员变量的值只能是:
@Inherited
@Inherited元Annotion指定被修饰的Annotion将具有继承性。
如果一个父类使用A注释(A注释被@Inherite修饰)修饰,其子类也会自动具有A注释。
APT处理Annotion
APT(annotation processing tool)是一种处理注释的工具,用于对源代码检测出其中的Annotion后,使用Annotion进行额外处理。