JAVA之注解

注解(Annotation)详细介绍

注解 是 Java 编程语言中的一种元数据机制,用于在源码中添加附加信息。注解可以用于类、方法、字段、参数等位置,为编译器、工具或运行时环境提供额外的指示。注解不会直接影响代码的执行逻辑,但可以在编译时、类加载时或运行时被处理,提供一些额外的功能或约束。

注解的作用

  1. 编译器使用的注解
    • @Override:用于表示一个方法是从父类重写过来的。编译器会检查这个方法是否真的覆写了父类的方法。如果没有,编译器将会报错。
    • @SuppressWarnings:告诉编译器在指定的代码部分忽略某些警告。例如,@SuppressWarnings("unchecked") 可以用来忽略类型安全的警告。
    • 这些注解在编译器处理后不会被保留到 .class 文件中,它们仅用于编译时的检查。
  2. 工具处理的注解
    • 一些工具在处理 .class 文件时可能会使用注解,例如字节码修改工具、代码生成工具等。这类注解会被编译到 .class 文件中,但在程序运行时通常不会被访问。
    • 示例:某些框架(如 Hibernate、Spring)使用注解来配置类的行为,工具会在加载 .class 文件时读取这些注解来生成所需的配置。
  3. 运行时读取的注解
    • 这些注解在程序运行时可以被反射机制读取。例如,使用 @PostConstruct 注解的方法会在对象创建后自动被调用。这类注解会被保留在 .class 文件中,并可以在运行时通过反射获取。
    • 常见的运行时注解:@Entity@Transactional@Autowired

注解的定义

注解的定义语法如下:

1
2
3
4
5
6
7
8
9
10
11
12
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME) // 注解在运行时可见
@Target(ElementType.FIELD) // 注解可以用于字段
public @interface Check {
int min() default 0;
int max() default 100;
int value() default 0;
}
  • @Retention:指定注解的保留策略。
    • RetentionPolicy.SOURCE:注解只保留在源码中,编译后不保留。
    • RetentionPolicy.CLASS:注解保留在 .class 文件中,但运行时不可见。
    • RetentionPolicy.RUNTIME:注解保留在 .class 文件中,且在运行时可见。
  • @Target:指定注解的应用位置。例如:
    • ElementType.FIELD:注解可以用于字段。
    • ElementType.METHOD:注解可以用于方法。

注解的配置参数

注解的参数可以是以下类型:

  • 基本类型:intfloatboolean 等。
  • String 类型。
  • 枚举类型。
  • Class 类型。
  • 上述类型的数组。

注解的参数可以有默认值,未指定参数时将使用默认值。常用的参数名为 value,当注解只有一个参数时,可以省略参数名:

1
2
3
4
5
public @interface Check {
int min() default 0;
int max() default 100;
int value() default 0;
}

注解的使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Hello {
@Check(min = 0, max = 100, value = 55)
public int n;

@Check(value = 99)
public int p;

@Check(99) // 等同于 @Check(value = 99)
public int x;

@Check // 所有参数都使用默认值
public int y;
}
  • @Check(min = 0, max = 100, value = 55):为字段 n 定义了 minmaxvalue 参数。
  • @Check(value = 99):仅定义了 value 参数,等同于 @Check(99)
  • @Check(99):省略了 value 参数名,等同于 @Check(value = 99)
  • @Check:使用默认值,没有显式指定参数。
  • 注解 是一种为代码提供元数据的机制,不直接影响代码逻辑。
  • 注解可用于编译时检查、工具处理、运行时读取等多种场景。
  • 可以定义注解,并为其设置参数,参数可以有默认值。
  • 注解的配置参数类型有限制,必须是常量。

定义注解

在Java中,定义注解(Annotation)是一种创建可用于代码的元数据的方式。注解本身不会直接影响程序的运行,但它们可以被编译器、开发工具或运行时环境读取和处理。

1. 定义注解

使用@interface关键字定义注解。注解定义的格式如下:

1
2
public @interface Report {
}
  • @interface关键字用于定义注解类型。
  • 注解定义通常是空的,除非需要添加配置参数。

2. 添加参数和默认值

