java 注解(annotations)是 java 5 引入的一种元数据(metadata),它提供了一种在源代码中嵌入补充信息的方式,这些信息可以被编译器、jvm 或其他工具在编译时、运行时进行处理。
注解本身不会直接影响程序的执行,但可以用来指导编译器、jvm 或其他工具的行为,从而实现各种功能。
一、 注解的定义 (annotation definition)
什么是元数据 (metadata)?
- 元数据是描述数据的数据。 在 java 中,元数据是指描述程序代码的数据,例如类、方法、字段等的属性信息。
- 注解就是一种元数据,它可以用来描述类、方法、字段、参数、局部变量等程序元素。
注解的语法
@retention(retentionpolicy.runtime) // 元注解,指定注解的保留策略 @target({elementtype.method, elementtype.type}) // 元注解,指定注解可以应用的程序元素类型 public @interface myannotation { // 定义一个注解 // 注解成员 (annotation members) string name() default "default name"; // 默认值 int value(); // 必须赋值 class<?> type() default void.class; //类型 }
@interface
关键字: 用于定义一个注解。- 注解名 (annotation name): 遵循 pascalcase 命名规范(每个单词首字母大写),例如
override
,deprecated
,webservlet
。 - 注解成员 (annotation members):
- 类似于接口中的方法声明,但没有方法体。
- 可以有默认值 (使用
default
关键字指定)。 - 如果没有默认值,则在使用注解时必须显式赋值。
- 成员的类型可以是原始类型、
string
、class
、枚举类型、注解类型或这些类型的数组。 - 注解成员实际上类似于接口中的方法,这意味着它们可以有访问修饰符,但通常省略 (默认是
public
)。 - 注解成员后面要跟一对小括号,即使没有参数。
二、元注解 (meta-annotations)
元注解是用于修饰注解的注解,用于指定注解的特性。
1.@retention
:
作用: 指定注解的保留策略 (retention policy),即注解在哪个阶段可用。
retentionpolicy
枚举:
retentionpolicy.source
: 注解仅保留在源代码中,在编译时被丢弃。 例如,@override
注解。retentionpolicy.class
: 注解保留在 class 文件中,但在运行时会被 jvm 丢弃。retentionpolicy.runtime
: 注解保留在 class 文件中,并且在运行时可以通过反射 api 获取。 这是最常用的保留策略,可以实现各种动态功能。
示例:
@retention(retentionpolicy.runtime) // 指定注解在运行时可用 public @interface myannotation { // ... }
2.@target
:
作用: 指定注解可以应用的程序元素类型 (target element)。
elementtype
枚举:
elementtype.type
: 可以应用于类、接口、枚举、注解。elementtype.field
: 可以应用于字段。elementtype.method
: 可以应用于方法。elementtype.parameter
: 可以应用于方法参数。elementtype.constructor
: 可以应用于构造方法。elementtype.local_variable
: 可以应用于局部变量。elementtype.annotation_type
: 可以应用于注解。elementtype.package
: 可以应用于包。elementtype.type_parameter
: 类型参数,since 1.8elementtype.type_use
: 使用类型的任何地方, since 1.8
示例:
@target({elementtype.method, elementtype.type}) // 指定注解可以应用于方法和类 public @interface myannotation { // ... }
1.@documented
:
作用: 指定该注解应该包含在 javadoc 文档中。
示例:
@documented // 指定该注解应该包含在 javadoc 文档中 public @interface myannotation { // ... }
2.@inherited
:
作用: 指定该注解可以被子类继承。
示例:
@inherited // 指定该注解可以被子类继承 public @interface myannotation { // ... }
3.@repeatable
(java 8+):
作用: 指定该注解可以在同一个程序元素上重复使用。
示例:
@repeatable(authorities.class) public @interface authority { string role(); } public @interface authorities { authority[] value(); } @authority(role="admin") @authority(role="editor") public class myclass { // ... }
三、内置注解 (built-in annotations)
java 提供了一些内置注解,可以直接使用:
1.@override
:
作用: 用于标记一个方法是重写 (override) 了父类的方法。
用途:
- 帮助编译器检查是否正确地重写了方法。 如果方法签名与父类方法不匹配,编译器会报错。
- 提高代码可读性,明确表明该方法是重写了父类的方法。
示例:
class animal { void eat() { system.out.println("animal is eating."); } } class dog extends animal { @override // 标记该方法重写了父类的 eat() 方法 void eat() { system.out.println("dog is eating."); } }
2.@deprecated
:
作用: 用于标记一个类、方法或字段已过时 (deprecated),不建议使用。
用途:
- 提醒开发者该类、方法或字段已经不推荐使用,可能会在将来的版本中被移除。
- 可以提供替代方案的说明。
示例:
@deprecated // 标记该方法已过时 public void oldmethod() { // ... } /** * @deprecated use {@link #newmethod()} instead. */ @deprecated public void oldmethodwithcomment(){ //... } public void newmethod() { // ... }
3.@suppresswarnings
:
作用: 用于抑制编译器发出的警告。
用途:
- 在某些情况下,编译器可能会发出一些不必要的警告,可以使用
@suppresswarnings
注解来抑制这些警告。
取值:
all
: 抑制所有警告。boxing
: 抑制与自动装箱/拆箱相关的警告。deprecation
: 抑制与使用了@deprecated
标记的类、方法或字段相关的警告。unchecked
: 抑制与未检查的类型转换相关的警告(例如,在使用原始类型时)。unused
: 抑制与未使用的变量相关的警告。resource
: 抑制与资源未关闭相关的警告。- 等等 (具体取决于编译器)。
示例:
@suppresswarnings("unchecked") // 抑制类型转换警告 list list = new arraylist(); list.add("hello"); string s = (string) list.get(0); // 编译器会发出 unchecked 类型转换警告
四、自定义注解 (custom annotations)
除了内置注解,你还可以根据自己的需求创建自定义注解。
定义注解:
- 使用
@interface
关键字定义注解。 - 指定元注解 (
@retention
,@target
等)。 - 定义注解成员。
使用注解:
- 在程序元素(类、方法、字段等)上使用自定义注解,并为注解成员赋值(如果需要)。
处理注解 (annotation processing):
- 编译时处理: 可以使用注解处理器 (annotation processor) 在编译时处理注解,生成代码或配置文件。 例如,lombok 使用注解处理器自动生成 getter/setter 方法。
- 运行时处理: 可以使用反射 api 在运行时获取注解信息,并根据注解信息执行相应的逻辑。
五、使用场景 (annotation usage scenarios)
注解在 java 中有着广泛的应用,主要包括以下场景:
编译时检查:
- 使用
@override
注解进行方法重写检查。 - 使用
@suppresswarnings
注解抑制编译器警告。
代码生成:
- lombok 使用注解处理器自动生成 getter/setter 方法、构造方法、
equals()
/hashcode()
方法等,简化代码编写。 - jpa 使用注解来映射类和数据库表,生成 sql 语句。
- spring mvc 使用注解来映射请求 url 和处理方法。
配置:
- spring 使用注解来配置 bean、依赖注入、事务管理、aop 等。
- jax-rs 使用注解来定义 restful web 服务。
运行时行为修改:
- 单元测试框架 (例如 junit, testng) 使用注解来标记测试方法。
- 安全框架 (例如 spring security) 使用注解来进行权限控制。
- 序列化框架 (例如 jackson, gson) 使用注解来控制序列化和反序列化过程。
六、代码示例 (自定义注解)
import java.lang.annotation.elementtype; import java.lang.annotation.retention; import java.lang.annotation.retentionpolicy; import java.lang.annotation.target; import java.lang.reflect.method; @retention(retentionpolicy.runtime) // 指定注解在运行时可用 @target(elementtype.method) // 指定注解可以应用于方法 public @interface myannotation { // 定义一个自定义注解 string name() default "default name"; // 成员变量,默认值default name int value(); // 成员变量 } class myclass { @myannotation(name = "my method", value = 100) // 使用注解并赋值 public void mymethod() { system.out.println("my method is called."); } } public class annotationexample { public static void main(string[] args) throws exception { class<?> clazz = myclass.class; method method = clazz.getmethod("mymethod"); if (method.isannotationpresent(myannotation.class)) { // 判断方法是否使用了 myannotation 注解 myannotation annotation = method.getannotation(myannotation.class); // 获取注解对象 system.out.println("annotation name: " + annotation.name()); // 获取 name 成员的值 system.out.println("annotation value: " + annotation.value()); // 获取 value 成员的值 } } }
总结
java 注解是一种强大的元数据机制,它可以在代码中嵌入补充信息,并在编译时或运行时被处理。 理解注解的定义、元注解、内置注解、自定义注解以及使用场景,可以帮助你编写更简洁、更灵活、更易于维护的 java 代码。 掌握注解,能让你更深入地理解各种框架和库的运作方式,并能够自定义 spring boot starter等功能.
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论