注解其实就是代码里的特殊标记,必须搭配注解的信息处理流程才有意义(使用反射)。
这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。
注解可以修饰:包、类、构造器、成员变量、方法、参数、局部变量 的声明。
这些信息被保存在注解的 “name=value” 对中。

常用的注解

生成文档相关的注解
@author:标明开发该类模块的作者,多个作者之间使用 ,分割
@version:标明该类模块的版本
@see:参考转向,也就是相关主题
@since:从哪个版本开始增加的
@param:对方法中参数的说明
@return:对方法返回值的说明
@exception:对方法可能抛出的异常进行说明

其中 @param @return @exception 这三个注解只能用于方法


在编译时进行格式检查的注解 也是 JDK 内置的三个注解
@Override:只用于重写父类方法
@Deprecated:用于表示所修饰的元素(类,方法、属性)已过时,不建议使用,但是还能调用
@SuppressWarnings:抑制编译器警告

  1. @Test
  2. public void Test() {
  3. // 如果 a 没有被使用,则会有一个警告,但不影响运行
  4. // 如果 a 没有被使用,加上该注解后,警告消失
  5. @SuppressWarnings("unused")
  6. int a = 10;
  7. }

自定义注解

JDK 中的元注解
@Target:用于指定修饰元素的类型(类、构造器、局部变量 等)
@Retention:用于指定注解的生命周期

  • source:编译后,注解将会被丢弃,即 class 文件中无该注解
  • class:编译后,注解将被保留在 class 文件中,但运行 Java 程序时,JVM 不保留注解
  • runtime:编译后,注解将被保留在 class 文件中,并且运行 Java 程序时,JVM 保留注解
  • 如果不写,class 属性是生命周期的默认值

@Document:默认情况,JavaDoc 不包含注解,如果注解被 @Document 修饰,则注解被 JavaDoc 提取成文档
@Inherited:被 @Inherited 修饰的注解具有继承性。默认不具有继承性。
元注解用于修饰其他注解。


注解用 @interface 声明。
注解 默认继承 java.lang.annotation.Annotation 接口。
注解的成员变量,以无参方法的形式声明。
如果成员变量只有一个 value 变量,则使用时,value 可省略不写。
可以用 default 关键字为注解的成员变量指定初始值。

  1. public @interface LoginRequired {
  2. boolean value() default true;
  3. }

Java8 对元注解的增强

可重复注解
可重复注解,即注解可以重复定义多个。
两个注解的 Target、Retention、Inherited 等元注解 必须相同

  1. @MyAnnotation(value="hi")
  2. @MyAnnotation(value="abc")
  3. class Person{
  4. private String name;
  5. private int age;
  6. public Person() {
  7. }
  8. }
  9. @Repeatable(MyAnnotations.class)
  10. @Retention(RetentionPolicy.RUNTIME)
  11. @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
  12. public @interface MyAnnotation {
  13. String value();
  14. }
  15. @Retention(RetentionPolicy.RUNTIME)
  16. @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
  17. public @interface MyAnnotations {
  18. MyAnnotation[] value();
  19. }

在 JDK8 之前,注解只能在声明上使用,
在 JDK8 及之后,注解可以写在任何使用类型的地方。

JDK8 的元注解 @Target 的参数类型 ElementType 枚举值多了两个:TYPE_PARAMETER、TYPE_USE

  1. @Target(value = {
  2. ElementType.TYPE_PARAMETER
  3. })
  4. @Retention(RetentionPolicy.RUNTIME)
  5. @interface Ann7 {
  6. String value() default "";
  7. }
  8. public class UseAnnotation7<@Ann7() T0, @Ann7() T1> {
  9. public <@Ann7() T2> void m1() {
  10. }
  11. public static void main(String[] args) throws NoSuchMethodException {
  12. for (TypeVariable typeVariable : UseAnnotation7.class.getTypeParameters()) {
  13. print(typeVariable);
  14. }
  15. for (TypeVariable typeVariable : UseAnnotation7.class.getDeclaredMethod("m1").getTypeParameters()) {
  16. print(typeVariable);
  17. }
  18. }
  19. private static void print(TypeVariable typeVariable) {
  20. System.out.println("类型变量名称:" + typeVariable.getName());
  21. Arrays.stream(typeVariable.getAnnotations()).forEach(System.out::println);
  22. }
  23. }
  24. 类型变量名称:T0
  25. @com.javacode2018.lesson001.demo18.Ann7(value="")
  26. 类型变量名称:T1
  27. @com.javacode2018.lesson001.demo18.Ann7(value="")
  28. 类型变量名称:T2
  29. @com.javacode2018.lesson001.demo18.Ann7(value="")
  1. public enum ElementType {
  2. /* 类、接口、枚举、注解上面 */
  3. TYPE,
  4. /* 字段上 */
  5. FIELD,
  6. /* 方法上 */
  7. METHOD,
  8. /* 方法的入参上 */
  9. PARAMETER,
  10. /* 构造函数上 */
  11. CONSTRUCTOR,
  12. /* 局部变量上 */
  13. LOCAL_VARIABLE,
  14. /* 注解上 */
  15. ANNOTATION_TYPE,
  16. /* 包上 */
  17. PACKAGE,
  18. /* 类型参数上(类型变量) */
  19. TYPE_PARAMETER,
  20. /* 类型名称上,即任何使用类型的地方 */
  21. TYPE_USE
  22. }

通过反射获取注解信息

反射机制