
注解概述
什么是注解
注解(Annotation)是一种用于为类、方法、字段、参数等添加结构化元数据(Metadata)的机制。它的主要作用不是直接影响代码的逻辑,而是提供信息给编译器、工具或运行时框架,用于辅助处理。
注解的基本特点:
- 不影响代码本身的业务逻辑;
- 以
@开头,例如:@Override; - 可被编译器读取;
- 可在运行时通过反射读取(取决于注解的保留策略)。
注解和注释
| 对比项 | 注解(Annotation) | 注释(Comment) |
|---|---|---|
| 目的 | 向编译器、工具或框架传递"结构化信息" | 给开发者阅读的文本说明 |
| 影响编译或运行 | 可能会影响(如检查、生成代码) | 不影响 |
| 是否被程序读取 | 可通过反射等方式读取 | 运行时不可读取 |
| 语法形式 | @AnnotationName |
//、/* */、/** */ |
| 用途 | 配置、约束、框架驱动 | 文档说明、备注 |
一句话:
注解是机器看的,注释是人看的。
注解的重要性
① 提升代码可读性和可靠性
例如:
@Override会提示编译器检查方法是否真正重写了父类方法。@Deprecated标识一个方法已过时,提示开发者谨慎使用。
② 支持编译器检查与代码生成
如:
- Lombok 使用注解生成 Getter/Setter 等代码;
- MapStruct 使用注解生成对象映射代码;
- JUnit 测试使用
@Test标记测试方法。
③ 是许多框架运作的基础
例如:
- Spring:
@Autowired、@Component、@Configuration等实现依赖注入和配置; - JPA:
@Entity、@Table、@Column等用于 ORM 映射; - Swagger:
@Api、@ApiModelProperty等用于接口文档生成。
这些框架大量使用注解来描述:
- 组件、配置
- 依赖关系
- 映射关系
- 权限控制
- 生命周期行为
可以说:
现代 Java 开发已经离不开注解。注解使框架实现"约定优于配置"成为可能。
④ 方便工具链处理
注解可用于:
- 编译期校验(如
@Nonnull) - 文档生成(如
@DocRoot) - 编译增强(如 APT)
常用基本注解
文档相关注解
java
@author 说明类、接口或方法的作者。
/**
* 工具类示例
* @author Zhang
*/
public class Utils {}
@version 指定版本号。
/**
* @version 1.0.0
*/
@since 说明该元素从哪个版本开始提供。
/**
* @since 1.5
*/
@param 对方法中某参数的说明,如果没有参数就不能写
@return 对方法返回值的说明,如果方法的返回值类型是void就不能写
/**
* 求圆面积的方法
* @param radius double 半径值
* @return double 圆的面积
*/
public static double getArea(double radius) {
return Math.PI * radius * radius;
}
@exception 对方法可能抛出的异常进行说明 ,如果方法没有用throws显式抛出的异常就不能写
@deprecated 表示元素已过时,可结合 @Deprecated 注解一起用。
/**
* @deprecated 请改用 newMethod()
*/
@Deprecated
public void oldMethod() {}
{@link ClassName} 插入一个到指定类、方法的链接。
/**
* 处理对象,详情见 {@link java.util.List}
*/
最基本的 3 个编译时检查注解(JDK 内置)
1、@Override
表示该方法必须覆盖父类的方法。如果方法签名不匹配,编译器会报错。
java
class Parent {
void speak() {}
}
class Child extends Parent {
@Override
void speak() {
System.out.println("Child speaking");
}
}
2、@Deprecated
标记方法或类为过时,不建议使用。使用时 IDE 会给出警告。
java
@Deprecated
public void oldApi() {
System.out.println("old");
}
public void newApi() {
System.out.println("new");
}
3、@SuppressWarnings
抑制编译器警告,如原生类型使用、未使用变量等。
常用参数:
"unchecked":忽略泛型未检查警告"deprecation":忽略访问过时 API 的警告"rawtypes":忽略原生类型使用警告
java
@SuppressWarnings({"unchecked", "deprecation"})
public void test() {
List list = new ArrayList(); // unchecked
oldApi(); // deprecation
}
元注解
元注解(Meta-Annotation) 是指用于修饰 注解本身 的注解。它们用于描述一个注解的行为规则,包括:注解可以用在什么位置、在什么阶段有效、是否会被继承、是否包含在 Javadoc 中等。
1、@Retention ------ 指定注解的生命周期
用于说明该注解在程序运行的哪个阶段可用。
RetentionPolicy 枚举:
| 可选值 | 描述 |
|---|---|
| SOURCE | 只在源代码中存在,编译后丢弃。例如 @Override |
| CLASS(默认) | 编译后保留在 class 文件中,但运行时不可见 |
| RUNTIME | 运行时仍然存在,可通过反射读取 |
java
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}
2、@Target ------ 指定注解可以应用的程序元素位置
限制注解可以用在哪些地方,否则会导致编译错误。
ElementType 可选范围:
- TYPE(类、接口、枚举)
- METHOD
- FIELD
- PARAMETER
- CONSTRUCTOR
- LOCAL_VARIABLE
- ANNOTATION_TYPE
- PACKAGE
- TYPE_PARAMETER(Java 8+)
- TYPE_USE(Java 8+)
java
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface MyAnnotation {}
表示该注解只能用于方法和字段。
3、@Inherited ------ 指定注解是否可被子类继承
若一个注解被 @Inherited 修饰,则当它作用在类上时,子类将自动继承该注解。
注意:
@Inherited 只对类(class)有效,对方法、属性无效。
java
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}
@MyAnnotation
class Parent {}
class Child extends Parent {} // Child 也拥有 MyAnnotation
4、@Documented ------ 指定注解是否包含在 Javadoc 中
让使用了该注解的地方,在生成 Javadoc 时也能看到注解说明。
java
@Documented
public @interface MyAnnotation {}
自定义注解
声明自定义注解
java
[元注解]
[修饰符] @interface 注解名 {
[成员列表]
}
- Java 用 4 个元注解来声明自定义注解的行为;
- 注解的成员在注解定义中以【无参数有返回值的抽象方法】的形式来声明,又称为配置参数。返回值类型只能是基本数据类型、
String类型、Class类型、enum类型、Annotation类型、以上类型的一维数组。 - 可以使用
default关键字为抽象方法指定默认返回值。 - 如果定义的注解含有抽象方法,那么使用时必须指定返回值,除非它有默认值。格式:
方法名 = 返回值,如果只有一个抽象方法需要赋值,且方法名为value,使用时可以省略value=,所以如果注解只有一个抽象方法成员,建议使用方法名value。
使用自定义注解
java
public enum Priority { LOW, MEDIUM, HIGH; }
public @interface SubAnnotation {
String value();
}
java
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAnnotation {
String author() default "anonymous";
int revision() default 1;
String[] tags() default {};
Class<?> targetClass() default Object.class;
// 枚举类型
Priority level() default Priority.MEDIUM;
// 注解类型成员
SubAnnotation info() default @SubAnnotation("default");
}
用在类上
java
@MyAnnotation(
author = "Alice",
revision = 2,
tags = {"core", "annotation"},
targetClass = String.class,
level = Priority.HIGH,
info = @SubAnnotation("sub-info")
)
public class Demo {
}
用在方法上
java
public class Demo {
@MyAnnotation(author = "Bob", revision = 3)
public void test() {}
}
注解的读取
自定义注解必须配上注解的信息处理流程才有意义。
自定义注解只能使用反射的代码读取,所以注解必须使用 @Retention(RetentionPolicy.RUNTIME)。
1、读取类上的注解
java
Class<?> clazz = Demo.class;
if (clazz.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class);
System.out.println(annotation.author());
System.out.println(annotation.revision());
}
2、读取方法上的注解
java
Method method = Demo.class.getMethod("test");
if (method.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation a = method.getAnnotation(MyAnnotation.class);
System.out.println(a.author());
}
3、读取所有注解
java
Annotation[] annotations = clazz.getAnnotations();
for (Annotation a : annotations) {
System.out.println(a);
}