前言

要想理解Annotation的作用,就必须先得知道何为元数据。元数据就是关于数据的数据,是添加到程序元素诸如方法、字段、类和包上的额外信息。
注解Annotation就是Java平台的元数据,该机制允许在代码中添加自定义注释,并允许通过反射,以编程方式访问元数据注释。通过为类、方法等提供附加额外数据的标准方法,元数据功能具有简化和改进的潜在能力。

Annotation作用

注解的作用,就是元数据的作用。需要注意的是,Annotation不影响程序代码的执行。无论是增加、删除Annotation,代码执行都不会受到影响。

内建注解

Java提供了几种内建的注解,如@Override、@Deprecated、@SuppressWarnings以及@FunctionalInterface。内建注解主要体现了元数据的编译检查作用。

1.@Override

告知编译器,我们要重写超类的当前方法。如果某方法带有该注解,但并没有重写父类的相应方法,会报错。如果父类没有这个重写方法,也会报错。@Override可适用的元素为方法,仅仅保留在java源文件中。

2.@Deprecated

告知编译器,该方法已经过时了。

public class Person {

    @Deprecated
    public void info(){

    }
}

这里再调用info方法,就提示过时了。

Person person = new Person();
        person.info();

3.@SuppressWarnings

告知编译器忽略特定的警告信息,比如在泛型中使用原生数据类型就会发出警告,使用该注解后警告不再发出。可适用于除注解类型声明和包名之外的所有元素,仅仅保留在java源文件中。
此注解有value()方法,支持用户设置多个字符串参数来忽略警告。如@SuppressWarnings(value={“deprecation”,“uncheck”})

4.@FunctionalInterface

告知编译器,检查这个接口。保证该接口只能有一个抽象方法,否则编译出错。

元Annotation

java.lang.annotation包提供了6个元Annotation,其中有5个用于修饰其他Annotation定义。
自定义Annotation:当一个接口直接继承java.lang.annotation.Annotation时,依然还是接口而非注解。只能通过@interface关键字的方式,隐含的继承.Annotation。

1.@Documented

用户指定被其修饰的Annotation将会被javadoc工具提取为文档,如果定义Annotation类使用了@Documented修饰,所有使用其修饰的API文档中将包含该Annotation说明

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {CONSTRUCTOR,FIELD,LOCAL_VARIABLE,METHOD,PACKAGE,PARAMETER,TYPE})
public @interface Deprecated {
}

定义@Deprecated的时候使用了@Documented,任何元素使用@Deprecated修饰再生成API文档时,将会包含@Deprecated的说明

@Deprecated
public String(byte[] ascii,int hibyte,int offset,int count)

该注解实现了编写文档功能。

2.@Inherited

被他修饰的Annotation就会具有继承性,如果父类被注解,子类将会被其修饰

3.@retention

表示该注解类型的注解保留的时长,当注解类型声明中没有@retention元注解,则默认保留策略为RetentionPolcy.CLASS,保留策略是枚举类型,共有3种。
a.SOURCE仅存在Java源文件,经过编译器后就丢弃相应的注解
b.CLASS存在java源文件,以及经编译器生成的class字节码文件。但在运行时JVM不再保留
c.RUNTIME存在源文件、class字节码文件,以及保留在运行时JVM中,可通过反射读取注解

4.@Target

表示该注解类型的所适用的程序元素的类型,无该注解可默认为适用所有元素类型,否则强制实施相应适用限制。程序元素是枚举类型,分为8种:
ANNOTATION_TYPE(注解类型说明)、CONSTURCTOR(构造方法声明)、FIELD(字段声明)、LOCAL_VARIABLE(局部变量声明)、METHOD(方法声明)、PACKAGE(包声明)、PARAMETER(参数声明)、TYPE(类、接口或枚举声明)

自定义注解

创建自定义注解,和创建接口相似,需要以@开头。

@Documented
@Target(ElementType.METHOD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)//保留策略
public @interface MyAnnotation {
    String name();
    String website() default "hello";
    int revision() default 1;

}

自定义注解中定义变量的规则:以无形参的方法形式来声明,即注解方法不带参数:name() website();注解方法返回值类型:基本类型、String、Enums、Annotation以及前面这些数组类型;注解方法可有默认值:default “hello” 默认website=“hello”;
当然注解中也可以不存在成员变量,再使用解析注解进行操作时,仅以是否包含该注解来进行操作。当注解中有成员变量时,若没有默认值,需在使用注解时指明成员变量的值。

public class AnnotationDemo {

    @MyAnnotation(name = "husky",website = "hello",revision = 1)
    public static void main(String [] args){
        System.out.println("我是主方法");
    }

    @SuppressWarnings({"deprecation","uncheck"})
    @MyAnnotation(name = "husky",website = "hello",revision = 2)
    public void demo(){
        System.out.println("我是demo");
    }
}

由于设定了保留策略为RetentionPolicy.RUNTIME,可以在运行时通过反射来使用,否则无法通过反射机制来获取。

注解解析

反射位于java.lang.reflect,其中有一个接口为AnnotatedElement。该接口主要有5个实现类:Class、Consturctor、Field、Method、Package。此外,还定义了几个注释相关的核心方法:

java如何创建注解后缀 java内建注解_java如何创建注解后缀

因此,当获取了某个类的Class对象,然后获取其Field、Method对象,通过这4个核心方法提取其中的注解,然后获得注解的详细信息。

public class AnnotationParser {
    public static void main(String [] args) throws SecurityException,ClassNotFoundException{
        String clazz = "com.example.husky.mergesort.Documented.AnnotationDemo";
        Method[] demoMethod = AnnotationParser.class.getClassLoader().loadClass(clazz).getMethods();
        for (Method method: demoMethod){
            if (method.isAnnotationPresent(MyAnnotation.class)){
                MyAnnotation annotationInfo = method.getAnnotation(MyAnnotation.class);

                System.out.println("method:"+method);
                System.out.println("name"+annotationInfo.name()+",website:"+
                        annotationInfo.website()+",revision:"+annotationInfo.revision());
            }
        }
    }
}