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