概念
注解是类的组成部分,可以为类携带额外的信息,提供一种安全的注释标记机制,用于将任何信息或元数据与程序元素(如类、方法、成员变量等)关联。
- 注解是供编译器或JVM使用的,编译器或JVM可以根据注解执行相应的功能。
- 注解类似于修饰符,适用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。
- 父类中的注解不能被子类继承。
注解的作用
- 标记:为类、方法或变量添加元数据,便于工具或框架识别。
- 框架支持:许多框架利用注解和反射来实现功能,这是框架的底层基础技术。
- 编译时检查:如 @Override 用于方法重写约束,@FunctionalInterface 用于函数式接口约束。
注解格式
定义格式:自定义注解用 @interface 关键字,注解默认可以标记很多地方
修饰符 @interface 注解名{
// 注解属性
}
使用注解的格式:@注解名
// 自定义注解
@interface MyAnnotation {
String value();
}
// 使用注解
@MyAnnotation(value = "示例注解")
public class Example {
@Override
public String toString() {
return "这是一个示例类";
}
}
常用注解
@Override:检查方法是否正确重写父类方法。
@Deprecated:标记某个方法或类为过时,不建议使用。
@SuppressWarnings:抑制特定的编译器警告。
@FunctionalInterface:指示接口为函数式接口。
注解属性
普通属性
注解可以包含属性,属性名必须带 ()。在使用注解时,属性必须赋值,除非属性定义了默认值。
属性的格式:
- 格式 1:数据类型 属性名()
- 格式 2:数据类型 属性名() default 默认值
适用的数据类型:
- 八种基本数据类型:int、short、long、double、byte、char、boolean、float
- String
- Class
- 以上类型的数组形式也被支持
特殊属性
注解中有一个特殊属性名称:value
- 如果注解只有一个 value 属性,可以在使用时省略 value 名称。
- 如果有多个属性且没有默认值,则 value 属性不能省略。
示例
// 自定义注解
@interface MyAnnotation {
String name(); // 必须提供
int age() default 18; // 默认值为18
String[] tags() default {}; // 默认值为空数组
String value(); // 特殊属性
}
// 使用注解
@MyAnnotation(name = "示例", value = "测试")
public class Example {
// ...
}
// 省略 value 属性
@MyAnnotation(name = "示例", value = "测试")
public class AnotherExample {
// ...
}
在以上示例中,MyAnnotation 定义了多个属性,包括一个特殊属性 value。使用时可以选择性地省略 value。
元注解概述
元注解是 Java 中用于注解自定义注解的注解,主要由 Sun 公司提供。它们可以控制自定义注解的应用范围和行为。元注解包括以下四种:
@Target
用于指定自定义注解可以应用于哪些 Java 元素。其默认值为任何元素。可用值定义在 ElementType 类中,主要包括:
- ElementType.CONSTRUCTOR:用于描述构造器。
- ElementType.FIELD:用于描述成员变量、对象、属性(包括 enum 实例)。
- ElementType.LOCAL_VARIABLE:用于描述局部变量。
- ElementType.METHOD:用于描述方法。
- ElementType.PACKAGE:用于描述包。
- ElementType.PARAMETER:用于描述方法参数。
- ElementType.TYPE:用于描述类、接口(包括注解类型)或 enum 声明。
@Retention
定义注解的生命周期,声明注解的作用范围。可用值定义在 RetentionPolicy 枚举类中:
- RetentionPolicy.SOURCE:在编译阶段丢弃,这些注解在编译结束后不再存在于字节码中,仅用于源码阶段,如 @Override 和 @SuppressWarnings。
- RetentionPolicy.CLASS:在类加载时丢弃,字节码文件中存在,但在运行时不存在。这是默认值。
- RetentionPolicy.RUNTIME:在运行期保留该注解,因此可以使用反射机制读取注解信息。自定义注解通常使用这种方式。
@Inherited
- 表示修饰的自定义注解可以被子类继承。仅适用于类类型的注解。
@Documented
- 指示该注解是否应包含在 Java 文档中。使用此注解标记的自定义注解在生成的文档中会包含其元信息。
示例
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyCustomAnnotation {
String value() default "default";
}
注解解析概述
在 Java 开发中,注解解析是理解和利用注解的关键。通过反射机制,可以动态地获取类、方法、字段等元素上的注解信息。以下是与注解解析相关的核心接口和类的概述。
相关接口与类
- Annotation:所有注解的父类,所有自定义注解都继承自这个接口。
- AnnotatedElement:定义了与注解解析相关的方法,提供了一系列获取注解的功能。
- Class、Method、Field、Constructor:这些类实现了 AnnotatedElement 接口,因此可以通过它们获取注解信息。
Class 类 API
- Annotation[] getDeclaredAnnotations():获取当前对象上声明的所有注解,返回一个注解数组。
- T getDeclaredAnnotation(Class<T> annotationClass):根据注解类型获取对应的注解对象,仅返回当前对象上声明的注解。
- T getAnnotation(Class<T> annotationClass):获取当前对象及其父类上声明的指定类型的注解。
- boolean isAnnotationPresent(Class<Annotation> annotationClass):判断当前对象是否使用了指定类型的注解。
- boolean isAnnotation():判断当前 Class 对象是否表示注解类型。
注解的原理
注解本质上是特殊接口,继承了 Annotation 接口。在运行时,Java 为每个注解生成一个动态代理类。当通过反射获取注解时,返回的是动态代理对象(例如 $Proxy1),通过这个代理对象调用注解的方法时,实际上会回调 AnnotationInvocationHandler 的 invoke 方法。
这个方法从 memberValues 这个 Map 中获取注解属性的值,而 memberValues 的数据来源于 Java 常量池。
注解数据解析原理
注解的解析通常遵循以下步骤:
- 确定注解的作用对象(类、方法等)。
- 获取对应的 Class 对象。
- 使用上述 API 方法从 Class 对象中获取注解信息。
示例
package com.example;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
String value();
}
@MyAnnotation(value = "Example Class")
class ExampleClass {
}
public class AnnotationParser {
public static void main(String[] args) {
Class<ExampleClass> exampleClass = ExampleClass.class;
// 检查是否存在注解
if (exampleClass.isAnnotationPresent(MyAnnotation.class)) {
// 获取注解
MyAnnotation annotation = exampleClass.getAnnotation(MyAnnotation.class);
System.out.println("注释值: " + annotation.value());
}
}
}