注解
- 从 Java 5 开始出现
- Annotaticm 能被用来为程序元素( 类、方法、成员变量等)设置元数据
- 所有注解都隐式继承了 Annotation java.lang.annotation.Annotation 接口
基本 Annotation
在 java.lang 包下
- @Override:限定重写父类方法
- @Deprecated:标示已过时(与文档注释中的 @deprecated 标记的作用基本相同)
- @SuppressWarnings("变量值") :抑制编译器警告(常见的变量值:unused, rawtypes, unchecked, serial, deprecation, all)
- @SafeVarargs: 抑制编译器“堆污染”警告(Java 7 新增)
- @FunctionalInterface: 函数式接口(接口中只有一个抽象方法)(Java 8 新增)
JDK 的元 Annotation
在 java.lang.annotation 包下,用于修饰其它的 Annotation 定义
- @Target :用于指定被修饰的注解能用于修饰哪些程序元素常量值封装在 ElementType 枚举类中:TYPE、FIELD、CONSTRUCTOR、METHOD、LOCAL_VARIABLE、PACKAGE、PARAMETER、ANNOTATION_TYPE
- @Retention:用于指定被修饰的注解可以保留多长时间常量值封装在 RetentionPolicy 枚举类中:SOURCE、CLASS(默认值)、RUNTIME
- @Documented:其修饰的注解会保存到 API 文档中
- @Inherited:其修饰的注解可以被子类所继承
自定义 Annotation
- Annotation 中的属性以无参数的抽象方法的形式来定义
- 属性的类型只能是基本类型、String、Class、annotation、枚举及这些类型一维数组
- 在定义 Annotation 的属性时可以使用 default 为其指定默认值
- 使用带属性的注解时,必须为该注解的所有没有默认值的属性指定值
- 对于 Annotation 中变量名为 value属性,在使用该注解时可以直接在该注解后的括号里指定 value 属性的值,无须使用“value = 变量值”的形式
public @interface MyTag1 {
String name();
int age();
}
// 使用
@MyTag1(name="xx", age=6)
public @interface MyTag2 {
// 使用 default 为两个成员变量指定初始值
String name() default "admin";
int age() default 17;
}
// 使用
@MyTag2
// 获取程序元素上的注解 @MyTag2 的成员变量的值
程序元素.getAnnotation(MyTag2.Class).成员变量名()
通过反射获取程序元素的 Annotation 信息
- 只能提取使用了@Retention(RetentionPolicy.RUNTIME) 修饰的注解
- AnnotatedElement 接口是所有程序元素(Class、Constructor、Field、Method、Package ) 的父接口
- AnnotatedElement 接口中的方法
编译时处理 Annotation
- 注解处理工具 APT(Annotation Processing Tool)
常用注解
在Java中, @Retention和 @Target是两个非常重要的元注解(meta-annotation),它们用于定义其他注解的行为和特性。
下面我将详细解释这两个注解以及它们如何结合使用。
@Target 注解
@Target注解用于指定一个注解可以应用的Java元素类型。它带有一个 value元素,该元素是一个 ElementType枚举类型的数组,用于列出注解可以应用的所有元素类型。
例如,如果你想要创建一个只能应用于方法上的注解,你可以这样定义:
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
public @interface MyMethodAnnotation {
// 注解内容
}
在这个例子中, MyMethodAnnotation只能被应用于方法上。如果你尝试将它应用于其他类型的元素(如类、字段等),编译器将会报错。
@Retention 注解
@Retention注解用于指定注解的保留策略,即注解在何时有效。它带有一个 value元素,该元素是 RetentionPolicy枚举类型的一个实例。 RetentionPolicy有三个可能的值:
- SOURCE:注解只在源代码中存在,编译成字节码文件时会被丢弃。这种注解通常用于标记,对代码的运行没有影响。
- CLASS:注解在源代码和字节码文件中都存在,但在运行时不会被加载到JVM中。这是默认的保留策略。
- RUNTIME:注解在源代码、字节码文件中存在,并且在运行时可以被加载到JVM中,因此可以通过反射机制读取注解的信息。
例如,如果你想要创建一个在运行时仍然有效的注解,你可以这样定义:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRuntimeAnnotation {
// 注解内容
}
结合使用@Target和@Retention
在实际应用中,你通常会结合使用 @Target和 @Retention来定义注解的行为和特性。
例如,你可以创建一个只能应用于方法上并且在运行时仍然有效的注解:
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.RUNTIME)
public @interface MyMethodRuntimeAnnotation {
String value() default "default value";
}
在这个例子中,MyMethodRuntimeAnnotation 注解只能被应用于方法上,并且在运行时可以通过反射机制读取其信息。
你可以这样使用这个注解:
public class MyClass {
@MyMethodRuntimeAnnotation(value = "Test method")
public void myMethod() {
// 方法实现
}
}
然后,在运行时,你可以通过反射获取 myMethod 方法的注解,并读取其 value 元素的值:
import java.lang.reflect.Method;
public class AnnotationTest {
public static void main(String[] args) throws Exception {
Method method = MyClass.class.getMethod("myMethod");
if (method.isAnnotationPresent(MyMethodRuntimeAnnotation.class)) {
MyMethodRuntimeAnnotation annotation = method.getAnnotation(MyMethodRuntimeAnnotation.class);
System.out.println("Annotation value: " + annotation.value());
}
}
}
这段代码将输出:Annotation value: Test method 。这证明了 MyMethodRuntimeAnnotation 注解在运行时是有效的,并且可以通过反射机制读取其信息。