@retention决定注解保留策略:source(仅源码)、class(默认,class文件中但运行时不可见)、runtime(运行时可通过反射获取);非默认因有体积、性能与安全成本。

@Retention 是 Java 中最核心的元注解之一,它直接决定一个自定义注解在何时“消失”——即注解信息保留到哪个阶段:源码期、编译期还是运行期。理解它的作用,是写出可被反射读取、能参与编译检查或仅作文档说明的注解的前提。
Retention 的三种保留策略
它接收一个 RetentionPolicy 枚举值,共三种选项:
-
RetentionPolicy.SOURCE:注解只保留在源代码中,编译时即被丢弃。比如
@Override和@SuppressWarnings就属于这一类——编译器用完就扔,class 文件里找不到它们。 - RetentionPolicy.CLASS:注解保留在 class 文件中,但 JVM 加载类时不会将其加载进内存。这是默认行为(若没写 @Retention,默认就是 CLASS)。适用于某些字节码处理工具(如 ASM、Javassist)在加载前分析注解,但对运行时反射不可见。
-
RetentionPolicy.RUNTIME:注解完整保留在 class 文件中,并由 JVM 加载进运行时内存。只有设为 RUNTIME,才能通过
Class.getAnnotation()或Method.getAnnotation()等反射 API 获取。Spring 的@Autowired、JUnit 的@Test都依赖这个策略。
为什么 RUNTIME 不是默认值?
因为保留注解是有成本的:
- 增加 class 文件体积(哪怕只是几个字节);
- 运行时加载更多元数据,略微影响类加载速度和内存占用;
- 暴露内部设计意图,可能带来安全或封装风险(例如无意中暴露校验规则细节)。
所以 Java 设计者选择更轻量的 CLASS 作为默认,把是否“活到运行期”的决定权交给开发者——按需开启,不滥用。
常见误用与验证方法
新手常犯两个错误:
- 写了注解却用反射拿不到 → 检查是否漏了
@Retention(RetentionPolicy.RUNTIME); - 以为加了 RUNTIME 就能在编译期起作用 → 错,编译期检查(如生成警告、报错)必须配合 注解处理器(Annotation Processor),和 Retention 无关(那是
@Target和javax.annotation.processing的事)。
快速验证方式:用 javap -v YourClass.class 查看字节码,搜索 “RuntimeVisibleAnnotations” 或 “RuntimeInvisibleAnnotations” —— 前者对应 RUNTIME,后者对应 CLASS(SOURCE 注解根本不出现在 class 中)。
搭配 @Target 使用才完整
@Retention 控制“存在多久”,@Target 控制“能标在哪”。两者常一起出现:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface MyLog {
String value() default "";
}
缺一不可:没有 @Target,注解可以乱标(编译器不拦);没有 @Retention(RUNTIME),就算标对了位置,运行时也读不到。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










