Java进阶之标准注解

简介: Java进阶之标准注解

Java进阶之标准注解
Java内置的注解是JDK中自带的注解,也叫Java标准注解,注解其实是一种标记,用来做一个标识。
比如上面学过的函数式接口,就是通过@FunctionalInterface来标识这个接口是函数式接口,即接口中只有一个抽象方法的接口。
Java中的标准注解(内置注解)主要分为以下几类:
标记注解(Marker Annotations): 这类注解不包含任何元素,也就是说,它们没有参数。它们的存在仅仅是为了标记某个程序元素。例如,@Deprecated 就是一个标记注解,用来标记过时的元素。
@Override:表示当前的方法是重写父类的方法。这个很常见。
// @Override示例
class Parent {
void someMethod() {
// do something
}
}
class Child extends Parent {
@Override // 表示这个方法覆盖了父类的someMethod方法
void someMethod() {
// do something
}
}
@Deprecated:表示某个元素(类、方法等)已经过时,不建议使用。
// @Deprecated示例
class Example {
@Deprecated // 表示这个方法不再推荐使用
void deprecatedMethod() {
// do something
}
}
如果被@Deprecated标识了,那么在调用的时候,方法名上就会显示删除线。
单值注解(Single-Value Annotations): 这类注解包含一个名为 “value” 的元素。在使用时,你可以直接指定该元素的值,而不需要明确写出 “value=”。例如,@SuppressWarnings 通常只设置一个值来指定要抑制的警告类型。
@SuppressWarnings:告诉编译器忽略特定的警告。
// @SuppressWarnings示例
class WarningExample {
@SuppressWarnings("unchecked") // 告诉编译器忽略未检查的转换警告
void ignoreWarnings() {
List list = new ArrayList();
}
}
@SuppressWarnings 注解可以应用于类、方法或者变量声明。它可以接受一个字符串数组作为参数,每个字符串代表一个警告类型。例如:
deprecation:使用了已过时的 API。
unchecked:执行了未经检查的类型转换。
rawtypes:使用了原始类型的泛型。
serial:可序列化的类缺少 serialVersionUID 字段。
finally:finally 子句无法正常完成。
all:抑制所有警告。
也可以同时声明两个类型:@SuppressWarnings("unchecked","deprecation")
警告最好尽可能地解决,而不是简单地使用@SuppressWarnings抑制它们。
多值注解(Multi-Value Annotations): 这类注解包含多个元素,每个元素都可以设置一个值。在使用时,你需要为每个元素指定值。例如,@RequestMapping 在 Spring 框架中用于映射 HTTP 请求到控制器的处理方法,它包含多个元素如 “path”, “method” 等。
元注解(Meta-Annotations): 这类注解用于注解其他注解。元注解定义了其他注解的行为和特性,用于定义和约束其他注解的类型和用法。
元注解用来自定义注解,以指定注解的行为、适用范围、保留策略等。
@Retention:指定被它注解的注解的保留策略,标识这个注解怎么保留。
它有一个RetentionPolicy类型的参数,这个参数有三个可选的策略值:
RetentionPolicy.SOURCE:注解只保留在源代码层面,在编译期间就会被忽略。SOURCE(只在源码中保留)
RetentionPolicy.CLASS:注解会被保留到编译后的字节码文件中,但在运行时环境中不会被虚拟机读取。CLASS(在字节码中保留)
RetentionPolicy.RUNTIME:注解会被保留到运行时环境中,可以通过反射机制读取注解信息。RUNTIME(在运行时保留)
下面是一个使用 @Retention 注解的示例:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

  // 指定 MyAnnotation 注解的保留策略为运行时
  @Retention(RetentionPolicy.RUNTIME)
  public @interface MyAnnotation {
      String value() default "default value";
  }
  在这个示例中,我们定义了一个名为 MyAnnotation 的自定义注解,并使用 @Retention(RetentionPolicy.RUNTIME) 来指定这个注解在运行时是可见的。

  @Target:用于标识指定被它注解的注解可以应用于哪些程序元素,哪些地方,如类型、方法、字段等。
  它有一个ElementType类型的参数,这个参数有多个可选的值,如:
  ElementType.TYPE:类、接口(包括注解类型)或枚举声明。
  ElementType.FIELD:字段声明(包括枚举常量)。
  ElementType.METHOD:方法声明。
  ElementType.PARAMETER:参数声明。
  ElementType.CONSTRUCTOR:构造函数声明。
  ElementType.LOCAL_VARIABLE:局部变量声明。
  ElementType.ANNOTATION_TYPE:注解类型声明。
  ElementType.PACKAGE:包声明。
  ElementType.TYPE_PARAMETER(Java 8+):类型参数声明。
  ElementType.TYPE_USE(Java 8+):类型使用声明。
  示例:
  import java.lang.annotation.ElementType;
  import java.lang.annotation.Target;

  // 指定 MyAnnotation 注解可以应用于方法声明
  @Target(ElementType.METHOD)
  public @interface MyAnnotation {
      String value() default "default value";
  }
  在这个示例中,我们定义了一个名为MyAnnotation的自定义注解,并使用@Target(ElementType.METHOD)来指定这个注解只能应用于方法声明,不能应用于类、字段或其他程序元素,否则会编译错误。
  @Documented注解是一个标记注解,用于指示被它注解的注解应该被包含在 Javadoc 生成的文档中。当一个注解被 @Documented 注解时,如果使用了这个注解的元素被 Javadoc 工具处理,那么这个注解的信息将会出现在生成的文档中。@Documented专门用来修饰其他注解类型(元注解),它不能直接应用于类、方法或字段等程序元素。
  @Inherited:用于指定被它注解的注解将具有继承性。如果一个类使用了被@Inherited注解的注解,那么它的子类也会自动继承这个注解。当一个注解被 @Inherited 注解时,如果某个类使用了这个注解,那么它的子类将自动继承这个注解,除非子类明确地使用了相同的注解但有不同的值。

  Java8中引入了类型注解和重复注解,扩展了 Java 注解的功能。
  类型注解(Type Annotations): 这类注解是Java8引入的,它们可以应用于任何类型的使用,而不仅仅是声明。
  Java 8 引入的类型注解包括:
  @FunctionalInterface:标记一个接口是一个函数式接口,它必须包含一个抽象方法。这个我们前面函数式编程的时候说过,标记是只有一个抽象方法的接口为函数式接口,不标记编译器也会自己判断。
  @SafeVarargs:这个应该是Java7引入的,标记一个方法,表明它对使用可变参数作为泛型类型参数是安全的。就是说我们使用泛型作为可变参数的时候,表明我们已经确保它是不会出现非法类型的,用来消除类型不安全警告。
  示例:
  import java.lang.annotation.ElementType;
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.lang.annotation.Target;

  // 定义一个注解,用于标记一个方法是安全的可变参数泛型方法
  @Target(ElementType.METHOD)
  @Retention(RetentionPolicy.SOURCE)
  public @interface SafeVarargsMethod {
  }

  public class SafeVarargsExample {

      // 定义一个方法,使用可变参数作为泛型类型参数
      @SafeVarargsMethod
      public <T> void safeVarargsMethod(List<T> list, T... args) {
          // 方法实现,假设 args 永远不会包含非 T 类型的元素
          for (T arg : args) {
              list.add(arg);
          }
      }
  }

  也可以自定义类型注解@NonNull,可以用于指示一个变量或参数不应该为null。这种一般三方类库中会提供。
  import java.lang.annotation.ElementType;
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.lang.annotation.Target;

  // 定义一个注解,用于指示一个参数不应该为 null
  @Target(ElementType.PARAMETER)
  @Retention(RetentionPolicy.RUNTIME)
  public @interface NonNull {
  }
  public class NonNullExample {
      // 方法接受一个带有 @NonNull 注解的参数
      public void exampleMethod(@NonNull String parameter) {
          // 方法实现,假设 parameter 永远不为 null
      }
  }
  在 NonNullExample 类的 exampleMethod 方法上,我们使用 @NonNull 注解来标记参数 parameter。这意味着 parameter 在调用这个方法时不应该为 null。
  通过使用@NonNull,我们可以强制方法参数在运行时不为null,从而提高代码的健壮性和可维护性。如果尝试传递一个null值给标记了@NonNull的参数,编译器将会报错,或者在运行时抛出异常。



  重复注解(Repeatable Annotations): 这类注解是Java8引入的,它们允许在同一位置多次使用相同的注解。为了实现这一点,需要定义一个容器注解来保存重复注解的数组。例如,@Repeatable 元注解用于指示一个注解是可以重复的。
  @Repeatable:Java8引入,允许一个注解在同一声明或类型上多次使用。
  在Java8之前,如果需要在同一个元素上使用多个相同的注解,需要使用注解数组或创建多个不同的注解。java8引入了@Repeatable可以在添加多个相同的注解时,每个注解都有不同的配置或参数。
  它允许在同一声明或类型上多次使用同一个注解。为了使一个注解可重复,需要定义一个容器注解,用于保存重复注解的数组。
  下面是一个使用 @Repeatable 注解的示例:
  import java.lang.annotation.Repeatable;
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;

  // 定义一个可重复的注解
  @Repeatable(MyRepeatableAnnotations.class)
  @Retention(RetentionPolicy.RUNTIME)
  public @interface MyRepeatableAnnotation {
      String value();
  }
  // 定义一个容器注解,用于保存 MyRepeatableAnnotation 的数组
  @Retention(RetentionPolicy.RUNTIME)
  public @interface MyRepeatableAnnotations {
      MyRepeatableAnnotation[] value();
  }
  // 使用 @RepeatableAnnotation 的示例
  public class RepeatableExample {
      // 在同一个方法上多次使用 @MyRepeatableAnnotation
      @MyRepeatableAnnotation("First annotation")
      @MyRepeatableAnnotation("Second annotation")
      public void myMethod() {
          // 方法实现
      }
  }
  在这个示例中,我们定义了一个名为 MyRepeatableAnnotation 的可重复注解,并使用 @Repeatable 来指定它的容器注解为 MyRepeatableAnnotations。我们还定义了 MyRepeatableAnnotations 容器注解,它包含一个 MyRepeatableAnnotation 类型的数组。
  然后,在 RepeatableExample 类的 myMethod 方法上,我们多次使用了 @MyRepeatableAnnotation 注解,每个注解都有不同的值。

  自定义一个注解示例:
  import java.lang.annotation.ElementType;
  import java.lang.annotation.Repeatable;
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.lang.annotation.Target;
  import java.lang.reflect.Method;

  public class AnnotationExample {
      // 定义一个标记注解
      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.METHOD)
      public @interface MyMarkerAnnotation {
      }
      // 定义一个单值注解
      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.METHOD)
      public @interface MySingleValueAnnotation {
          String value();
      }
      // 定义一个多值注解
      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.METHOD)
      public @interface MyMultiValueAnnotation {
          String name();
          int age();
      }
      // 定义一个可重复的注解
      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.METHOD)
      @Repeatable(MyRepeatableAnnotations.class)
      public @interface MyRepeatableAnnotation {
          String value();
      }
      // 定义一个容器注解来保存可重复注解的数组
      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.METHOD)
      public @interface MyRepeatableAnnotations {
          MyRepeatableAnnotation[] value();
      }

      // 使用自定义注解的方法
      @MyMarkerAnnotation
      @MySingleValueAnnotation("Single value")
      @MyMultiValueAnnotation(name = "Multi", age = 25)
      @MyRepeatableAnnotation("First repeatable")
      @MyRepeatableAnnotation("Second repeatable")
      public void annotatedMethod() {
          // 方法实现
      }
      public static void main(String[] args) {
          try {
              // 获取 AnnotationExample 类的 Class 对象
              Class<?> clazz = AnnotationExample.class;
              // 获取名为 "annotatedMethod" 的方法
              Method method = clazz.getMethod("annotatedMethod");
              // 获取并打印标记注解
              MyMarkerAnnotation markerAnnotation = method.getAnnotation(MyMarkerAnnotation.class);
              System.out.println("Marker Annotation: " + markerAnnotation);
              // 获取并打印单值注解
              MySingleValueAnnotation singleValueAnnotation = method.getAnnotation(MySingleValueAnnotation.class);
              System.out.println("Single Value Annotation: " + singleValueAnnotation.value());
              // 获取并打印多值注解
              MyMultiValueAnnotation multiValueAnnotation = method.getAnnotation(MyMultiValueAnnotation.class);
              System.out.println("Multi Value Annotation - Name: " + multiValueAnnotation.name());
              System.out.println("Multi Value Annotation - Age: " + multiValueAnnotation.age());
              // 获取并打印可重复注解
              MyRepeatableAnnotation[] repeatableAnnotations = method.getAnnotationsByType(MyRepeatableAnnotation.class);
              System.out.println("Repeatable Annotations:");
              for (MyRepeatableAnnotation repeatableAnnotation : repeatableAnnotations) {
                  System.out.println(repeatableAnnotation.value());
              }
          } catch (NoSuchMethodException e) {
              e.printStackTrace();
          }
      }
  }
  在这个示例中,我们定义了四个注解:一个标记注解、一个单值注解、一个多值注解和一个可重复的注解。然后,我们在 annotatedMethod 方法上使用了这些注解。在 main 方法中,我们使用反射来获取 annotatedMethod 方法的注解信息,并打印出来。
  END
