从JDK5开始,Java增加了对元数据的支持,也就是注解Annotation。注解就是代码里的特殊标记,这些标记可以在编译、类加载和运行时被读取,并进行相应的处理。通过使用注解,开发人员可以在不改变原有逻辑的基础上,在源文件中嵌入一些补充信息。 Annotation是一个接口,程序可以通过反射机制来获取指定程序元素的Annotation对象,然后通过Annotation对象来取得注解中的元数据。值得注意的是,Annotaion并不影响程序的执行,无论添加、删除注解,代码都照常运行。
Java提供了几个基本的注解,功能如下:
在java.lang,annotation包下提供了几个元注解Meta Annotation,主要用于修饰其他的Annotation定义,具体如下:
Java使用@interface修饰符定义一个注解类;注解的成员以无参数无抛出异常的方式声明;可以通过default关键字为成员指定一个默认值;成员的类型是受限的,合法的类型包括8种基本类型及其包装类、String、Class、enum和注解类型。
下面程序演示了一个自定义注解:
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 NeedTest{ //使用@interface定义注解
//声明注解成员,default指定其默认值
boolean value() default true;
}
上面程序使用@interface定义了一个注解类,这非常类似于定义了一个注解接口,这个注解接口继承了Annotation接口。值得注意的是,自定义的注解不可以显式实现接口或继承父类。
定义了@NeedTest注解后,就可以在任意方法上使用该注解,实例如下:
public class BookService {
@NeedTest(value=true)
public void deleteBook(long bookId){
System.out.println("删除图书:" + bookId);
}
@NeedTest(false)
public void addTopic(long bookId){
System.out.println("添加图书:" + bookId);
}
}
从上例可以看出,如果注解只有一个成员,则该成员名必须为value(),在使用时可以忽略成员名value和赋值号=。当注解有多个成员时,如果仅对value()成员赋值,也可以不指定成员名,但如果同时对多个成员赋值,则必须使用”成员名=值”的形式。
自定义Annotation后,这些Annotation不会自动生效,必须又开发中提供相应的工具提取并处理Annotation的信息。Java5在java.lang.reflect包下新增了AnnotatedElement接口,该接口表示程序中可以接受注解的程序元素,它的实现类包括Class、Constructor、Field、Method、Package。因此可以通过Java反射机制对注解进行解析处理。下面程序定义一个注解解析器:
import java.lang.reflect.Method;
//注解的工具类
public class NeedTestUtil {
public static void main(String[] args) throws Exception {
BookService bookService = new BookService();
//获取被注解修饰的目标类
Class<?> clazz = BookService.class;
//获取所有方法
for (Method method : clazz.getDeclaredMethods()){
//判断当前方法是否被NeedTest注解修饰
if (method.isAnnotationPresent(NeedTest.class)){
NeedTest needTest = method.getAnnotation(NeedTest.class);
//如果NeedTest的value成员为true,则进行测试
if (needTest.value()){
System.out.println(method.getName() + "方法需要进行测试");
System.out.println("测试结果:");
method.invoke(bookService, Math.round(Math.random() * 100));
}
else {
System.out.println(method.getName() + "方法不需要进行测试");
}
}
}
}
}
通过上述程序,实现了一个类似Junit测试框架的@Test注解。