详解Java注解(Annotation)

时间:2022-07-24
本文章向大家介绍详解Java注解(Annotation),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

从JDK5开始,Java增加了对元数据的支持,也就是注解Annotation。注解就是代码里的特殊标记,这些标记可以在编译、类加载和运行时被读取,并进行相应的处理。通过使用注解,开发人员可以在不改变原有逻辑的基础上,在源文件中嵌入一些补充信息。 Annotation是一个接口,程序可以通过反射机制来获取指定程序元素的Annotation对象,然后通过Annotation对象来取得注解中的元数据。值得注意的是,Annotaion并不影响程序的执行,无论添加、删除注解,代码都照常运行。

1.基本注解

Java提供了几个基本的注解,功能如下:

  1. @Override:用来指定方法重载,它可以强制一个子类必须重写父类的方法。@Override只能修饰方法,不能修饰其他程序元素。
  2. @Deprecated:@Deprecated用于表示某个程序元素(类、方法等)已过时,当其他程序使用已过时的程序元素时,编译器会发出警告。
  3. @SuppressWarnings:@SuppressWarnings只是被该注解修饰的程序元素(以及该程序元素的所有子元素)。取消指定的编译器警告。

2.元注解

在java.lang,annotation包下提供了几个元注解Meta Annotation,主要用于修饰其他的Annotation定义,具体如下:

  1. @Retention:只能用于修饰Annotation定义,指定被修饰的Annotation可以保留多长时间。 @Retention内部包含了一个RetentionPolicy变量,该变量可以取三个值: RetentionPolicy.SOURCE:Annotation只保留在源代码中,编译时直接丢弃。 RetentionPolicy.CLASS:编译器将把Annotation记录在class文件中,当运行Java程序时无法获取 Annotation信息。改值为默认值。 RetentionPolicy.RUNTIME:目标类加载到JVM后依然保留,运行期可以通过反射获取注解信息。
  2. @Target:指定可以使用该注解的程序元素。内部使用ElementType指定该注解可以修饰哪些程序单元,如ElementType.METHOD表明注解可以修饰方法;ElementType.TYPE表示注解可以修饰类、接口等。

3.自定义注解

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()成员赋值,也可以不指定成员名,但如果同时对多个成员赋值,则必须使用”成员名=值”的形式。

4.通过反射提取注解信息

自定义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注解。