目录
相关文章
|
2月前
|
Java Maven 编译器
Java编译器注解运行和自动生成代码问题之@AutoService工作问题如何解决
Java编译器注解运行和自动生成代码问题之@AutoService工作问题如何解决
|
2月前
|
Java API 编译器
Java编译器注解运行和自动生成代码问题之编译时通过参数设置选项值问题如何解决
Java编译器注解运行和自动生成代码问题之编译时通过参数设置选项值问题如何解决
|
3天前
|
Arthas Java 测试技术
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
Java字节码文件、组成、详解、分析;常用工具,jclasslib插件、阿里arthas工具;如何定位线上问题;Java注解
Java字节码文件、组成,jclasslib插件、阿里arthas工具,Java注解
|
8天前
|
存储 JSON 前端开发
【Java】用@JsonFormat(pattern = “yyyy-MM-dd“)注解,出生日期竟然年轻了一天
在实际项目中,使用 `@JsonFormat(pattern = &quot;yyyy-MM-dd&quot;)` 注解导致出生日期少了一天的问题,根源在于夏令时的影响。本文详细解析了夏令时的概念、`@JsonFormat` 注解的使用方法,并提供了三种解决方案:在注解中添加 `timezone = GMT+8`、修改 JVM 参数 `-Duser.timezone=GMT+08`,以及使用 `timezone = Asia/Shanghai
9 0
【Java】用@JsonFormat(pattern = “yyyy-MM-dd“)注解,出生日期竟然年轻了一天
|
17天前
|
Java
Java系列之 IDEA 为类 和 方法设置注解模板
这篇文章介绍了如何在IntelliJ IDEA中为类和方法设置注解模板,包括类模板的创建和应用,以及两种不同的方法注解模板的创建过程和实际效果展示,旨在提高代码的可读性和维护性。
|
17天前
|
存储 缓存 Java
Java本地高性能缓存实践问题之使用@CachePut注解来更新缓存中数据的问题如何解决
Java本地高性能缓存实践问题之使用@CachePut注解来更新缓存中数据的问题如何解决
|
17天前
|
Java 编译器 开发者
【Java 第八篇章】注解
从JDK5起,Java引入注解作为元数据支持,区别于注释,注解可在编译、类加载和运行时被读取处理。注解允许开发者在不影响代码逻辑的前提下嵌入补充信息。核心概念包括`Annotation`接口、`@Target`定义适用范围如方法、字段等,`@Retention`设定生命周期,如仅存在于源码或运行时可用。Java提供了内置注解如`@Override`用于检查方法重写、`@Deprecated`标记废弃元素、`@SuppressWarnings`抑制警告。自定义注解可用于复杂场景,例如通过反射实现字段验证。
13 0
|
2月前
|
Java Perl
Java进阶之正则表达式
【7月更文挑战第17天】正则表达式(RegEx)是一种模式匹配工具,用于在字符串中执行搜索、替换等操作。它由普通字符和特殊元字符组成,后者定义匹配规则。
20 4
|
2月前
|
设计模式 Java
Java进阶之代理
Java进阶之代理
21 4
|
2月前
|
编译器 Java
Java编译器注解运行和自动生成代码问题之重写init方法的问题如何解决
Java编译器注解运行和自动生成代码问题之重写init方法的问题如何解决
下一篇
DDNS