Java注解本质是继承Annotation的接口,编译后以元数据存于.class属性表;其行为由@Retention(SOURCE/CLASS/RUNTIME)决定生命周期,仅RUNTIME可反射获取,处理依赖APT或反射,本身无执行逻辑。

注解在编译后并不会“消失”,而是以结构化元数据形式保留在 .class 文件的属性表中。反编译本身不直接“显示注解原理”,但通过合适的工具和方法,可以观察注解如何被写入字节码、何时保留(SOURCE / CLASS / RUNTIME)、以及运行时能否反射获取——这些正是理解注解原理的关键实证环节。
看注解是否存在于字节码中:用 javap -v 查属性表
javap 是 JDK 自带、最底层可靠的查看方式。它能直接暴露注解在字节码中的物理存在形式:
- 执行 javap -v MyClass.class,重点查找 RuntimeVisibleAnnotations(@Retention(RetentionPolicy.RUNTIME) 的注解)或 RuntimeInvisibleAnnotations(@Retention(RetentionPolicy.CLASS) 的注解)属性项
- 每个注解条目会显示其完整类名、元素名(如 value=)、字面量值(如 "admin")、嵌套注解或数组结构
- 若某注解未出现在这两个属性中,说明它被设为 @Retention(SOURCE),仅参与编译期检查,不会进入字节码
验证注解保留策略对反编译结果的影响
不同保留策略决定注解能否被反编译工具还原为源码级写法:
- FernFlower、JD-GUI 等高级反编译器默认只还原 RuntimeVisibleAnnotations 对应的注解(即 RUNTIME 级),因为它们有明确语义且常用于框架逻辑
- @Retention(CLASS) 的注解(如 Lombok 生成的 @lombok.Generated)可能不显示在反编译源码中,但 javap -v 仍可见其属性记录
- 若反编译结果里完全看不到某个注解,先用 javap -v 确认它是否进了字节码;若没进,问题出在声明时的 @Retention 配置,而非反编译工具
观察注解与字节码指令的关联(进阶)
注解本身不产生执行逻辑,但某些注解会触发编译器生成额外字节码(如 @Override 检查、@SafeVarargs 生成桥接方法、Lombok 的 @Data 插入 getter/setter)。此时可:
- 对比加注解前后的 javap -c 输出,查看方法体是否有新增指令(如 aload_0、getfield、areturn 等)
- 例如:一个空的 @Data 类,反编译后会出现 public String getName() { return this.name; },而原始源码中并无此方法——这说明注解已驱动编译器生成了字节码
- 这种“注解→编译器行为→字节码变更”的链路,才是注解原理在字节码层面的真实体现
注意反编译的局限性:别把“没显示”等同于“不存在”
图形化反编译工具为提升可读性,会做语义简化。常见误导情形包括:
- 省略无参数的标记型注解(如 @FunctionalInterface),即使字节码中存在 RuntimeVisibleAnnotations
- 将重复注解(@Repeatable)展开为多个独立声明,掩盖其容器机制
- 无法还原注解处理器生成的代码(如 Dagger、AutoService),因那些是全新 class,非原 class 的注解内容
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











