一、注解的作用

Annotation(注解)是 Java 提供的一种对元程序中元素关联信息和元数据(metadata)的途径
和方法。Annatation(注解)是一个接口,程序可以通过反射来获取指定程序中元素的 Annotation
对象,然后通过该 Annotation 对象来获取注解中的元数据信息。

注解是jdk1.5开始引入的一个新特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解。主要作用有一下几个方面:

  • 生成文档,通过代码里标识的元数据生成javadoc文档。
  • 编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证。
  • 编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码。
  • 运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例。

二、常见注解及分类

  • 标注注解:@Override@Deprecated@SuppressWarnings,分别用于标明重写某个方法、标明某个类或方法过时、标明要忽略的警告,用这些注解标明后编译器就会进行检查
  • 元注解:元注解是用于定义注解的注解。包括@Retention@Target@Inherited@Documented@Retention用于标明注解被保留的时间长短,即生命周期@Target用于标明注解使用的范围@Inherited用于标明注解是否可被子类继承,@Documented用于标明是否生成javadoc文档
  • 自定义注解:可以根据自己的需求定义注解,并可用元注解对自定义注解进行注解。
class A{
    public void test() {
        
    }
}

class B extends A{

    /**
     * 重载父类的test方法
     */
    @Override
    public void test() {
    }

    /**
     * 被弃用的方法
    */
    @Deprecated
    public void oldMethod() {
    }

    /**
     * 忽略告警
     * 
     * @return
     */
    @SuppressWarnings("rawtypes")
    public List processList() {
        List list = new ArrayList();
        return list;
    }
}

三、Java8提供了哪些新的注解?

@Repeatable——重复注解
@Native——使用 @Native 注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。对于 @Native 注解不常使用,了解即可。

四、自定义注解

package com.pdai.java.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyMethodAnnotation {

    public String title() default "";

    public String description() default "";

}

使用注解:

package com.pdai.java.annotation;

import java.io.FileNotFoundException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class TestMethodAnnotation {

    @Override
    @MyMethodAnnotation(title = "toStringMethod", description = "override toString method")
    public String toString() {
        return "Override toString method";
    }

    @Deprecated
    @MyMethodAnnotation(title = "old static method", description = "deprecated old static method")
    public static void oldMethod() {
        System.out.println("old method, don't use it.");
    }

    @SuppressWarnings({"unchecked", "deprecation"})
    @MyMethodAnnotation(title = "test method", description = "suppress warning static method")
    public static void genericsTest() throws FileNotFoundException {
        List l = new ArrayList();
        l.add("abc");
        oldMethod();
    }
}

用反射接口获取注解信息:

public static void main(String[] args) {
    try {
        // 获取所有methods
        Method[] methods = TestMethodAnnotation.class.getClassLoader()
                .loadClass(("com.pdai.java.annotation.TestMethodAnnotation"))
                .getMethods();

        // 遍历
        for (Method method : methods) {
            // 方法上是否有MyMethodAnnotation注解
            if (method.isAnnotationPresent(MyMethodAnnotation.class)) {
                try {
                    // 获取并遍历方法上的所有注解
                    for (Annotation anno : method.getDeclaredAnnotations()) {
                        System.out.println("Annotation in Method '"
                                + method + "' : " + anno);
                    }

                    // 获取MyMethodAnnotation对象信息
                    MyMethodAnnotation methodAnno = method
                            .getAnnotation(MyMethodAnnotation.class);

                    System.out.println(methodAnno.title());

                } catch (Throwable ex) {
                    ex.printStackTrace();
                }
            }
        }
    } catch (SecurityException | ClassNotFoundException e) {
        e.printStackTrace();
    }
}

测试输出:

Annotation in Method 'public static void com.pdai.java.annotation.TestMethodAnnotation.oldMethod()' : @java.lang.Deprecated()
Annotation in Method 'public static void com.pdai.java.annotation.TestMethodAnnotation.oldMethod()' : @com.pdai.java.annotation.MyMethodAnnotation(title=old static method, description=deprecated old static method)
old static method
Annotation in Method 'public static void com.pdai.java.annotation.TestMethodAnnotation.genericsTest() throws java.io.FileNotFoundException' : @com.pdai.java.annotation.MyMethodAnnotation(title=test method, description=suppress warning static method)
test method
Annotation in Method 'public java.lang.String com.pdai.java.annotation.TestMethodAnnotation.toString()' : @com.pdai.java.annotation.MyMethodAnnotation(title=toStringMethod, description=override toString method)
toStringMethod

五、不能使用关键字extends来继承某个@interface

不能使用关键字extends来继承某个@interface,但注解在编译后,编译器会自动继承java.lang.annotation.Annotation接口。
虽然反编译后发现注解继承了Annotation接口,请记住,即使Java的接口可以实现多继承但定义注解时依然无法使用extends关键字继承@interface
区别于注解的继承,被注解的子类继承父类注解可以用@Inherited: 如果某个类使用了被@Inherited修饰的Annotation,则其子类将自动具有该注解。

六、注解与反射接口

定义注解后,如何获取注解中的内容呢?反射包java.lang.reflect下的AnnotatedElement接口提供这些方法
AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的方法来访问Annotation信息。我们看下具体的接口:

  • boolean isAnnotationPresent(Class<?extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false。
  • <T extends Annotation> T getAnnotation(Class<T> annotationClass):返回该程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
  • Annotation[] getAnnotations():返回该程序元素上存在的所有注解,若没有注解,返回长度为0的数组。
  • <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass):返回该程序元素上存在的、指定类型的注解数组。
  • <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass):返回直接存在于此元素上的所有注解。与此接口中的其他方法不同,该方法将忽略继承的注释。
  • <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass):返回直接存在于此元素上的所有注解。与此接口中的其他方法不同,该方法将忽略继承的注释。
  • Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注解及注解对应的重复注解容器。