在注解中,可以定义参数和默认值。注解的参数类似于方法参数,可以使用default关键字设置默认值。推荐使用value作为最常用的参数名。示例如下:

1
2
3
4
5
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}
  • 参数定义遵循普通方法的语法规则,但它们是常量,并且必须有默认值。

3. 使用元注解

元注解(Meta-Annotations)是用于修饰其他注解的注解。Java标准库提供了几个常用的元注解:

@Target

定义注解可以应用于代码中的哪些元素(如类、方法、字段等)。例如:

1
2
3
4
5
6
@Target(ElementType.METHOD)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}
  • ElementType指定了注解的目标位置,如TYPE(类或接口)、FIELD(字段)、METHOD(方法)等。

这段代码定义了一个名为 Report 的自定义注解(Annotation)。

1. 注解定义

1
2
3
4
5
6
@Target(ElementType.METHOD)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}

@Target(ElementType.METHOD)

  • @Target 是一个元注解,用于指定注解可以应用的目标元素类型。
  • ElementType.METHOD 表示该注解只能用于方法上。也就是说,@Report 只能应用于方法,而不能应用于字段、类等其他元素上。

public @interface Report

  • public:这是注解的访问修饰符,意味着它可以在任何地方被使用。
  • @interface:用于定义一个注解类型。@interface 是Java语言中定义注解的关键字。

int type() default 0;

  • 这是一个名为 type 的注解参数。
  • int 表示该参数的类型是整数。
  • default 0 表示如果在使用 @Report 注解时没有提供 type 参数的值,则默认值为 0

String level() default "info";

  • 这是一个名为 level 的注解参数。
  • String 表示该参数的类型是字符串。
  • default "info" 表示如果在使用 @Report 注解时没有提供 level 参数的值,则默认值为 "info"

String value() default "";

  • 这是一个名为 value 的注解参数。
  • String 表示该参数的类型是字符串。
  • default "" 表示如果在使用 @Report 注解时没有提供 value 参数的值,则默认值为空字符串 ""
  • 在许多注解中,value 是一个常用的默认参数名。如果注解只有一个参数,通常可以省略参数名直接指定值。

总结

这段代码定义了一个名为 Report 的注解,该注解可以用于方法上。它包含三个参数:

  1. type:一个整数类型的参数,默认值为 0
  2. level:一个字符串类型的参数,默认值为 "info"
  3. value:一个字符串类型的参数,默认值为空字符串 ""

你可以在代码中使用这个注解来标记方法,并通过这些参数提供额外的信息或配置。比如:

1
2
3
4
@Report(type = 1, level = "debug", value = "Initialization method")
public void init() {
// method implementation
}

在这个例子中,init 方法被标记为 @Report 注解,并且为 typelevelvalue 参数提供了具体的值。这些值可以在运行时通过反射读取,以实现动态处理或配置。

@Retention

定义注解的生命周期,即注解的存活时间。它有三个值:

  • RetentionPolicy.SOURCE:注解仅在源代码中存在,编译后丢弃。
  • RetentionPolicy.CLASS:注解保留在.class文件中,但在运行时不可用。
  • RetentionPolicy.RUNTIME:注解保留在运行时,可以通过反射读取。

例如:

1
2
3
4
5
6
@Retention(RetentionPolicy.RUNTIME)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}
  • RetentionPolicy.RUNTIME 允许在程序运行时通过反射读取注解。

@Repeatable

定义一个注解是否可以重复使用。通常,注解只能在一个目标上出现一次,但使用@Repeatable可以允许注解重复。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
@Repeatable(Reports.class)
@Target(ElementType.TYPE)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}

@Target(ElementType.TYPE)
public @interface Reports {
Report[] value();
}
  • @Repeatable 允许在一个目标上应用多个相同的注解。

这段代码定义了一个可以重复使用的注解 @Report 和一个用于容纳多个 @Report 注解的注解 @Reports

1. @Repeatable 注解

1
2
3
4
5
6
7
@Repeatable(Reports.class)
@Target(ElementType.TYPE)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}
  • @Repeatable(Reports.class):这是一个元注解,用于标记 @Report 注解可以被重复使用。@Repeatable 指定了一个容器注解类 Reports,该类用于存放多个 @Report 注解实例。
  • @Target(ElementType.TYPE):表示 @Report 注解可以应用于类型(类、接口等)上。
  • @interface Report:定义了一个注解类型 Report
  • 注解参数:typelevelvalue,分别有默认值 0"info"""

