Java元注解不参与执行但决定自定义注解的识别、继承与重复使用;@Inherited仅对类级注解生效且依赖反射约定,@Repeatable需配合容器注解并改变获取方式,@Retention和@Target必须显式设为RUNTIME和合适目标。

Java元注解本身不参与业务执行,但决定了自定义注解能否被正确识别、继承或重复使用。真正影响运行时行为的关键,在于 @Inherited 和 @Repeatable 的组合规则与反射获取方式——它们不是简单的“有或没有”,而是受目标类型、声明位置和获取方法共同约束。
@Inherited 仅对类级注解生效,且继承逻辑依赖反射调用
当一个注解标注了 @Inherited 并作用于类(ElementType.TYPE),子类在未显式声明该注解时,通过 getAnnotation(Class) 可以获取到父类的注解实例。但这不是语言层面的继承,而是反射 API 的约定行为:
- 只适用于 类,对方法、字段、参数等位置的注解无效
- 接口上的注解永远不会被实现类继承
- 若子类自己也标注了同一注解,会覆盖父类注解(不是合并)
- 使用
getDeclaredAnnotation(Class)则只查本类,不向上查找
@Repeatable 要求容器注解配合,获取方式与单次使用不同
启用重复能力不只是加个 @Repeatable,它强制引入容器注解,并改变反射读取逻辑:
- 被 @Repeatable 标记的注解 A,必须声明对应容器注解 B,B 中需含
A[] value()成员 - 写多次
@A("x") @A("y"),编译后实际生成的是一个@B({@A("x"), @A("y")}) -
getAnnotation(A.class)在重复使用时返回 null;必须用getAnnotationsByType(A.class)才能拿到数组 - 容器注解 B 的元注解(如 @Target、@Retention)需与 A 保持一致或更严格
继承 + 重复组合时,子类获取结果取决于声明形式
当父类用了可重复注解,子类是否继承、如何获取,要看子类自己怎么写:
立即学习“Java免费学习笔记(深入)”;
- 父类标了
@A("a") @A("b")→ 实际是@B({@A("a"),@A("b")}),子类调用getAnnotationsByType(A.class)可拿到两个 - 子类自己也写
@A("c")→ 不会合并,而是覆盖为单个;若写@A("c") @A("d"),则形成新的容器,覆盖父类整个容器 - 子类调用
getAnnotation(B.class)只能拿到自己声明的容器,拿不到父类的(即使继承)
生命周期与作用位置是基础,必须显式设定
没设 @Retention(RetentionPolicy.RUNTIME),反射就读不到;没设 @Target,注解可能被误用到非法位置:
- @Retention 默认是 CLASS 级别,运行时无法反射获取,自定义注解务必显式写 RUNTIME
- @Target 不写时默认只允许用于类/接口/枚举(TYPE),想用在方法上必须明确声明
- @Documented 不影响功能,但对框架使用者很重要——比如 Spring 注解带它,Javadoc 就会显示用途


















