java注解——注解详解和自定义注解

在上一篇文章中,我们介绍了注解(Annotation)的相关概念,对于 java1.5 引入的这种新特性,我们可以称其为一种特殊的注释。之所以说它特殊是因为不同于普通注释(comment)只能存在于源码,而且还能在程序的编译期跟运行期中存在,会最终编译成一个.class文件。理所应当,注解会比普通注解起到更多的作用。
注解为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻方便地使用这些数据(通过 解析注解 来使用这些数据)。要想深入的理解并使用注解,必须能定义自己的注解,所以了解java提供的注解定义语法就非常重要了。
java.lang.annotation中包含所有定义自定义注解所需用到的元注解接口。比如接口java.lang.annotation.Annotation是所有注解都继承的接口,并且是自动继承,不需要定义时指定,类似于所有类都自动继承Object。首先我们先来了解什么是元注解


元注解

元注解是 Java 本身提供,是专门用来定义注解的注解。被用来提供对其他注解类型作说明。Java提供了四个标准的元注解类型:

  • @Target,
  • @Retention,
  • @Documented,
  • @Inherited
@Target

@Target标识 Annotation 可以被使用在什么地方。
使用方式:@Target(ElementType.METHOD)。其可能的 ElementType 取值包括:

  • CONSTRUCTOR --> 构造器(构造函数)的声明
  • FIELD ---> 域声明,包括 ENUM(枚举)实例
  • LOCAL_VIRIEBLE ---> 局部变量声明
  • METHOD ---> 方法声明
  • PACKAGE ---> 包声明
  • PARAMETER ---> 参数声明
  • *TYPE ---> 类,接口声明。包括注解类型和ENUM类型
@Retention

决定了 Annotation 的生命周期,即被保留的时间长短或者说在什么范围内有效。Annotation 一共有三种生命周期,有的注解只出现在源代码中;有的注解被被编译在class文件中,但被虚拟机忽略;有的则是被虚拟机加载,一直到程序运行都还存在。
使用方式:@Retention(RetentionPolicy = RUNTIME)。其可能的 **RetentionPolicy ** 取值包括:

  • SOURCE -> 在源文件中有效(即源文件保留)
  • CLASS -> 在class文件中有效(即class保留)
  • RUNTIME -> 在运行时有效(即运行时保留)
@Documented

描述 Annotation 应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。被例如javadoc此类的工具文档。

@Inherited

这也是一个标记注解。@Inherited 阐述了某个被标注的类型是被继承的。被标注过的 class 的子类所继承。类并不从它所实现的接口继承 Annotation,方法并不从它所重载的方法继承 Annotation。
当使用@Inherited类型标注的 Annotation 的 Retention 是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。


在本文的开头我们就说过,定义注解时会自动继承java.lang.annotation.Annotation接口。同时还要注意的是,在定义注解时不能继承其他注解和接口
注解的定义相对简单,有点类似于定义接口:

public @interface TestAnnotation {
      String field1() default "it's empty"; //default 设置默认值
}

其中的每个方法都相当于声明了一个配置参数,参数的名称就是方法名,参数的类型就是返回值类型。在自定义注解时有一些注意事项:

  1. 方法(配置参数)只能用 public 或者 默认(default)这两种访问权限修饰。
  2. 方法(配置参数)的返回值支持类型只包括以下几种:
    • 所有的数据类型(int,float,boolean,byte,double,char,long,short
    • String类型
    • Class类型
    • enum类型
    • Annotation(注解)类型
    • 以上所有类型的数组
  3. 注解中有一个特殊的存在:value。当注解中只有一个方法(配置参数)时,最好是将方法命名为 value。这样在使用注解时,就可以省略参数名,比如之后的例子:@Owner
简单的自定义注解使用例子
/**
* book相关的注解。
* 包括两个参数:名称和页数
**/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BookInfo {
      public String name() default "";
      public int pageSize() default 0;
}
/**
* 拥有人注解
* 只有一个参数,命名为 value
**/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Owner {
      public String value() default "";
}
public class UserBook {
      @BookInfo(name = "EngLish", pageSize = 80)
      public EngLish engLish;

      @Owner("testName")
      public String ownerName;
}

注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null。
在注解的语法部分中,对于特殊的 value 参数,当只指定该参数时,可以省略参数名和等号。这也是上面说的注意事项中第三点的原因

根据注解参数的不同,可以将注解分为以下几类:

  • 标注注解。没有参数成员,通过判断该注解是否存在获得相关的信息
  • 单值注解。只有一个参数成员,一般为 value,这样可以省略参数名和等号
  • 完整注解

你可能感兴趣的:(java注解——注解详解和自定义注解)