Java 注解浅析

一、什么是注解

注解(Annotation)即代码里的特殊标记,JDK5.0 之后引入了 Annotation 的概念来描述元数据,在 Java 中元数据以标签的形式存在于 Java 代码中,但是元数据标签的存在并不影响程序代码的编译和执行

在 JavaSE 中,注解的使用目的比较简单,例如标记过时的功能(@Deprecated)、限定必须重写父类的方法(@Override)、抑制编译器警告(@SuppressWarning)等.但是在JavaEE 中注解占据了更加重要的角色,例如之前需要实现 AOP 时,需要繁冗的 XML 配置,有了注解之后实现 AOP 变得更加的简洁.注解是一种趋势,在一定程度上可以说 框架 = 注解 + 反射 + 设计模式  

  

二、常见的注解示例

1、文档注释相关注解

@author: 说明开发该模块的作者,多个作者可以用逗号分割
@version: 说明该模块的版本
@since: 说明是从哪个版本开始增加的
@param: 对方法中的参数进行说明,如果没有参数就不能使用该注解
@return: 对方法返回值的说明,如果方法的返回值类型是 void ,则不能使用该注解
@thorws: 对方法可能抛出的异常进行说明,如果方法上没有显示的用 throws 抛出异常,则不能使用该注解
 
2、JDK 内置的三个基本注解
@Override: 限定必须重写父类方法
@Deprecated: 表示修饰的元素(类、方法等)已过时,通常是因为所修饰的结构存在危险或者已经有更好的选择方式
@SuppressWarning: 抑制警告
 
3、元注解
元注解可以理解为一种基本注解,它就是可以标注在其它的注解上面的注解,元注解有五种,分别是 @Retention、@Documented、@Target、@Inherited、@Repeatable
3.1、@Rentention
Rentention 的英文为保留期的意思,当 @Rentention 应用到一个注解上的时候,它解释说明了这个注解的存活时间
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
  // @Retention 的取值
    RetentionPolicy value();
}

@Rentention 的取值选项如下

@Rentention(RententionPolicy.SOURCE / RententionPolicy.CLASS / RententionPolicy.RUNTIME) 

RententionPolicy.SOURCE: 注解只在源码阶段保留,编译器编译的时候将会被丢弃

RententionPolicy.CLASS: 注解只被保留至编译器编译后的字节码文件,它不会被虚拟机加载,如果没有注明使用其它的值,默认的取值就是 RententionPolicy.CLASS

RententionPolicy.RUNTIME: 注解能够保留至运行期,通过反射可以获取该注解的信息

3.2、@Target

Target 的英文意思是目标的意思,如果一个注解上面标注了 @Target 注解,那么就指定了该注解可以作用的位置(例如:类上、构造方法上、成员变量上、局部变量上等)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
  // @Target 的取值 ElementType[] value(); }
@Target 的取值如下
public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
   // 类、接口(包括注解类型)、或者是枚举声明上 TYPE,
/** Field declaration (includes enum constants) */
  // 字段上 FIELD, /** Method declaration */
  // 方法上 METHOD, /** Formal parameter declaration */
  // 参数上 PARAMETER, /** Constructor declaration */
  // 构造方法上 CONSTRUCTOR, /** Local variable declaration */
  // 局部变量上 LOCAL_VARIABLE, /** Annotation type declaration */
  // 注解上 ANNOTATION_TYPE, /** Package declaration */
  // 包上 PACKAGE, /** * Type parameter declaration * * @since 1.8 */
  // 参数类型声明上 TYPE_PARAMETER, /** * Use of a type * * @since 1.8 */
   TYPE_USE }

3.3、@Documented

如果某一个注解上面使用了 @Documented 注解,那么该注解将会被 JavaDoc 工具提取成文档,定义了 @Documented 的注解必须包含@Rentention(RetentionPolicy.RUNTIME)

3.4、@Inherited