2. @Reports 注解

1
2
3
4
@Target(ElementType.TYPE)
public @interface Reports {
Report[] value();
}
  • @Target(ElementType.TYPE):表示 @Reports 注解可以应用于类型(类、接口等)上。
  • @interface Reports:定义了一个容器注解类型 Reports
  • Report[] value()@Reports 注解包含一个 Report 数组,存储多个 @Report 注解实例。

总结

  • @Report 是一个可重复使用的注解,可以应用于类、接口等类型上。它可以存储三个参数:typelevelvalue
  • @Reports 是一个容器注解,用于存放多个 @Report 注解实例。它包含一个 Report 数组 value

通过使用 @Repeatable@Reports,你可以在一个类上使用多个 @Report 注解实例,而不需要为每个注解定义一个新的容器。

@Inherited

定义子类是否可以继承父类的注解。@Inherited 仅适用于类(ElementType.TYPE),并且仅针对类的继承有效。

1
2
3
4
5
6
7
@Inherited
@Target(ElementType.TYPE)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}
  • @Inherited 确保子类可以继承父类的注解。

代码解释

1
2
3
4
5
6
7
@Inherited
@Target(ElementType.TYPE)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}

1. @Inherited

  • 定义: @Inherited 是一个元注解,用于指示注解是否可以被子类继承。
  • 作用: 当一个注解类(例如 @Report)被 @Inherited 修饰时,子类会自动继承该注解。
  • 适用范围: @Inherited 仅适用于类(即 @Target(ElementType.TYPE))的继承关系,而不适用于接口或方法等其他元素的继承。

2. @Target(ElementType.TYPE)

  • 定义: @Target 是一个元注解,用于指定注解可以应用于代码的哪些部分。
  • ElementType.TYPE: 表示 @Report 注解可以应用于类和接口(即类型上)。

3. public @interface Report

  • @interface: 这是 Java 中定义注解的关键字。
  • Report: 这是自定义的注解类型名。它可以被应用于类、接口等类型上。

4. 注解参数

  • int type() default 0: type 是一个参数,类型为 int,默认值为 0
  • String level() default "info": level 是一个参数,类型为 String,默认值为 "info"
  • String value() default "": value 是一个参数,类型为 String,默认值为空字符串 ""

使用示例

假设我们有一个父类使用了 @Report 注解:

1
2
3
@Report(type = 1, level = "debug")
public class Parent {
}

如果我们定义一个子类 Child 继承自 Parent

1
2
public class Child extends Parent {
}

由于 Parent 类使用了 @Inherited 注解,Child 类会自动继承 @Report 注解。即使 Child 类没有显式地声明 @Report 注解,它也会继承 Parent 类的 @Report 注解,Child 类的行为等同于 Parent 类的 @Report 注解。

总结

  • @Inherited 使得注解可以被子类继承。
  • @Target(ElementType.TYPE) 表明注解可以应用于类和接口。
  • 注解 Report 包含三个参数,typelevelvalue,它们都有默认值。

这个机制对于需要在类继承层次结构中共享元数据(如配置、标记等)非常有用。

定义注解的步骤包括:

  1. 使用 @interface 关键字定义注解。
  2. 添加参数并设置默认值,常用参数名为 value
  3. 使用元注解配置注解的行为,包括目标位置 (@Target)、生命周期 (@Retention)、是否可重复 (@Repeatable)、是否可继承 (@Inherited)。

大多数自定义注解通常需要配置 @Target@Retention,其中 @Retention(RetentionPolicy.RUNTIME) 是最常用的设置。根据实际需求,可以选择使用其他元注解来增强注解的功能。

处理注解

Java注解(Annotation)本身不直接影响代码逻辑。其主要作用是提供元数据,如何使用这些元数据完全由程序或工具决定。根据 @Retention 的配置,注解的生命周期分为三种类型:

  1. SOURCE: 注解在编译时丢弃,不会保留在 .class 文件中。
  2. CLASS: 注解保留在 .class 文件中,但不会被加载到 JVM 中。
  3. RUNTIME: 注解在运行时保留,并可通过反射 API 读取。

