深入理解Java注解Annotation:从基础到实战
深入理解Java注解:从基础到实战
引言
Java注解(Annotation)是JDK 1.5引入的一个强大特性,它允许开发者在代码中添加元数据(metadata),这些元数据可以在编译时、类加载时或运行时被读取和处理。注解不仅简化了代码的配置和维护,还为框架和工具提供了丰富的扩展点。本文将详细介绍Java注解的基本概念、自定义注解的创建、注解的使用以及注解的解析,并通过一个综合案例来展示注解的实际应用。
1. 什么是注解?
注解(Annotation)是一种代码级别的说明,它可以在包、类、字段、方法、局部变量、方法参数等元素的前面声明,用来对这些元素进行说明。注解本质上是一个接口,所有注解都会继承java.lang.annotation.Annotation接口。
注解的作用主要包括:
- 编译检查:例如
@Override注解用于检查方法是否重写了父类的方法。 - 代码分析:通过注解对代码进行分析,例如框架的配置。
- 生成帮助文档:例如
@author和@version注解用于生成文档时标记作者和版本信息。
2. 自定义注解
2.1 注解的定义格式
自定义注解使用@interface关键字来定义,格式如下:
public @interface 注解名 {
// 属性定义
数据类型 属性名();
数据类型 属性名() default 默认值;
}
例如,定义一个简单的注解Book:
public @interface Book {
String name();
double price() default 100.0;
String[] author();
}
2.2 注解的属性
注解的属性可以是以下类型:
- 八种基本数据类型(int, short, long, double, byte, char, boolean, float)
- String, Class, 注解类型, 枚举类
- 以上类型的一维数组形式
3. 注解的使用
3.1 注解的使用格式
注解的使用格式如下:
@注解名(属性=值, 属性=值)
例如,使用上面定义的Book注解:
@Book(name = "Java编程思想", author = {"Bruce Eckel"})
public class BookStore {
@Book(name = "Effective Java", price = 50.0, author = {"Joshua Bloch"})
public void buy() {
System.out.println("购书.....");
}
}
3.2 特殊属性value
如果注解中只有一个属性,并且属性名为value,则在使用时可以省略value属性名:
@interface A {
String value();
}
@A("值") // 当自定义注解中仅有一个value属性时,可以省略value属性名
public void test() {
}
3.3 注解使用的注意事项
- 使用注解:
@注解名(属性名=属性值,属性名=属性值...) - 如果注解中只有一个属性,并且属性名是value,那么给
value属性赋值的时候可以不书写value属性名:@MyAnno("abc") - 如果注解中只有一个属性,并且属性名不是value,那么给该属性赋值,必须书写属性名:
@MyAnno(name="abc") - 如果注解中的属性没有默认值,那么使用的时候必须给属性赋值。
- 如果注解中的属性有默认值,那么使用的时候可以给属性赋值,覆盖默认值,也可以不赋值。
- 如果属性是数组,那么赋值的时候:
属性名={属性值,属性值...} - 如果属性是数组,并且赋值的时候只给一个值,则可以省略大括号:
属性名=属性值 - 如果注解中含有多个属性,并且没有默认值,那么给
value赋值的时候不能省略value:@MyAnno(value="abc", name="def") - 如果注解中含有多个属性,并且有默认值,那么给
value单独赋值可以省略value属性名:@MyAnno("张三") - 同一个注解不能同时修饰一个方法或者类。
注解的使用规则可能会让人感到有些复杂,尤其是需要记住各种特殊情况和简写规则时。不过,理解这些规则可以帮助你更高效地使用注解,并且在阅读和维护代码时更容易理解其他开发者的意图。
如果你觉得这些规则过于复杂,或者你更倾向于保持代码的简洁性和可读性,那么完全可以按照最规范的方式来使用注解,即:
-
明确指定属性名:无论注解中有多少属性,都明确指定属性名。例如:
@MyAnno(value = "abc", name = "def") -
避免使用简写:即使注解中只有一个属性,也明确指定属性名。例如:
@MyAnno(value = "abc") -
始终提供所有属性的值:即使属性有默认值,也明确提供属性的值,以确保代码的可读性和一致性。
这样做的好处是:
- 代码更易读:明确指定属性名和值,使得代码更易于理解和维护。
- 减少错误:避免因为简写规则而导致的潜在错误或误解。
- 一致性:保持代码风格的一致性,使得团队成员更容易理解和遵循。
当然,如果你在某些情况下确实需要使用简写规则来提高代码的简洁性,那么理解这些规则也是有帮助的。但在大多数情况下,保持代码的清晰和规范性往往比追求简洁更重要。
4. 元注解
元注解是用来约束自定义注解的使用范围和生命周期的注解。常用的元注解有@Target和@Retention。
通俗的来说 元注解就是修饰注解的注解 还有一个词叫元数据 就是修饰数据的数据 类似的还有 元宇宙
举个栗子
IDEA中Ctrl+N 找一下override