某个注解如果使用了 @Inherited 元注解,那么该注解将具有继承性
举个例子:@TestInheritedAnnotation 被 @Inherited 修饰,父类 Animal 使用了 @TestInheritedAnnotation 注解,那么 Animal 的子类都自动具有@TestInheritedAnnotation 这个注解
 
@Inherited样例:
// 自定义 @TestInheritedAnnotation 注解,然后在该注解上使用元注解 @Inherited 
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TestInheritedAnnotation {
    String value();
}


// 定义动物类作为父类,并且使用了 @TestInheritedAnnotation 注解
@TestInheritedAnnotation(value = "xiaomaomao")
public class Animal {
}


// 定义 Animal 的子类 Cow,测试 Cow 中是不是也有 @TestInheritedAnnotation 注解
public class Cow extends Animal {
    public static void main(String[] args) {
        System.out.println(isHasInheritedAnnotation());
    }

    public static String isHasInheritedAnnotation() {
        String value = "";
        // 判断 Cow 类上是否使用了 @TestInheritedAnnotation 注解
        boolean hasAnnotation = Cow.class.isAnnotationPresent(TestInheritedAnnotation.class);
        if (hasAnnotation) {
            TestInheritedAnnotation annotation = Cow.class.getAnnotation(TestInheritedAnnotation.class);
            value = annotation.value();
        }
        return value;
    }
}

3.5、@Repeatable

可重复注解, @Repeatable 注解是 JDK 8 之后才加入进来的,是 JDK 8 新特性,它的意思是注解的值可以取多个

// 自定义容器注解 Person
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Person {
    // @Person 注解是 @RepeatableAnnotation 的容器,定义类型为注解数组
    RepeatableAnnotation[] value();
}

