@Override 是编译期强制校验注解,javac 硬编码识别其存在并检查重写合法性:方法需在父类/接口中声明、签名一致、返回类型协变、访问修饰符不更严格、不可重写 final 或 static 方法。

@Override 看似只是一行标记,但它背后没有运行时逻辑,也不生成字节码——它的全部作用,发生在编译阶段,由 javac 编译器主动识别并强制校验。这种“不执行、只检查”的机制,正是它作为编译期契约工具的本质。
编译器如何识别 @Override
编译器并不靠“读注解内容”来工作。@Override 的源码非常简单:
@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)
public @interface Override { }
它没有属性、没有实现类、不保留到 class 文件(RetentionPolicy.SOURCE 表示仅在源码阶段存在),编译器根本不会把它写进 .class 文件。那怎么起作用?答案是:javac 内置了硬编码规则:一旦遇到方法上标注了 @Override,就立即触发一套预设的重写合法性检查流程。
这个检查不是反射读取注解再判断,而是编译器在语法树(AST)解析阶段,直接比对当前方法与父类/接口中同名方法的签名。
校验过程的关键检查点
当编译器看到 @Override 时,会同步验证以下几项,任一不满足即报错(编译失败):
- 目标方法必须在直接父类或所实现的任一接口中声明(含继承链上可访问的方法)
- 方法名、参数个数、每个参数的类型及顺序必须完全一致
- 返回类型需满足协变要求(子类返回类型可以是父类返回类型的子类型,如 Object → String 允许;反之不允许)
- 访问修饰符不能比父类方法更严格(例如父类是 protected,子类不能用 private 或 package-private)
- 不能重写 final 方法,也不能重写 static 方法(二者在语义上均不可覆盖)
为什么去掉 @Override 代码还能编译通过?
因为 Java 语言规范本身允许隐式重写:只要方法签名匹配,即使没加 @Override,JVM 仍会按重写语义执行。但这时编译器不会做任何校验。
这就埋下隐患:
- 把
toString()拼成toStrng(),不加 @Override 就变成一个全新方法,父类调用仍走默认逻辑 - 父类方法改了参数类型(如从
List改为ArrayList),子类未同步更新,旧代码看似重写实则失效
而 @Override 的价值,就是把这类本该在开发阶段暴露的问题,提前到编译期拦截。
@Deprecated 和 @SuppressWarnings 的处理逻辑差异
它们和 @Override 同属内置注解,但编译器对待方式不同:
-
@Deprecated:编译器识别后不阻止编译,只在调用处输出 warning(如 “warning: [deprecation] oldMethod() in X has been deprecated”)。从 Java 9 起支持
@Deprecated(since = "2.0"),这部分信息会被保留在 class 文件的 Signature 属性中,供 IDE 或文档工具读取 - @SuppressWarnings:编译器根据其 value 属性(如 "unchecked"、"deprecation")动态关闭对应警告通道,属于“策略开关”,不涉及语义校验
三者共性在于:都由 javac 在解析阶段识别,都不依赖反射,也不进入运行时;区别在于约束强度——@Override 是强校验(error),@Deprecated 是弱提示(warning),@SuppressWarnings 是抑制指令(silence)。

















