登录
首页 >  文章 >  java教程

@Retention注解存留状态反编译解析

时间:2026-05-23 13:43:57 209浏览 收藏

本文深入解析了Java中@Retention注解的存留机制,揭示其声明的保留策略(SOURCE、CLASS、RUNTIME)如何真实体现在字节码与运行时行为中:SOURCE级注解编译即消失,CLASS级仅存于class文件但反射不可见,而只有RUNTIME级才既在javap反编译结果中可见,又可通过反射实际获取;文章强调必须绕过IDE和图形化反编译器的“美化误导”,坚持用javap -v查看原始字节码属性,并结合反射代码实测,才能准确验证注解是否真正按预期阶段保留——这是Java元编程实践中极易被忽视却至关重要的底层真相。

@Retention 的作用是声明注解在哪个阶段保留(源码期、编译期、运行期),但它本身不会直接出现在字节码里。要确认一个注解是否真的按 @Retention 声明的策略被保留在字节码中,不能只看源码或 IDE 提示,得通过反编译工具观察实际生成的 class 文件内容。

用 javap 查看注解保留状态

Java 自带的 javap 是最轻量、最可靠的方式。它能显示类文件中实际存在的注解信息,包括是否被写入 RuntimeVisibleAnnotationsRuntimeInvisibleAnnotations 属性:

  • @Retention(RetentionPolicy.SOURCE):编译后完全消失,javap -v 不会显示该注解
  • @Retention(RetentionPolicy.CLASS):保留在 class 文件中,但 JVM 加载时不加载,javap -v 可见,但 getDeclaredAnnotations() 获取不到
  • @Retention(RetentionPolicy.RUNTIME):保留在 class 文件中,且运行时可通过反射访问,javap -v 可见,并且 getDeclaredAnnotations() 能返回

关键操作步骤

以一个简单注解为例:

//@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {}

编译后执行:

javap -v MyClass.class | grep -A 5 "RuntimeVisibleAnnotations"

如果输出中出现 RuntimeVisibleAnnotations 且包含 MyAnno,说明注解已按 RUNTIME 策略写入字节码;若用 CLASS 策略,也会出现,但属性名是 RuntimeInvisibleAnnotations(注意:JVM 规范中并不存在此属性名,实际仍是 RuntimeVisibleAnnotations,只是是否“可见”由 RetentionPolicy 决定;真正区分靠的是注解是否被标记为 RetentionPolicy.RUNTIME —— javap 会如实展示其存在,而反射行为才体现“可见性”)

更准确的做法是结合 javap -v 全量输出,搜索 Annotation 关键字,定位到对应方法或字段的注解表项,再核对其签名和保留策略是否匹配。

别依赖反编译工具的“美化输出”

像 JD-GUI、CFR、Procyon 这类图形化反编译器,默认会把 class 中的注解“还原”成源码风格,容易误判。例如:即使注解是 CLASS 级别,JD-GUI 仍可能把它显示在反编译出的 Java 代码里,让人以为它“还在源码中”。这其实是反编译器根据字节码中的 Annotation 结构做的推测性还原,并非真实语义保留。

务必用 javap -v 查原始属性,它不猜测、不美化,只忠实打印 class 文件结构。

验证反射行为才是最终依据

字节码里有注解 ≠ 运行时能拿到。比如 @Retention(CLASS) 的注解,javap 能看到,但以下代码会返回空数组:

MyClass.class.getDeclaredMethod("test").getDeclaredAnnotations();

只有 RUNTIME 级别的注解,反射 API 才会将其暴露出来。所以,确认存留状态的闭环是:javap 看字节码 → 编写反射代码实测 → 二者一致才算验证完成。

终于介绍完啦!小伙伴们,这篇关于《@Retention注解存留状态反编译解析》的介绍应该让你收获多多了吧!欢迎大家收藏或分享给更多需要学习的朋友吧~golang学习网公众号也会发布文章相关知识,快来关注吧!

相关阅读
更多>
最新阅读
更多>
课程推荐
更多>