Java 注解(Annotation)

一、Java 注解有哪些?

  Java 注解,也叫元数据,又称 Java 标注,即一种描述数据的数据。注解是 JDK1.5 版本开始引入的一种注释机制,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。

  注解可分为三类:

  • Java 自带的标准注解
  • 元注解
  • 自定义注解
二、Java 自带的标准注解(内置注解)

  Java 自带的标准注解,也叫内置注解,在 java.lang 包下(随着 JDK 版本的升级,Java 开发者们可能会在之后更高的版本中引入更多的内置注解),包括 @Override、@Deprecated、@SuppressWarnings、@FunctionalInterface、@SafeVarargs 等,使用这些注解后编译器就会进行检查。

  各个注解的作用如下。

  • @Override
    检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误
  • @Deprecated
    标记过时方法。如果使用该方法,会报编译警告,但是不影响使用
  • @SuppressWarnings
    指示编译器去忽略注解中声明的警告
  • @FunctionalInterface
    Java 8 开始支持,标识一个匿名函数或函数式接口
  • @SafeVarargs
    Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告
三、元注解

  元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。明白点说,就是我们定义注解时用的注解就是元注解。元注解是用于定义注解的注解,在 java.lang.annotation 中。

  除了 @ 符号,注解很像是一个接口。

  在注解中一般会有一些元素以表示某些值(例如 @SuppressWarnings("all") 括号内的 all)。注解的元素看起来就像接口的方法,唯一的区别在于可以为其制定默认值。没有元素的注解称为标记注解,例如 @Test 就是一个标记注解。

  注解的可用类型包括以下几种:所有基本数据类型、String、Class、enum、Annotation、以上类型的数组形式。元素不能有不确定的值,即要么有默认值,要么在使用注解的时候提供元素的值。元素不能使用 null 作为默认值。注解在只有一个元素且该元素的名称是 value 的情况下,在使用注解的时候可以省略 "value=",直接写需要的值即可。

  元注解一共有 5 种:@Retention、@Documented、@Target、@Inherited、@Repeatable

3.1 @Retention

  用于指定注解的保留策略,标识这个注解怎么保存,是只在代码中,还是编入 class 文件中,或者是在运行时可以通过反射访问。它的 value 有以下三个参数。

  • RetentionPolicy.SOURCE
    注解只保留在源文件中,在编译时会被编译器丢弃
  • RetentionPolicy.CLASS
    注解保留在 class 文件中,但不会被加载到 JVM(Java 虚拟机)中,运行时无法获得(这是默认策略)
  • RetentionPolicy.RUNTIME
    注解会被保留在 class 文件中,且会被加载到虚拟机中,注解保留在程序运行期间,此时可以通过反射获得定义在某个类上的所有注解
3.2 @Documented

  用于将注解包含在 javadoc 中。默认情况下,javadoc 是不包括注解的,但如果使用了 @Documented 注解,则相关注解类型信息会被包含在生成的文档中。

3.3 @Target

  用于指定注解的使用范围,说明该注解可以被声明在哪些元素之前。它的 value 是 ElementType 集合。ElementType 枚举值如下所示。

  • ElementType.TYPE
    应用于类、接口(包括注解类型)、枚举
  • ElementType.FIELD
    应用于字段
  • ElementType.METHOD
    应用于方法
  • ElementType.PARAMETER
    应用于方法的参数
  • ElementType.CONSTRUCTOR
    应用于构造函数
  • ElementType.LOCAL_VARIABLE
    应用于局部变量
  • ElementType.ANNOTATION_TYPE
    应用于注解类型
  • ElementType.PACKAGE
    应用于包
  • ElementType.TYPE_PARAMETER(JDK 1.8 新增)
    应用于类型变量
  • ElementType.TYPE_USE(JDK 1.8 新增)
    应用于任何使用类型的语句中
3.4 @Inherited

  用于指明父类注解会被子类继承得到。并不是说注解本身可以继承,而是说如果一个父类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了父类的注解。比较拗口,实际效果可以参考下述测试代码。

  定义应用于父类的注解:

package com.example.demo.help;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ParentAnnotation {
    String value();
}

  定义父类:

package com.example.demo.help;

@ParentAnnotation(value = "value")
public class Parent {
}

  定义子类,测试输出,效果是子类继承了父类的注解:

package com.example.demo.help;

import java.lang.annotation.Annotation;
import java.util.Arrays;

public class Son extends Parent {
    public static void main(String[] args) {
        Annotation[] annotations = Son.class.getAnnotations();
        // [@com.example.demo.help.ParentAnnotation(value=value)]
        System.out.println(Arrays.toString(annotations));
    }
}

3.5 @Repeatable

  Java 8 开始支持,标识某注解可以在同一个声明上使用多次。测试代码如下。

  创建测试注解 TestAnnotation,声明可重复使用:

package com.example.demo.help;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(RepeatableAnnotation.class)
public @interface TestAnnotation {
    String value();
}

  RepeatableAnnotation 注解如下:

package com.example.demo.help;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatableAnnotation {
    TestAnnotation[] value();
}

  创建测试类 TestRepeatable,重复使用注解进行测试:

package com.example.demo.help;

@TestAnnotation(value = "value1")
@TestAnnotation(value = "value2")
public class TestRepeatable {
    public static void main(String[] args) {
        RepeatableAnnotation annotation = TestRepeatable.class.getAnnotation(RepeatableAnnotation.class);
        TestAnnotation[] testAnnotations = annotation.value();
        for (TestAnnotation testAnnotation : testAnnotations) {
            // @com.example.demo.help.TestAnnotation(value=value1)
            // @com.example.demo.help.TestAnnotation(value=value2)
            System.out.println(testAnnotation);
        }
    }
}

四、自定义注解

  Java 还可以让使用者根据自己的需求自定义注解,实现个性化处理,使用示例详见:《SpringBoot 自定义注解 + AOP 实现必填参数非空校验、接口传入参数和应答数据打印、方法耗时统计》。

五、Java 注解有什么用?

  注解是一个辅助类,它在 Junit、Spring 等工具框架中被广泛使用。我们在编程中经常会使用到的注解作用有以下几种。

5.1 编译检查

  注解具有让编译器进行编译检查的作用,例如 @SuppressWarnings、@Deprecated 和 @Override 都具有编译检查作用。若某个方法被 @Override 标注,则意味着该方法会覆盖父类中的同名方法。如果有方法被 @Override 标示,但父类中却没有同名方法,则编译器会报错。

5.2 在反射中使用 Annotation

  在反射的 Class、Method、Field 等函数中,有许多与注解相关的接口。这也意味着,我们可以在反射中解析并使用注解。

5.3 根据注解生成帮助文档

  通过给注解加上 @Documented 标签,能使该注解标签出现在 javadoc 中。

5.4 能够帮忙查看代码

  通过 @Override、@Deprecated 等,我们能很方便的了解程序的大致结构。另外,我们也可以通过自定义注解来实现一些功能。

文章参考:
  • Java-五种元注解详解
  • Java 注解(Annotation)
  • Java注解有哪些-Java元注解有几个

你可能感兴趣的:(Java,java)