@Retention是Java中决定注解生命周期的核心元注解,取值为RetentionPolicy.SOURCE(仅源码)、CLASS(默认,存于字节码但反射不可见)或RUNTIME(运行时可通过反射获取),选错策略将导致注解在目标阶段不可见。

@Retention 是 Java 中决定注解“能活多久”的核心元注解。它不控制注解能写在哪(那是 @Target 的事),而是明确告诉编译器和 JVM:这个注解的信息,该保留到哪个阶段——只在写代码时有用?编译完就丢?还是必须带到运行时供反射调用?选错策略,注解可能写了却完全不起作用。
三种保留策略各司其职
它接收 RetentionPolicy 枚举值,只有三个选项,不能自定义:
-
SOURCE:只存在于 .java 源文件里,javac 编译完成立刻丢弃,.class 文件里完全找不到。适合纯编译期用途,比如
@Override、@SuppressWarnings——编译器用完即焚,IDE 提示也依赖它。 - CLASS:默认策略。注解会写进 .class 字节码,但 JVM 加载类时不会把它加载进内存,反射 API 查不到。适合字节码处理工具(如 ASM、Javassist)在类加载前做分析,对运行时逻辑无感。
-
RUNTIME:注解完整保留在 .class 中,并随类一起被 JVM 加载进运行时方法区。这是唯一能让
Class.getAnnotation()、Method.getAnnotation()等反射方法成功取到注解的策略。Spring 的@Component、JUnit 的@Test全靠它撑腰。
为什么 RUNTIME 不是默认值
保留注解不是零成本,有实际开销:
- 每个 RUNTIME 注解都会增加 .class 文件体积(哪怕几字节);
- JVM 加载类时要解析并驻留更多元数据,拖慢类加载速度,多占一点内存;
- 运行时暴露注解内容,可能泄露设计细节或校验逻辑,带来潜在安全或封装风险。
所以 Java 默认用更轻量的 CLASS,把“要不要活到运行时”的决定权交给开发者——按需开启,不盲目堆砌。
立即学习“Java免费学习笔记(深入)”;
怎么选?关键看“谁需要读它”
不用猜,直接问自己一个问题:哪个环节需要用到这个注解的信息?
- 如果是给 IDE 或 javac 插件用(比如提示错误、自动补全),选 SOURCE;
- 如果是给构建时的字节码增强工具用(比如修改方法逻辑、加日志),选 CLASS;
- 如果是给运行中的程序逻辑用(比如根据注解动态调用方法、注入对象、校验参数),必须选 RUNTIME。
选高了(比如该用 SOURCE 却用了 RUNTIME)不会报错,但徒增体积和风险;选低了(比如该用 RUNTIME 却用了 CLASS),反射就读不到,功能直接失效。
常见踩坑与验证方式
新手最容易犯两个错误:
- 写了注解却用反射拿不到 → 检查是否漏了
@Retention(RetentionPolicy.RUNTIME); - 以为加了 RUNTIME 就能在编译期起作用 → 错,编译期检查(如生成警告、报错)必须配合注解处理器(Annotation Processor),和 Retention 无关。
快速验证方式:用 javap -v YourClass.class 查看字节码,搜索 RuntimeVisibleAnnotations(对应 RUNTIME)或 RuntimeInvisibleAnnotations(对应 CLASS)——SOURCE 注解根本不出现在 class 中。


















