JDK8阅读笔记(三)java-lang:Annotation

1,041 阅读9分钟

JDK8中携带了许多注解,注解由接口Annotation表示,通过包来归纳注解:

  • java.beans + ConstructorProperties (java.beans) + Transient (java.beans)
  • java.lang + Override (java.lang) + SafeVarargs (java.lang) + SuppressWarnings (java.lang) + Deprecated (java.lang) + FunctionalInterface (java.lang)
  • java.lang.annotation + Inherited (java.lang.annotation) + Retention (java.lang.annotation) + Documented (java.lang.annotation) + Target (java.lang.annotation) + Repeatable (java.lang.annotation) + Native (java.lang.annotation)
  • java.lang.invoke + DontInline (java.lang.invoke) + ForceInline (java.lang.invoke) + Stable (java.lang.invoke) + Compiled (java.lang.invoke.LambdaForm) + Hidden (java.lang.invoke.LambdaForm) + PolymorphicSignature (java.lang.invoke.MethodHandle)
  • com.sun.tracing + ProviderName (com.sun.tracing) + ProbeName (com.sun.tracing)
  • com.sun.tracing.dtrace + ModuleName (com.sun.tracing.dtrace) + NameAttributes (com.sun.tracing.dtrace) + ProviderAttributes (com.sun.tracing.dtrace) + Attributes (com.sun.tracing.dtrace) + FunctionName (com.sun.tracing.dtrace) + ModuleAttributes (com.sun.tracing.dtrace) + FunctionAttributes (com.sun.tracing.dtrace) + ArgsAttributes (com.sun.tracing.dtrace)
  • javax.management
  • MXBean (javax.management)
  • DescriptorKey (javax.management)
  • Contended (sun.misc)
  • CallerSensitive (sun.reflect)

一、java.lang.annotation

java.lang.annotation包是注解的核心,包含了 Annotation接口(用于表示注解)以及几个元注解。

Annotation——注解

Annotation接口

java.lang.annotation.Annotation 代表了注解的通用接口,定义了注解常用的几个方法

/**
 * 所有注解扩展类型的通用接口。 请注意,手动扩展此接口的接口不会定义注释类型。 另外,此接口本身并未定义注释类型。
 * ` 
 */
public interface Annotation {
  /**
  * 用于判断在逻辑上是否是与此注释相同
  */
  boolean equals(Object obj);
  /**
   * 返回此注释的哈希码:注释的哈希码是其成员(包括具有默认值的成员)的哈希码之和
   */
  int hashCode();
  /**
   * 返回此注释的字符串表示形式,表示的细节是依赖于实现的。
   */
  String toString();
  /**
   * 返回此注释的注释类型
   */
  Class<? extends Annotation> annotationType();
}

注解的 错误/异常 提示

编译时的错误提示:

  • java.lang.annotation.AnnotationFormatError 表示注解使用错误。当注解解析器尝试从类文件中读取注解并确定注解格式错误时抛出, 通过反射读取注释的API可能会抛出此错误。

运行时的异常提示:

  • java.lang.annotation.AnnotationTypeMismatchException 异常,被抛出时表示程序已尝试访问注释元素,该元素的类型在注释被编译(或序列化)后发生了变化。 用于以反射方式读取注释的API可能会抛出此异常。
public class AnnotationTypeMismatchException extends RuntimeException {
    private static final long serialVersionUID = 8125925355765570191L;

  /**
   * 注释元素的Method对象
   */
  private final Method element;

  /**
   * 在注释中发现的(错误的)数据类型,该字符串也可以(但不是必须)包含该数据值。 字符串的确切格式未指定。
   */
  private final String foundType;

  public AnnotationTypeMismatchException(Method element, String foundType) {
      super("Incorrectly typed data found for annotation element " + element
            + " (Found data of type " + foundType + ")");
      this.element = element;
      this.foundType = foundType;
  }

  /**
   * 返回错误类型元素的Method对象
   */
  public Method element() {
      return this.element;
  }

  /**
   * 返回在错误类型元素中找到的数据类型
   */
  public String foundType() {
      return this.foundType;
  }
}

元注解

java.lang.annotation 中还包含了几个注解,JDK8 中的注解如下:

  • @Target 作用范围
  • @Retention 保留策略
  • @Inherited 继承策略
  • @Documented 文档生成策略
  • @Repeatable(JDK8) 重复配置注解
  • @Native(非元注解) 该注解用于修饰成员变量,用于表示这个变量可以被本地代码引用

