Report report = Person.class.getAnnotation(Report.class); 为什么 Person.class 这个类对象,能够通过反射拿到一个注解的“实现类”?

✅ 简洁回答:

是的,编译器会把注解信息(包括注解类型和参数值)存储在被注解类的 .class 文件的特殊结构中
JVM 运行时可以通过 Class.getAnnotation() 方法反射出一个动态代理对象,它实现了注解接口,封装了你在源码中定义的值。


🧠 注解反射的关键机制:Annotation Proxy(注解代理类)

  • 注解是接口;
  • 你不能直接用 new 创建它的实例;
  • JVM 会使用动态代理(Proxy.newProxyInstance(...))自动创建一个实现了注解接口的对象;
  • 这个对象内部封装了注解的值,像 report.name() 实际就是调用了这个代理对象中的方法。

🔍 编译器是如何处理注解的?

1. 当你这样写:

@Report(name = "年度报告", type = 1)
public class Person {}

编译器会做两件事:

  • @Report 这个注解的“存在”信息写入 Person.class 的 class 文件中
  • 把注解的参数(比如 name=“年度报告”, type=1)一并写进去

这些信息存储在 .class 文件的特殊区域里,叫:

RuntimeVisibleAnnotationsRuntimeInvisibleAnnotations

你可以用 javap -v Person.class 看到这部分:

RuntimeVisibleAnnotations:
  0: #19(#20=s#21, #22=I#3)
     Report(name="年度报告", type=1)

🔍 运行时怎么获取注解?

当你运行这句代码时:

Report report = Person.class.getAnnotation(Report.class);

JVM 会:

  1. 读取 Person.class 中的 RuntimeVisibleAnnotations 部分;
  2. 确认里面有 @Report
  3. 提取所有注解参数;
  4. 利用动态代理(Java Proxy 机制)生成一个实现了 Report 接口的代理对象
  5. 返回这个代理对象,供你调用 report.name()report.type() 等方法。

✅ 类似结构图

┌─────────────────────┐
│      Person.class   │
│─────────────────────│
│ 字节码部分           │
│ RuntimeVisibleAnnotations:   ← 被编译器写入
│   → @Report(name="...", ...) │
└─────────────────────┘
         ↓
      JVM反射
         ↓
  动态创建代理对象(实现 Report 接口)
         ↓
  report.name() 实际调用 Proxy.invoke()

✅ 核心总结

步骤说明
注解写在类上编译器将注解及其参数存入 .class 文件
使用反射获取注解JVM 读取 class 文件中注解数据
生成代理对象JVM 自动创建一个实现了注解接口的代理类实例
使用注解参数调用 report.name() 实际是调用代理对象的对应方法返回注解值

✅ 结尾补充:是不是引用?

你的提问中说:

“是不是编译器会自动将注解的引用或是什么放入到被注解修饰过的类中?”

这个说法基本正确,不过更准确说:

  • 编译器不是把注解对象的“引用”放进去(因为注解在运行时才被生成);
  • 而是把注解的类型信息 + 参数值写进了 class 文件中的 注解表(属性表结构);
  • JVM 运行时用这些信息去动态生成注解实现对象(代理)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值