Java注解是嵌入源码的结构化元数据,供工具链读取,具有类型、生命周期和作用范围;区别于注释,它参与编译校验与运行时反射,如@Override强制重写、@Deprecated提示弃用、@SuppressWarnings压制警告。

@Override、@Deprecated、@SuppressWarnings 这些“带@的标记”,就是 Java 注解——它不是注释,不被编译器忽略;也不是语法糖,不改变执行逻辑;而是一套嵌入源码的结构化元数据,专为工具链(编译器、框架、反射)读取设计。
注解和注释到底差在哪?
注释(// 或 /* */)只给人看,编译时直接丢弃;注解则是给机器看的“可编程标签”,编译器能检查它(比如 @Override 写错就报错),运行时也能通过反射拿到它(比如 Spring 用 @Service 找 Bean)。
关键区别在于:注解有类型、有生命周期、有作用范围,是语言级设施,不是文本备注。
-
@Override必须重写父类/接口方法,否则编译失败 → 编译期强制校验 -
@Deprecated标记后,调用处出现黄色警告(非错误),IDE 会划掉方法名 → 提示性元数据 -
@SuppressWarnings("unchecked")是“临时免责声明”,告诉编译器:“我知道这里有问题,先别提醒我” → 针对性压制警告
自定义注解必须配齐两个元注解
自己写 @MyLog 或 @Validated 类注解时,漏掉 @Retention 或 @Target 就等于没写完——前者决定“这注解活到什么时候”,后者决定“能贴在哪儿”。
-
@Retention(RetentionPolicy.RUNTIME):必须设为RUNTIME,反射才能读到;设成CLASS(默认)或SOURCE,运行时getAnnotation()返回null -
@Target({ElementType.METHOD, ElementType.TYPE}):不加这个,注解默认能打在任何地方(包括局部变量),但多数场景需要限制,比如日志注解只允许标在方法上
import java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MyLog { String value() default "default"; }
运行时读注解,反射不是万能的
想在代码里拿到注解内容,得靠反射,但容易踩三个坑:
-
getAnnotation()只查当前元素直接声明的注解,父类/接口上的不会继承(除非注解本身加了@Inherited,且仅对TYPE生效) - 方法重载时,
getMethod("xxx", String.class)和getDeclaredMethod("xxx", Object.class)查到的注解可能不同 - 注解属性值如果是数组、枚举、Class 或其他注解类型,反射取值要小心空指针或类型转换异常
例如:
立即学习“Java免费学习笔记(深入)”;
MyLog log = method.getAnnotation(MyLog.class);
if (log != null) { // 一定先判空!
System.out.println(log.value());
}真正难的不是写注解,而是设计它的生命周期与作用域边界。一个注解该不该被子类继承?需不需要在字节码里保留?是否允许重复标注(@Repeatable)?这些问题没想清楚,后期框架集成或维护时就会卡在“为什么读不到”“为什么被忽略”“为什么报错却没提示”上。