除了@Native注解,其它几个注解都是元注解,元注解主要用于描述注解的信息。

@Target

java.lang.annotation.Target 该接口定义了元注释,用于描述注释本身;表明了注释的作用类型。相关源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
  /**
   * return:可以应用注释类型的元素种类的数组
   */
  ElementType[] value();
}

单个ElementType常量在@Target注释中出现多次会导致编译时错误。

  @Target({ElementType.FIELD, ElementType.METHOD, ElementType.FIELD})
  public @interface Bogus {
      ...
  }

正确用法:

  @Target({})
  public @interface MemberType {
      ...
  }

@Target何处生效由 ElementType 的值决定。

java.lang.annotation.ElementType 的枚举常量提供了对注解可能出现在 Java 程序中的语法位置做了简单分类。 这些常量在java.lang.annotation.Target元注释中使用,声明了以下几个字段:

  • TYPE 类、接口(包括注解类型)或枚举
  • FIELD 字段 (包括枚举常量)
  • METHOD 方法
  • PARAMETER 形参
  • CONSTRUCTOR 构造函数
  • LOCAL_VARIABLE 局部变量
  • ANNOTATION_TYPE 注解类型
  • PACKAGE 包声明
  • TYPE_PARAMETER 在类型参数的声明语句中,如泛型
  • TYPE_USE 包含TYPE、TYPE_PARAMETER,便于使用

@Retention

java.lang.annotation.Retention 指示带注释类型的注释将保留多长时间;如果注释类型声明中不存在 Retention 注释,则保留策略默认为 RetentionPolicy.CLASS;仅当元注释类型直接用于注释时,保留元注释才有效。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
  /**
   * 返回保留策略
   * @return the retention policy
   */
  RetentionPolicy value();
}

在JDK8中,保留策略由 java.lang.annotation.RetentionPolicy 枚举类定义。相关源码如下:

public enum RetentionPolicy {
  /**
   * 编译时丢弃注解。
   */
  SOURCE,

  /**
   * 注解将由编译器记录在类文件中,但在运行时不需要由 VM 保留(默认)
   */
  CLASS,

  /**
   * 注解将被编译器记录在类文件中,并在运行时由 VM 保留,因此它们可以被反射读取
   * 可以被 java.lang.reflect.AnnotatedElement 反射类读取。
   */
  RUNTIME
}

@Inherited

@Inherited注解的全路径为 java.lang.annotation.Inherited,用于标记注解,表示子类自动继承父类的注解。例如:自定义注解A中被标记了@Inherited注解,用@A注解的B类后,如果用C类继承B类,那么C类也会继承对应注解。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

@Documented

@Documented 注解表明这个注解应该被 javadoc工具记录。默认情况下,javadoc是不包括注解的。 但如果声明注解时指定了 @Documented,则它会被 javadoc 之类的工具。

package java.lang.annotation;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

@Repeatable(JDK8)

@Repeatable 也是jdk8中新增的注解,该元注解可以赋予注解重复特性,可以在一个生效位置使用多个相同注解。相关源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    /**
     * 指示可重复注释类型的包含注释类型
     */
    Class<? extends Annotation> value();
}

java.lang

java.lang包中包含了几个常用的注解,如下所示:

  • @Override:限定重写父类方法,该注解只能用于方法
  • @Deprecated:用于表示所修饰的元素(类,方法等)已过时
  • @SuppressWarnings:抑制编译器警告
  • @SafeVarargs
  • @FunctionalInterface

@SafeVarargs

@SafeVarargs 在JDK 7中引入,主要目的是处理可变参数中的泛型,此注解会告知编译器:在可变长参数中的泛型是类型安全的。

数组元素的数据类型在编译和运行时都是确定的,而泛型的数据类型只有在运行时才能确定下来,因此当把一个泛型存储到数组中时,编译器在编译阶段无法检查数据类型是否匹配,因此会给出警告信息:heap pollution,通过该注解可以抑制该警告。

注解@SafeVarargs 的类路径为java.lang.SafeVarargs,相关源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface SafeVarargs {
}

@FunctionalInterface

@FunctionalInterface 用于声明函数式接口,该注解为JDK8中的lamda表达式设计的,用于约束接口方法;标注的接口里面只能有一个非 Object 对象的公共方法的抽象方法,可以有多个静态方法和默认方法。

被@FunctionalInterface 注解的接口,可以用lamda表达式实现。

相关源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