// 自定义注解 RepeatableAnnotation
// 使用 @Repeatable 注解的时候,必须要为当前注解定义一个容器注解,我们这里是 @Person
@Repeatable(Person.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface RepeatableAnnotation {
    String role() default "coder";
}

// 测试类
@RepeatableAnnotation(role = "president")
@RepeatableAnnotation(role = "product manager")
@RepeatableAnnotation(role = "technology manager")
@RepeatableAnnotation(role = "cleaner")
public class RepeatableAnnotationDemo {
    public static void main(String[] args) {
        StringBuffer stringBuffer = new StringBuffer();
        // 判断 RepeatableAnnotationDemo 类上是否使用了@Person 注解
        boolean hasPersonAnnotation = RepeatableAnnotationDemo.class.isAnnotationPresent(Person.class);
        if (hasPersonAnnotation) {
			// 获取 RepeatableAnnotationDemo 类上 Person 注解
            Person personAnnotation = RepeatableAnnotationDemo.class.getAnnotation(Person.class);
            // 通过 Person 注解容器获取到该容器内的所有 @RepeatableAnnotation 注解
            RepeatableAnnotation[] repeatableAnnotations = personAnnotation.value();
            // 获取所有的 @RepeatableAnnotation 中的 role 值并拼接输出
            for (RepeatableAnnotation repeatableAnnotation : repeatableAnnotations) {
                stringBuffer.append(repeatableAnnotation.role() + "===>");
            }
            System.out.println(stringBuffer);
        }
    }
}

// 测试结果
president===>product manager===>technology manager===>cleaner===>

  

三、注解的属性

注解的属性也叫做成员变量,注解只有成员变量,没有方法,注解的成员变量是以 无形式参数的方法 来声明的,其方法中定义了该成员变量的类型和成员方法的名字

注意: 在注解中定义属性,属性的类型必须是八种基本数据类型、类、接口、注解以及它们的数组

如下自定义的注解中有 int 类型的 id,它有一个默认值 10086、String 类型的 userName、以及 String 数组类型的 hobby

@Retention(value=RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface TestAnnotation {
  // 注解中可以有默认值,使用 default 关键字来指定 int id() default 10086; String userName(); String[] hobby(); }

1、如何给注解赋值

// 根据 @TestAnnotation 的属性类型和属性名称依次给注解赋值,多个值用逗号隔开,由于 id 属性有默认值,
// 那么这里可以不用给id赋值,如果显示的给id赋值的话,那么它会覆盖默认值
@TestAnnotation(userName = "xiaomaomao",hobby={"film","novel","play computer games"})
public class AnnotationDemo {
}

2、如果注解只有一个属性的情况

@Retention(value=RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface TestAnnotation {
  // 如果注解只有一个属性,请使用 value 来命名该属性
   String value();
}  

取值的时候需要注意

// 如果注解只有一个属性可以不写 value,直接 @TestAnnotation("xiaomaomao") 赋值也是一样的
@TestAnnotation(value="xiaomaomao")
public class AnnotationDemo {
}

  

四、自定义注解

1、定义新的 Annotation 类型使用 @interface 关键字.

2、自定义注解自动继承了java.lang.annotation.Annotation 接口.

3、Annotation 的成员变量在 Annotation 定义中以无参数方法的形式来声明,其方法名和返回值定义了该成员的名字和类型,我们称为配置参数.类型只能是八种基本数据类型、String 类型、Class 类型、enum 类型、Annotation 类型、以上所有类型的数组.

4、可以在定义 Annotation 的成员变量时为其指定初始值,指定成员变量的初始值可使用 default 关键字.

5、如果只有一个参数成员,建议使用参数名为 value.

6、如果定义的注解含有配置参数,那么使用时必须指定参数值,除非它有默认值.格式是 "参数名 = 参数值",如果只有一个参数成员,且名称为 value,可以省略 "value="

7、没有成员定义的 Annotation 称为标记;包含成员变量的 Annotation 称为元数据 Annotation.

特别注意:自定义注解必须配上注解的信息处理流程才有意义.

 
自定义注解样例:
// 自定义注解 TestAnnotation
@Retention(value=RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface TestAnnotation {
    int id() default 10086;
    String userName();
    String[] hobby();
}

// 测试类
@TestAnnotation(id=10001,userName = "xiaomaomao",hobby={"film","novel","play computer games"})
public class AnnotationDemo {
    public static void main(String[] args) {
        // 判断 AnnotationDemo 类上是否使用了 @TestAnnotation 注解
        boolean hasAnnotation = AnnotationDemo.class.isAnnotationPresent(TestAnnotation.class);
        StringBuffer stringBuffer = new StringBuffer();
        if (hasAnnotation) {
            // 获取 AnnotationDemo 上的所有注解,这里只有一个 @TestAnnotation 注解
            Annotation[] annotations = AnnotationDemo.class.getAnnotations();
            for (Annotation annotation : annotations) {
                // 获取 @TestAnnotation 注解中的属性并进行拼接
                if (TestAnnotation.class.equals(annotation.annotationType())) {
                    TestAnnotation testAnnotation = (TestAnnotation) annotation;
                    int id = testAnnotation.id();
                    String userName = testAnnotation.userName();
                    String[] hobbys = testAnnotation.hobby();
                    stringBuffer.append(id);
                    stringBuffer.append("
");
                    stringBuffer.append(userName);
                    stringBuffer.append("
");
                    stringBuffer.append("[");
                    for (String hobby : hobbys) {
                        stringBuffer.append(hobby+"、");
                    }
                    stringBuffer.append("]");
                    stringBuffer.append("
");
                    // 获取 @TestAnnotation 上的注解类型并进行拼接
                    Annotation[] underTestAnnotations = testAnnotation.annotationType().getAnnotations();
                    for (Annotation underTestAnnotation : underTestAnnotations) {
                        Class<? extends Annotation> aClass = underTestAnnotation.annotationType();
                        stringBuffer.append(aClass);
                        stringBuffer.append("
");
                    }

                }
                System.out.println(stringBuffer);
            }
        }
    }
}

// 测试结果
10001
xiaomaomao
[film、novel、play computer games、]
interface java.lang.annotation.Retention
interface java.lang.annotation.Target

  

原文地址:https://www.cnblogs.com/xiaomaomao/p/13474555.html