Java注解学习

简介

Java注解是JDK1.5引入的一种注释机制,它不会改变编译器的编译方式,Java编译器对包含注解和不包含注解的代码会生成相同的Java虚拟机指令。在实际应用中,注解只是一种标识,具体的操做须要借助其余工具来解析和处理java

注解语法

注解是使用@interface来定义的,全部注解都隐式的扩展自java.lang.annotation.Annotation接口。数组

以下MyFirstAnnotation是一个自定义注解,它具备两个参数name和value,默认值都为空字符串。在它的定义之上还标注了@Retention和@Target两个元注解:函数

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyFirstAnnotation {
    String name() default "";

    String value() default "";
}

注解参数类型是有限制的,必须限制在如下几种类型中:工具

  1. 基本类型
  2. String
  3. Class
  4. enum类型
  5. 注解类型
  6. 由前面所述类型组成的数组

例如,若是不使用以上几种,则会出现编译错误:测试

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyFirstAnnotation {
    String name() default "";

    Integer value() default 1;//包装类型也不行,编译错误
}

@Target和@Retention这种做用在注解上的注解,称之为元注解。经常使用的元注解以下:code

1.@Target,用于标识注解能够标注的位置,接收一个ElementType[]参数。参数取值能够参考ElementType枚举类:继承

public enum ElementType {
    /** 类、接口(包括注释类型)或枚举声明 */
    TYPE,

    /** 字段声明(包括enum常量) */
    FIELD,

    /** 方法声明 */
    METHOD,

    /** 形参声明 */
    PARAMETER,

    /** 构造函数声明 */
    CONSTRUCTOR,

    /** 局部变量声明 */
    LOCAL_VARIABLE,

    /** 注解类型声明 */
    ANNOTATION_TYPE,

    /** 包声明 */
    PACKAGE,

    /**
     * 类型参数声明
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * 任何类型名称
     *
     * @since 1.8
     */
    TYPE_USE
}

当指定的@Target和注解的使用位置不匹配时就会出现编译错误,以下所示,@Target(ElementType.METHOD)表示MyFirstAnnotation注解只能用来标注方法,标注在类上就出现编译错误:接口

//编译错误
@MyFirstAnnotation
public class Demo {

    //正确
    @MyFirstAnnotation
    public void doSomeThing(){
    }

}

2.@Retention,用于标识注解能够保留多久,接收一个RetentionPolicy参数,参数取值能够参考RetentionPolicy枚举类:字符串

public enum RetentionPolicy {
    /**
     * 表示注解只会存在于.java的源代码文件中,不会保留到编译后的.class文件中
     */
    SOURCE,

    /**
     * 表示注解能够保留到.class文件中,可是不会被Java虚拟机所加载
     */
    CLASS,

    /**
     * 表示注解能够保留到.class文件中,并由虚拟机加载
     */
    RUNTIME
}

3.@Documented,表示这个注解能出如今javadoc中。get

4.@Inherited,表示当这个注解用于一个类的时候,可以自动的被它的子类继承。

5.@Repeatable,表示这个注解能够在同一个位置应用屡次。

默认状况下,同一个位置添加多个重复注解会有编译错误:

public class Demo {
    //编译错误
    @MyFirstAnnotation(name = "张三")
    @MyFirstAnnotation(name = "李四")
    @MyFirstAnnotation(name = "王五")
    public void doSomeThing(){
    }

}

能够经过添加@Repeatable元注解来使@MyFirstAnnotation能重复使用。
首先须要建立一个容器注解@MyFirstAnnotations,容器注解@MyFirstAnnotations必需要有一个参数value,而且其类型为MyFirstAnnotation[]:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyFirstAnnotations {
    MyFirstAnnotation[] value();
}

而后添加@Repeatable注解,并指定容器注解。

@Repeatable(MyFirstAnnotations.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyFirstAnnotation {
    String name() default "";

    String value() default "";
}

关于注解的使用须要注意,注解参数是不能为null的,默认值也是不能为null

自定义注解测试

定义注解@RepeatMethod,包含int类型参数value。

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 RepeatMethod {

    int value() default 1;

}

在doSomeThing方法上应用注解。若是要指定的注解参数为value,而且没有指定其余的参数值时,能够采用简写的方式,省略参数名和等号。

public class Demo {
    @RepeatMethod(5)
    public void doSomeThing(){
        System.out.println("----注解测试----");
    }
}

经过反射获取注解信息,而后作对应的处理,如假设@RepeatMethod注解的做用是重复调用被标注的方法,参数value是指定重复调用方法的次数:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MyDemo {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, InvocationTargetException {
        Class cla = Demo.class;
        Method[] methods = cla.getMethods();
        Object demo = cla.newInstance();
        for (Method method : methods){
            //判断方法上是否有标注@RepeatMethod注解
            if(method.isAnnotationPresent(RepeatMethod.class)){
                //获取RepeatMethod注解的参数值
                RepeatMethod repeatMethod = method.getAnnotation(RepeatMethod.class);
                for (int i = 0;i < repeatMethod.value(); i++)
                method.invoke(demo, null);
            }
        }
    }
}

输出结果:

----注解测试----
----注解测试----
----注解测试----
----注解测试----
----注解测试----