用法如下:

@FunctionalInterface
public interface Test{
    void sayMessage(String message);
    default void t1(){
       
    }
    default void t2(){
    }
}

@FunctionalInterface注解标注接口后,使接口只能有一个抽象方法,便于通过Lambda实现匿名内部类。

二、java.lang.invoke

java.lang.invoke该包包含了方法句柄的使用,定义了几个非公开的注解:

  • @DontInline
  • @ForceInline
  • @Stable
  • @Hidden
  • @Compiled

@DontInline与@ForceInline

@DontInline 注解用于标记方法为内联方法,源码如下:

@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
@interface DontInline {
}

什么是内联函数

在计算机科学中,内联函数(有时称作在线函数或编译时期展开函数)是一种编程语言结构,用来建议编译器对一些特殊函数进行内联扩展(有时称作在线扩展);也就是说建议编译器将指定的函数体插入并取代每一处调用该函数的地方(上下文),从而节省了每次调用函数带来的额外时间开支。

内联函数的选择策略:

  • 热点代码(经常调用的代码)
  • 方法体大小(方法体过大会内联会影响性能)

@ForceInline 注解可以声明方法以及构造器,用于避免被编译器转换为内联函数。

@Stable

@Stable注解 用于标记属性中所有组成变量最多更改一次值;由于所有字段都以引用的默认值 null 开头(相应地,原语为零),因此该注释表明存储在该字段中的第一个非空(相应地,非零)值永远不会改变。

如果该字段不是数组类型,则表示为稳定的值只是该字段的值;final字段也可以使用@Stable注解,但该声明对非数组字段无效。如果@Stable作用的字段是数组,则该数组每个元素都可以修改一次。

源码如下:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Stable {
}

@Hidden 与 @Compiled

@Hidden 与 @Compiled注解与Lambda相关,源码如下:

  /**
   * 字节编译的 LambdaForms 的内部标记。
   */
  /*non-public*/
  @Target(ElementType.METHOD)
  @Retention(RetentionPolicy.RUNTIME)
  @interface Compiled {
  }
  /**
   * LambdaForm 解释器框架的内部标记
   */
  /*non-public*/
  @Target(ElementType.METHOD)
  @Retention(RetentionPolicy.RUNTIME)
  @interface Hidden {
  }

三、其它注解

DTrace(com.sun.tracing)

com.sun.tracing包下包含了一些DTrace工具相关的注解。

DTrace(全称Dynamic Tracing),也称为动态跟踪,是由 Sun™ 开发的一个用来在生产和试验性生产系统上找出系统瓶颈的工具,可以对内核(kernel)和用户应用程序(user application)进行动态跟踪并且对系统运行不构成任何危险的技术。在任何情况下它都不是一个调试工具, 而是一个实时系统分析寻找出性能及其他问题的工具。 DTrace 是个特别好的分析工具,带有大量的帮助诊断系统问题的特性。还可以使用预先写好的脚本利用它的功能。 用户也可以通过使用 DTrace D 语言创建他们自己定制的分析工具, 以满足特定的需求。

相关注解如下:

  • com.sun.tracing + ProviderName (com.sun.tracing) + ProbeName (com.sun.tracing)
  • com.sun.tracing.dtrace + ModuleName (com.sun.tracing.dtrace) + NameAttributes (com.sun.tracing.dtrace) + ProviderAttributes (com.sun.tracing.dtrace) + Attributes (com.sun.tracing.dtrace) + FunctionName (com.sun.tracing.dtrace) + ModuleAttributes (com.sun.tracing.dtrace) + FunctionAttributes (com.sun.tracing.dtrace) + ArgsAttributes (com.sun.tracing.dtrace)

MXBean(javax.management)

MXBean是一种引用预定义数据类型的MBean。通过这种方式,您可以确保任何客户机(包括远程客户机)都可以使用您的MBean,而不需要客户机访问代表MBean类型的特定的类。MXBean提供一种方便的方法来绑定数据,而不需要客户端进行特殊的绑定操作。相关注解如下:

  • MXBean :显式地标记某一接口是否为 MXBean 接口的注释。默认情况下,如果接口的名称以 MXBean 结尾(如 SomethingMXBean),则该接口为 MXBean 接口
  • DescriptorKey:元注释。描述注释元素如何与 Descriptor 的字段相关的元注释,这可以是 MBean 的描述符,也可以是 MBean 中的属性、操作或构造函数,或者是操作或构造函数的参数。