自定义注解
注解本质上是一个继承了 java.lang.annotation.Annotation 接口的接口。定义注解和定义接口类似,但要用 @interface 关键字。
基本语法
java
// 定义注解
@Retention(RetentionPolicy.RUNTIME) // 运行时保留
@Target(ElementType.METHOD) // 只用于方法
public @interface MyAnnotation {
// 注解属性(本质上是一个抽象方法)
String value() default ""; // 有默认值,使用时可省略
String name(); // 无默认值,使用时必须指定
int[] numbers() default {}; // 数组属性
}代码示例
基本注解
java
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Todo {
String value() default "";
Priority priority() default Priority.MEDIUM;
}
enum Priority {
LOW, MEDIUM, HIGH
}
class Task {
@Todo(value = "完成用户认证", priority = Priority.HIGH)
public void authenticate() {}
@Todo("添加日志记录") // value 属性可省略名称
public void process() {}
}带多个属性的注解
java
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Entity {
String tableName();
String primaryKey() default "id";
boolean autoGeneratePK() default true;
}
@Entity(tableName = "users", autoGeneratePK = true)
class User {}属性类型限制
注解属性只能是以下类型:
- 基本类型(
int、long、boolean等) StringClass或Class<?>(带泛型)- 枚举类型
- 以上类型的数组
java
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ComplexAnnotation {
String message(); // ✅ String
int count(); // ✅ 基本类型
Class<?> clazz(); // ✅ Class
Priority priority(); // ✅ 枚举
String[] tags(); // ✅ 数组
Class<?>[] dependencies(); // ✅ Class 数组
}注解的属性本质
注解中的属性定义,实质上就是抽象方法:
java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Author {
// 这其实是一个抽象方法
// 返回类型是 String,默认值是空字符串
String value() default "";
}
// 使用时,其实是在给属性赋值
@Author("张三") // 相当于调用 value() 方法,传入 "张三"使用时的省略规则
- 如果只有一个属性叫
value,使用时可省略属性名 - 如果属性是数组类型,且只赋值一个元素,可以省略
{}
java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
String value() default "";
int priority() default 0;
}
// ✅ 省略属性名(只有一个 value)
@Test("测试用例1")
// ✅ 显式指定属性名
@Test(value = "测试用例2", priority = 1)
// ✅ 数组只有一个元素,省略 {}
@Test({"test1"}) // 或者 @Test("test1")注解处理器
定义注解后,可以通过反射读取注解信息:
java
import java.lang.reflect.*;
public class AnnotationProcessor {
public static void main(String[] args) {
// 读取类上的注解
if (User.class.isAnnotationPresent(Entity.class)) {
Entity entity = User.class.getAnnotation(Entity.class);
System.out.println("Table: " + entity.tableName());
System.out.println("PK: " + entity.primaryKey());
}
// 读取方法上的注解
for (Method method : Task.class.getDeclaredMethods()) {
if (method.isAnnotationPresent(Todo.class)) {
Todo todo = method.getAnnotation(Todo.class);
System.out.println("Todo: " + todo.value() +
" (Priority: " + todo.priority() + ")");
}
}
}
}注意事项
- 注解属性不能是对象引用:只能是基本类型、String、Class、枚举及其数组
- 提供默认值:非必需的属性应该提供
default,避免使用时必须指定 - 命名规范:注解属性名通常用名词或动词不定式(如
value、author、since) - 反射前提:
@Retention必须设置为RUNTIME,反射才能读取
