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
目录
相关文章
|
5月前
|
Java Perl
Java进阶之正则表达式
【7月更文挑战第17天】正则表达式(RegEx)是一种模式匹配工具,用于在字符串中执行搜索、替换等操作。它由普通字符和特殊元字符组成,后者定义匹配规则。
38 4
|
5月前
|
设计模式 Java
Java进阶之代理
Java进阶之代理
29 4
|
5月前
|
设计模式 Java
Java进阶之代理
Java进阶之代理
32 3
|
5月前
|
设计模式 Java
Java进阶之代理
【7月更文挑战第16天】Java动态代理通过`java.lang.reflect.Proxy`和`InvocationHandler`实现,无需编译期定义代理类。与静态代理相比,它更灵活,代码更简洁,适用于方法数量变化或未知接口代理。
33 2
|
5月前
|
Java
Java进阶之内部类
【7月更文挑战第13天】Java内部类增进代码组织与封装,允许直接访问外部类成员,包括私有成员。主要有四种类型:成员、静态、局部和匿名内部类。匿名内部类常用于一次性实现接口或扩展类。内部类可隐藏实现细节,减少命名冲突,并在特定上下文中定义辅助类。示例展示了静态和非静态内部类如何在Shape类中封装Circle和Rectangle。使用内部类能提升代码可读性,但可能增加复杂性。
49 6
|
5月前
|
Java 编译器 API
Java进阶之标准注解
【7月更文挑战第15天】Java标准注解包括标记注解(如@Deprecated)、@Override(检查方法重写)、@SuppressWarnings(抑制警告)。多值注解如@RequestMapping在Spring中用于HTTP请求映射。元注解如@Retention控制注解保留策略,@Target指定应用位置。Java8引入类型注解(@FunctionalInterface、@SafeVarargs)和重复注解(@Repeatable)。自定义注解可通过反射读取,如示例中的MyMarkerAnnotation等。
28 2
|
5月前
|
IDE Java 测试技术
Java进阶之反射
【7月更文挑战第14天】Java反射机制允许在运行时动态获取类信息、创建对象及调用其方法。它基于`Class`类,让我们能访问类的属性、方法、构造器。例如,通过`Class.forName()`加载类,`Class.newInstance()`创建对象,`Method.invoke()`执行方法。反射广泛应用于动态代理、单元测试、序列化及框架中,提供灵活性但牺牲了性能,且可破坏封装性。IDE的代码补全也是反射的应用之一。在使用时需谨慎,避免对私有成员的不当访问。
41 1
|
5月前
|
Java 编译器 API
Java进阶之标准注解
Java进阶之标准注解
31 0
|
5月前
|
Java
Java进阶之函数式编程
【7月更文挑战第12天】Java 8 引入函数式编程,重点包括Lambda表达式和函数式接口。Lambda是匿名、简洁的函数,可作为参数传递,简化多参数接口实现。例如:`Runnable run = () -&gt; System.out.println(&quot;Hello, world!&quot;);`。函数式接口只含一个抽象方法,如`Runnable`。Java提供内置函数接口如`Predicate`、`Function`等,便于操作集合。
32 0
|
网络协议 Java API
Java新人必学课程,Java进阶学习全路线(内涵Java超级干货推送及专家社群福利)
自从拉了java社群之后,发现经常有一些新手java开发在吐槽,吐槽java不知道从何学起,感觉路很迷惘。其实小编想说,沉下心,你会发现要走的路并不是很难。为了帮助新人更好的成长,小编特意邀请了最课程的陆敏技老师来给大家进行java基础课程直播分享。
19110 0