元数据

4.1 @Target
@Target用于指定注解的使用位置,可选的参数值在ElementType枚举类中定义:
TYPE:用在类、接口上FIELD:用在成员变量上METHOD:用在方法上PARAMETER:用在参数上CONSTRUCTOR:用在构造方法上LOCAL_VARIABLE:用在局部变量上
例如:
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface MyAnnotation {
}
4.2 @Retention
@Retention用于指定注解的生命周期,可选的参数值在RetentionPolicy枚举类中定义:
SOURCE:注解只存在于源代码中,编译后的字节码文件中不存在CLASS:注解存在于源代码和字节码文件中,运行时内存中不存在(默认值)RUNTIME:注解存在于源代码、字节码文件和运行时内存中,可以通过反射获取
例如:
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
5. 注解解析
注解解析是通过反射技术来获取注解中的数据。AnnotatedElement接口定义了解析注解的方法,Class、Method、Field等类都实现了该接口。
5.1 获取注解数据的原理
boolean isAnnotationPresent(Class<Annotation> annotationClass);
T getAnnotation(Class<T> annotationClass);
例如,解析Book注解:
public class TestBookStore {
public static void main(String[] args) throws NoSuchMethodException {
Class<BookStore> bookStoreClass = BookStore.class;
Method method = bookStoreClass.getMethod("buy");
if (method.isAnnotationPresent(Book.class)) {
Book bookAnno = method.getAnnotation(Book.class);
System.out.println("书名:" + bookAnno.name());
System.out.println("价格:" + bookAnno.price());
System.out.println("作者:" + Arrays.toString(bookAnno.author()));
}
}
}
6. 综合案例:模拟Junit测试
6.1 需求
模拟Junit测试的@Test注解,实现一个自定义注解@MyTest,并在目标类中使用该注解标记测试方法,最后通过反射调用所有带有@MyTest注解的方法。
6.2 实现
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 MyTest {
}
public class TestAnnotationParse {
@MyTest
public void method1() {
System.out.println("我是方法1");
}
@MyTest
public void method3() {
System.out.println("我是方法3");
}
public void method2() {
System.out.println("我是方法2");
}
}
public class Test {
public static void main(String[] args) throws Exception {
Class<TestAnnotationParse> testClass = TestAnnotationParse.class;
Method[] methods = testClass.getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(MyTest.class)) {
method.invoke(testClass.newInstance());
}
}
}
}
7. 总结
Java注解是一个强大的工具,它允许开发者在代码中添加元数据,并通过反射技术在运行时获取这些元数据。通过自定义注解,开发者可以实现许多高级功能,如编译检查、代码分析和生成文档。本文详细介绍了注解的基本概念、自定义注解的创建、注解的使用以及注解的解析,并通过一个综合案例展示了注解的实际应用。希望本文能帮助读者更好地理解和使用Java注解。
浙公网安备 33010602011771号