使用反射 API 读取 RUNTIME 注解

  1. 判断注解是否存在

    1
    2
    // 判断Person类是否有@Report注解
    boolean hasReport = Person.class.isAnnotationPresent(Report.class);

    解释

    1. Person.class:
      • 这表示 Person 类的 Class 对象。在 Java 中,Class 对象用于获取有关类的信息,例如其字段、方法和注解。
    2. isAnnotationPresent(Class<Annotation> annotationClass):
      • 这是 Class 类中的一个方法,用于检查指定的注解是否存在于该类上。
      • 参数 annotationClass 是要检查的注解的 Class 对象,例如 Report.class
    3. Report.class:
      • 这是 Report 注解的 Class 对象。Report.class 表示 Report 注解类型的 Class 对象,用于在 isAnnotationPresent 方法中传递。
    4. 返回值 boolean:
      • 这个方法返回一个布尔值,指示 Person 类上是否存在 @Report 注解。
      • 如果 Person 类上存在 @Report 注解,isAnnotationPresent 返回 true;如果不存在,返回 false

    这段代码的目的是检查 Person 类上是否应用了 @Report 注解。它使用 Class.isAnnotationPresent 方法来确定注解是否存在,并将结果存储在 hasReport 变量中。这样可以在程序中动态决定是否处理类的特定注解。

  2. 读取注解

    1
    2
    3
    4
    // 获取Person类上的@Report注解
    Report report = Person.class.getAnnotation(Report.class);
    int type = report.type();
    String level = report.level();

    这段代码用于获取 Person 类上的 @Report 注解,并从中提取注解的值。

    解释

    1. Person.class.getAnnotation(Report.class):
      • Person.classPerson 类的 Class 对象。这表示 Person 类的元数据,可以用来检索关于 Person 类的各种信息。
      • getAnnotation(Class<T> annotationClass)Class 类中的一个方法,用于获取指定的注解类型。如果该注解存在于该类上,方法将返回该注解的实例;否则,返回 null
      • Report.classReport 注解的 Class 对象。它被传递给 getAnnotation 方法以指定要查找的注解类型。
    2. Report report:
      • 这行代码从 Person 类中获取 @Report 注解的实例。如果 Person 类上存在 @Report 注解,getAnnotation(Report.class) 将返回该注解实例,并将其赋值给 report 变量。
      • 如果 Person 类上没有 @Report 注解,report 将为 null
    3. int type = report.type():
      • report 对象中调用 type() 方法,获取注解 @Reporttype 属性值。
      • type() 方法是 @Report 注解中定义的一个方法,用于获取 type 参数的值。它返回一个 int 类型的值,这个值是在定义 @Report 注解时指定的。
    4. String level = report.level():
      • report 对象中调用 level() 方法,获取注解 @Reportlevel 属性值。
      • level() 方法是 @Report 注解中定义的一个方法,用于获取 level 参数的值。它返回一个 String 类型的值,这个值是在定义 @Report 注解时指定的。

    示例

    假设 @Report 注解定义如下:

    1
    2
    3
    4
    5
    6
    7
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
    }

    并且 Person 类上有如下注解:

    1
    2
    3
    @Report(type = 1, level = "debug")
    public class Person {
    }

    执行上述代码后:

    • Report report = Person.class.getAnnotation(Report.class); 将获得 @Report 注解的实例。
    • int type = report.type(); 将返回 1,因为 type 被设置为 1
    • String level = report.level(); 将返回 "debug",因为 level 被设置为 "debug"
  3. 方法、字段和构造方法

    • 方法

      1
      2
      Method method = Person.class.getMethod("someMethod");
      Report report = method.getAnnotation(Report.class);

      这段代码用于获取 Person 类中的 someMethod 方法上的 @Report 注解,并将其存储在 report 变量中。

      1
      2
      Method method = Person.class.getMethod("someMethod");
      Report report = method.getAnnotation(Report.class);

      解释

      1. Method method = Person.class.getMethod("someMethod");:
        • Person.classPerson 类的 Class 对象,表示该类的元数据。
        • getMethod(String name, Class<?>... parameterTypes)Class 类中的一个方法,用于获取 Class 对象中指定名称和参数类型的方法的 Method 对象。
        • "someMethod" 是要获取的方法的名称。
        • getMethod("someMethod") 会返回 Person 类中名为 someMethodMethod 对象。如果该方法存在于 Person 类中,它将返回一个 Method 实例;如果方法不存在或方法参数不匹配,将抛出 NoSuchMethodException
      2. Report report = method.getAnnotation(Report.class);:
        • method.getAnnotation(Class<T> annotationClass)Method 类中的一个方法,用于获取指定的注解类型。如果该方法上存在指定的注解,方法将返回该注解的实例;否则,返回 null
        • Report.classReport 注解的 Class 对象。它被传递给 getAnnotation 方法以指定要查找的注解类型。
      3. Report report:
        • 这行代码将 method.getAnnotation(Report.class) 的返回值赋给 report 变量。如果 someMethod 方法上存在 @Report 注解,report 将是 @Report 注解的实例;如果没有 @Report 注解,report 将为 null

      示例

      假设 @Report 注解定义如下:

      1
      2
      3
      4
      5
      6
      7
      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.METHOD)
      public @interface Report {
      int type() default 0;
      String level() default "info";
      String value() default "";
      }

      并且 Person 类中有一个方法 someMethod,如下所示:

      1
      2
      3
      4
      5
      public class Person {
      @Report(type = 1, level = "debug")
      public void someMethod() {
      }
      }

      执行上述代码后:

      • Method method = Person.class.getMethod("someMethod"); 将返回 Person 类中 someMethod 方法的 Method 对象。
      • Report report = method.getAnnotation(Report.class); 将获取 someMethod 方法上的 @Report 注解实例。
      如果 someMethod 方法上的 @Report 注解存在,report 将包含注解的属性值,例如:
      • report.type() 将返回 1
      • report.level() 将返回 "debug"

      如果 someMethod 方法上没有 @Report 注解,则 report 将为 null

      这段代码用于检查并获取 Person 类中指定方法(someMethod)上的注解 @Report。如果该方法上存在 @Report 注解,通过 method.getAnnotation(Report.class) 方法可以访问该注解的属性值。

    • 字段

      1
      2
      Field field = Person.class.getField("someField");
      Report report = field.getAnnotation(Report.class);

      这段代码用于获取 Person 类中的 someField 字段上的 @Report 注解,并将其存储在 report 变量中。

      1
      2
      Field field = Person.class.getField("someField");
      Report report = field.getAnnotation(Report.class);

      解释

      1. Field field = Person.class.getField("someField");:
        • Person.classPerson 类的 Class 对象,表示该类的元数据。
        • getField(String name)Class 类中的一个方法,用于获取 Class 对象中指定名称的 Field 对象。它返回 Class 对象中名为 name 的字段的 Field 对象。
        • "someField" 是要获取的字段的名称。
        • getField("someField") 会返回 Person 类中名为 someFieldField 对象。如果该字段存在于 Person 类中,它将返回一个 Field 实例;如果字段不存在或名称不匹配,将抛出 NoSuchFieldException
      2. Report report = field.getAnnotation(Report.class);:
        • field.getAnnotation(Class<T> annotationClass)Field 类中的一个方法,用于获取指定的注解类型。如果该字段上存在指定的注解,方法将返回该注解的实例;否则,返回 null
        • Report.classReport 注解的 Class 对象。它被传递给 getAnnotation 方法以指定要查找的注解类型。
      3. Report report:
        • 这行代码将 field.getAnnotation(Report.class) 的返回值赋给 report 变量。如果 someField 字段上存在 @Report 注解,report 将是 @Report 注解的实例;如果没有 @Report 注解,report 将为 null

      示例

      假设 @Report 注解定义如下:

      1
      2
      3
      4
      5
      6
      7
      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.FIELD)
      public @interface Report {
      int type() default 0;
      String level() default "info";
      String value() default "";
      }

      并且 Person 类中有一个字段 someField,如下所示:

      1
      2
      3
      4
      public class Person {
      @Report(type = 1, level = "debug")
      public String someField;
      }

      执行上述代码后:

      • Field field = Person.class.getField("someField"); 将返回 Person 类中 someField 字段的 Field 对象。
      • Report report = field.getAnnotation(Report.class); 将获取 someField 字段上的 @Report 注解实例。
      如果 someField 字段上的 @Report 注解存在,report 将包含注解的属性值,例如:
      • report.type() 将返回 1
      • report.level() 将返回 "debug"

      如果 someField 字段上没有 @Report 注解,则 report 将为 null

      这段代码用于检查并获取 Person 类中指定字段(someField)上的注解 @Report。如果该字段上存在 @Report 注解,通过 field.getAnnotation(Report.class) 方法可以访问该注解的属性值。

    • 构造方法

      1
      2
      Constructor<?> constructor = Person.class.getConstructor();
      Report report = constructor.getAnnotation(Report.class);

      这段代码用于获取 Person 类中无参数构造函数上的 @Report 注解,并将其存储在 report 变量中。

      1
      2
      Constructor<?> constructor = Person.class.getConstructor();
      Report report = constructor.getAnnotation(Report.class);

      解释

      1. Constructor<?> constructor = Person.class.getConstructor();:
        • Person.classPerson 类的 Class 对象,表示该类的元数据。
        • getConstructor(Class<?>... parameterTypes)Class 类中的一个方法,用于获取该类中指定参数类型的构造函数。此方法返回一个 Constructor 对象。
        • 当没有传递参数类型时,getConstructor() 方法将返回无参数构造函数的 Constructor 对象。如果 Person 类中存在一个无参数构造函数,返回的将是该构造函数的 Constructor 实例;如果没有无参数构造函数,将抛出 NoSuchMethodException
        • Constructor<?> 是一个泛型类的实例,表示构造函数的元数据。
      2. Report report = constructor.getAnnotation(Report.class);:
        • getAnnotation(Class<T> annotationClass)Constructor 类中的一个方法,用于获取构造函数上指定类型的注解。如果构造函数上存在该注解,方法将返回注解实例;否则,返回 null
        • Report.classReport 注解的 Class 对象。它作为参数传递给 getAnnotation 方法以指定要查找的注解类型。
      3. Report report:
        • 这行代码将 constructor.getAnnotation(Report.class) 的返回值赋给 report 变量。如果 Person 类中的无参数构造函数上存在 @Report 注解,report 将是 @Report 注解的实例;如果构造函数上没有 @Report 注解,report 将为 null

      示例

      假设 @Report 注解定义如下:

      1
      2
      3
      4
      5
      6
      7
      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.CONSTRUCTOR)
      public @interface Report {
      int type() default 0;
      String level() default "info";
      String value() default "";
      }

      并且 Person 类中定义了一个无参数构造函数,并且该构造函数上有 @Report 注解:

      1
      2
      3
      4
      5
      6
      public class Person {
      @Report(type = 1, level = "debug")
      public Person() {
      // 构造函数逻辑
      }
      }

      执行上述代码后:

      • Constructor<?> constructor = Person.class.getConstructor(); 将返回 Person 类中无参数构造函数的 Constructor 对象。
      • Report report = constructor.getAnnotation(Report.class); 将获取无参数构造函数上的 @Report 注解实例。
      如果构造函数上的 @Report 注解存在,report 将包含注解的属性值,例如:
      • report.type() 将返回 1
      • report.level() 将返回 "debug"

      如果构造函数上没有 @Report 注解,则 report 将为 null

      这段代码用于获取 Person 类中无参数构造函数上的注解 @Report。如果该构造函数上存在 @Report 注解,通过 constructor.getAnnotation(Report.class) 方法可以访问该注解的属性值。如果注解不存在,则 reportnull

  4. 方法参数的注解

    1
    2
    3
    4
    5
    6
    7
    8
    Annotation[][] paramAnnotations = method.getParameterAnnotations();
    Annotation[] param1Annotations = paramAnnotations[0];
    for (Annotation anno : param1Annotations) {
    if (anno instanceof Range) {
    Range range = (Range) anno;
    int max = range.max();
    }
    }

    这段代码用于获取方法参数上的所有注解,并检查这些注解的具体类型,然后提取某些注解的属性值。

    1
    2
    3
    4
    5
    6
    7
    8
    Annotation[][] paramAnnotations = method.getParameterAnnotations();
    Annotation[] param1Annotations = paramAnnotations[0];
    for (Annotation anno : param1Annotations) {
    if (anno instanceof Range) {
    Range range = (Range) anno;
    int max = range.max();
    }
    }

    详细解释

    1. Annotation[][] paramAnnotations = method.getParameterAnnotations();:
      • method.getParameterAnnotations()Method 类中的一个方法,用于获取方法参数上的所有注解。返回值是一个二维数组 Annotation[][]
      • 每个一维数组 Annotation[] 对应方法的一个参数,包含了该参数上的所有注解。二维数组的索引与参数的索引一致,例如 paramAnnotations[0] 是第一个参数上的所有注解。
    2. Annotation[] param1Annotations = paramAnnotations[0];:
      • paramAnnotations 数组中获取第一个参数(索引为 0)的所有注解。param1Annotations 是一个 Annotation[] 数组,包含了第一个参数上的所有注解。
      • 如果方法有多个参数,paramAnnotations 数组的长度与方法参数的个数相同,每个 Annotation[] 数组包含了对应参数上的注解。
    3. for (Annotation anno : param1Annotations):
      • 遍历 param1Annotations 数组中的每个注解 anno
      • 对于 param1Annotations 数组中的每个元素,执行 for 循环中的代码块。
    4. if (anno instanceof Range):
      • 检查当前注解 anno 是否是 Range 注解的实例。
      • Range 是一个自定义的注解(假设它是定义的注解,例如:@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER))。
    5. Range range = (Range) anno;:
      • 如果 annoRange 的实例,则将其强制转换为 Range 类型,以便访问 Range 注解定义的方法。
    6. int max = range.max();:
      • 获取 Range 注解的 max 属性值。
      • maxRange 注解定义的一个属性,表示该注解的某种最大值限制。

    示例说明

    假设我们有一个方法 someMethod,其参数上使用了 @Range 注解:

    1
    2
    3
    public void someMethod(@Range(max = 10) int value) {
    // 方法逻辑
    }

    然后,我们通过反射来获取该方法参数上的注解信息:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // 获取Method实例
    Method method = Person.class.getMethod("someMethod", int.class);

    // 获取所有参数的注解
    Annotation[][] paramAnnotations = method.getParameterAnnotations();

    // 获取第一个参数(索引为0)的所有注解
    Annotation[] param1Annotations = paramAnnotations[0];

    // 遍历第一个参数的所有注解
    for (Annotation anno : param1Annotations) {
    // 检查注解是否为@Range
    if (anno instanceof Range) {
    // 强制转换为@Range注解
    Range range = (Range) anno;
    // 获取@Range注解的max属性值
    int max = range.max();
    System.out.println("Max value: " + max);
    }
    }

    在这个例子中,method.getParameterAnnotations() 返回了一个二维数组,包含了 someMethod 方法所有参数的注解信息。paramAnnotations[0] 提取了第一个参数(value)的注解数组。我们检查这些注解是否是 Range 类型,若是,则读取 Range 注解的 max 属性值。

    这段代码主要用于读取和处理方法参数上的注解。首先,通过 method.getParameterAnnotations() 方法获取参数的注解信息,然后通过遍历检查每个注解的类型,获取注解的属性值。这样做允许程序在运行时动态地处理和检查方法参数上的注解。

使用注解的实例

注解定义:

1
2
3
4
5
6
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Range {
int min() default 0;
int max() default 255;
}

在类中使用注解:

1
2
3
4
5
6
public class Person {
@Range(min = 1, max = 20)
public String name;
@Range(max = 10)
public String city;
}

检查方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
void check(Person person) throws IllegalArgumentException, ReflectiveOperationException {
for (Field field : person.getClass().getFields()) {
Range range = field.getAnnotation(Range.class);
if (range != null) {
Object value = field.get(person);
if (value instanceof String s) {
if (s.length() < range.min() || s.length() > range.max()) {
throw new IllegalArgumentException("Invalid field: " + field.getName());
}
}
}
}
}

通过注解和检查方法,我们可以在运行时验证 Person 实例的字段值是否满足注解定义的约束。