bigywater.tech
Java · 159

注解:Retention、反射和运行时代理

注解在运行时到底是什么?怎么继承、重复、读取?这篇一项项验证(RetentionPolicy 三种策略的基本读取已有专门文章,这里补充代理对象、继承、重复、位置与开销)。

$ProxyN
注解实例的类(动态代理)
null
重复写两次的 @Tag,getAnnotation 读到的结果
约 12 ms → 17 ns
第一次 getAnnotation 与之后每次

注解实例

实测 · 本机执行输出
  注解实例的类:$ProxyN,Proxy.isProxyClass = true,annotationType() = Info
  toString() = @Main.Info(name="svc", level=3, tags={"a", "b"})
  Child 上能读到 @Info 吗(@Info 没有 @Inherited):false
  只写 @Info、不给成员值 → name = anon,level = 1,tags 长度 = 0
  equals:同一个注解两次读取 equals = true,是同一个对象 = true,hashCode 相等 = true

@Inherited

实测 · 本机执行输出
  @Marker 有 @Inherited,Base 上有 @Marker:Child.class.getAnnotation(Marker) = @Main.Marker(v="base");getDeclaredAnnotation = null
  接口上的 @Marker(Impl implements I):Impl.class.getAnnotation(Marker) = null(类注解的继承只沿父类,不沿接口)
  方法上的 @Marker:Base.m 有 = true,覆盖它的 Child.m 有 = false(方法注解不继承)

@Repeatable

实测 · 本机执行输出
  @Tag 可重复。One(写了 1 个 @Tag):getAnnotation(Tag) = @Main.Tag("only"),getAnnotationsByType 长度 = 1
  Two(写了 2 个 @Tag):getAnnotation(Tag) = null(被编译器包进了容器注解 @Tags),getAnnotation(Tags) 不为 null:true,getAnnotationsByType(Tag) 长度 = 2
  Two.class.getAnnotations() 里的类型:[Tags]

保留策略与位置

实测 · 本机执行输出
  Three 上的注解(运行时可见的):[Rt](@Cl 是 CLASS、@Src 是 SOURCE,读不到)
  方法 f 的参数注解:第 1 个参数 [@Main.Info(name="p", level=1, tags={})],第 2 个参数 []
  TYPE_USE 注解 List<@NonNull String>:类型参数上的注解 = [@Main.NonNull()]
  Field.getAnnotations()(普通声明注解)= [](TYPE_USE 注解不算声明注解)

读取开销

实测 · 本机执行输出
  第一次 getAnnotation:12626 µs;第二次:0 µs;连续 100 万次平均 17 ns/次;读注解成员 name() 100 万次平均 8 ns/次

类文件里的注解属性

实测 · 本机执行输出
  RuntimeVisibleAnnotations: ['0: #26()', 'Main$Rt']
  RuntimeInvisibleAnnotations: ['0: #28()', 'Main$Cl']
  RuntimeVisibleParameterAnnotations: ['parameter 0:', '0: #20(#21=s#22)', 'Main$Info(']
  RuntimeVisibleTypeAnnotations: ['0: #14(): FIELD, location=[TYPE_ARGUMENT(0)]', 'Main$NonNull']

分步演示

注解
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12
    • 没有验证:注解处理器(编译期)、注解在不同类加载器间的兼容性、默认值被改变后的二进制兼容性、JDK 17 与 27 的第一次读取耗时;第一次读取的毫秒数是一次运行的结果
    京ICP备2026067502号-1京公网安备11010502063487号