注解
什么是注解(Annotation)
注解的概念
注解(Annotation)是自 JDK 1.5 起引入的一种元数据(Metadata)机制,它允许开发者为程序元素(如类、方法、字段、接口、包等)添加结构化的附加信息。这些信息不直接影响程序的运行逻辑,但可以被编译器、开发工具或运行时框架读取并处理,从而实现诸如编译检查、代码生成、配置管理、行为增强等功能。
简单来说,注解是贴在代码上的“标签”,它本身不执行任何操作,但能被工具识别并据此做出决策。
注解和注释
- 注释 (Comment):是程序员在源码中编写的说明性文字,旨在提升代码的可读性与可维护性。这些内容仅供开发者阅读,在程序编译阶段会被完全忽略,不会对程序的运行产生任何影响。
 - 注解 (Annotation):是一种嵌入在代码中的元数据(metadata),它以结构化的形式为编译器、开发工具或运行时环境提供附加信息与指令。程序会根据预设的规则读取并处理这些注解,从而实现在编译时检查、代码生成或运行时注入等自动化行为。
 
项目 注解(Annotation) 注释(Comment) 定义方式 使用 @声明,如@Override使用 //、/* ... */、/** ... */等符号编译影响 可以参与编译和运行时处理 不会被编译器编译到字节码中 存在阶段 源代码、字节码、运行时(根据保留策略不同) 仅存在于源代码中 功能 提供元信息供工具、编译器、框架处理 仅用于增加代码可读性 是否可反射访问 可以通过反射 API 获取 无法访问 
注解的作用
注解的主要作用体现在 编译期 和 运行期,常见作用包括:
阶段 作用 说明 典型示例 编译期 编译检查 验证代码正确性,防止常见错误 @Override、@Deprecated代码生成 由 APT 工具生成新类或资源文件 Lombok 的 @Data、@Builder警告抑制 控制编译器警告输出 @SuppressWarnings("unchecked")运行期 反射处理 运行时读取注解并执行特定逻辑 Spring 的 @RequestMapping、@Transactional依赖注入 框架根据注解自动装配 Bean 或服务 @Autowired、@Inject权限控制 实现方法级别的安全访问控制 @PreAuthorize("hasRole('ADMIN')")工具支持 文档生成 将注解信息包含进 Javadoc @DocumentedIDE 提示 支持智能补全、重构、错误检测等 @Nullable、@NonNull
Java 内置注解
Java 提供了一些 JDK 内置的注解,主要用于编译器检查、代码提示或运行期处理。
@Deprecated
- 作用:标记某个程序元素(类、方法、字段等)已过时,不推荐使用。编译器在其他代码引用该元素时会生成警告信息,提示开发者迁移到新的 API。
 - 引入版本:JDK 1.5
 - 升级改进:Java 9 增加
 since和forRemoval属性
since(String):表示该元素自哪个版本开始弃用forRemoval(boolean):表示该元素是否计划在未来移除- 保留策略:
 RUNTIME,可在运行时通过反射判断是否弃用- 源码定义:
 @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.PARAMETER, ElementType.TYPE, ElementType.PACKAGE, ElementType.MODULE }) public @interface Deprecated { String since() default ""; boolean forRemoval() default false; }
- 使用示例:
 public class Example { /** * @deprecated 从 2.1 起弃用,请使用 {@link #newApi()} 替代 */ @Deprecated(since = "2.1", forRemoval = true) public void oldApi() { System.out.println("old"); } public void newApi() { System.out.println("new"); } public static void main(String[] args) { Example e = new Example(); e.oldApi(); // ⚠️ 编译器警告:oldApi() 已弃用 } }
@Override
- 作用:检查方法是否真正覆盖父类或实现接口中的方法。如果方法签名不匹配或可见性不正确,编译器会报错,避免因拼写错误或方法签名错误导致逻辑问题。
 - 背景:
 
- JDK 1.5 引入
 @Override,最初用于覆盖超类方法- 从 Java 6 开始,也可以用于实现接口的方法,因此在实现接口时使用
 @Override是安全且推荐的做法- 该注解属于SOURCE 级别,即编译后不会保留到
 .class文件中- 注意事项:
 
- 只对 实例方法 生效
 - 不能用于 构造方法
 - 用于 类继承或接口实现的方法
 - 源码定义:
 @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override {}
- 使用示例:
 class Parent { void greet() { System.out.println("Hi from Parent"); } } class Child extends Parent { @Override void greet() { // 正确覆盖父类方法 System.out.println("Hello from Child"); } // ⚠️ 如果方法签名错误,编译器会报错 // @Override // void greett() { } // compile error: method does not override or implement a method from a supertype } interface MyInterface { void run(); } class Impl implements MyInterface { @Override public void run() { // 正确实现接口方法 System.out.println("Running..."); } }
@SuppressWarnings
- 作用:告诉编译器在指定范围内 抑制特定类型的编译警告。常用于处理泛型类型不安全操作、已弃用方法调用或未使用变量等场景。
 - 背景:
 
- JDK 1.5 引入
 - 属于 SOURCE 级别,编译后不会保留到
 .class文件- 通过限制范围使用,可以避免隐藏潜在问题
 - 适用范围:
 
- 可应用于类、方法、构造方法、字段、局部变量、参数等
 - 推荐放置在最小范围,只抑制确实需要的警告
 - 源码定义:
 @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); // 指定要抑制的警告名称集合 }
- 常见参数值:
 
警告名 含义 unchecked 泛型类型不安全操作(如原始类型、强制转换) deprecation 使用了已弃用的方法或类 rawtypes 使用了未指定泛型参数的集合类型 unused 未使用的变量、方法或导入 serial 实现 Serializable但未定义serialVersionUIDall 抑制所有警告(不推荐) 注意:避免使用
@SuppressWarnings("all"),容易隐藏真实问题
- 使用示例:
 import java.util.*; public class SuppressExample { // 只抑制一个警告 @SuppressWarnings("unchecked") public void rawUsage() { List list = new ArrayList(); // raw type -> unchecked warning list.add("a"); List<String> strings = (List<String>) list; // unchecked cast } // 抑制多个警告 @SuppressWarnings({"unchecked", "deprecation"}) void multi() { List list = new ArrayList(); // unchecked oldMethod(); // deprecation } @Deprecated public void oldMethod() {} }
@SafeVarargs
- 作用:标记可变参数方法(varargs method)或构造器是类型安全的,从而抑制编译器对泛型可变参数的“堆污染”(Heap Pollution)警告。
 - 背景:
 
- JDK 1.7 引入
 - 用于 final、static 或 private 方法/构造器
 - 不能用于普通实例方法,因为子类可能重写该方法,编译器无法保证安全性
 - 堆污染说明:
 
- 泛型数组在运行时类型信息被擦除
 - 如果向数组中插入不符合泛型约束的对象,可能导致
 ClassCastException@SafeVarargs告诉编译器该方法不会修改可变参数数组,操作类型安全- 源码定义:
 @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.CONSTRUCTOR, ElementType.METHOD}) public @interface SafeVarargs {}
- 使用示例:
 import java.util.Arrays; public class SafeVarargsDemo { // ✅ 正确用法:只读参数,不修改 @SafeVarargs private static <T> void printAll(T... elements) { for (T e : elements) System.out.println(e); } // ⚠️ 错误用法:修改了泛型数组,不能标记为 @SafeVarargs // @SafeVarargs private static <T> void breakTypeSafety(T... elements) { Object[] objs = elements; objs[0] = 123; // 破坏类型安全:原本可能是 String[] } public static void main(String[] args) { printAll("A", "B", "C"); // 安全 // breakTypeSafety("X", "Y", "Z"); // 运行时 ClassCastException } }
@FunctionalInterface
作用:标记一个接口为函数式接口(Functional Interface),编译器会强制检查该接口是否恰好只包含一个抽象方法。
函数式接口是 Java 8 引入 Lambda 表达式的基础。
特点:
- 函数式接口:接口中恰好有一个抽象方法(Single Abstract Method,SAM)。
 - 允许有默认方法(default)或静态方法,不会影响函数式接口的定义。
 - 即使没有加
 @FunctionalInterface注解,只要接口满足 SAM 定义,也可以作为函数式接口使用。- 加了注解,编译器会检查,如果接口违反了 SAM 规则(抽象方法多于 1 个),编译器会报错。
 源码定义:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface FunctionalInterface {}
- 使用示例:
 @FunctionalInterface public interface MyFunc { void accept(String s); // 唯一的抽象方法 default void helper() { // 默认方法,不算抽象 System.out.println("helper"); } // ⚠️ 如果再加一个抽象方法,编译器会报错 // void another(); }
@Generated
- 作用:标记某段代码是由工具自动生成的,而非手工编写。主要用于区分手写代码和自动生成代码,方便静态分析工具、IDE 或框架进行处理。
 - 特点:
 
- 主要作用是元信息记录,不会影响程序编译或运行。
 - 位于
 javax.annotation.processing.Generated包(Java 9 引入)。- 常被代码生成器、注解处理器、框架工具使用。
 - 源码定义:
 @Documented @Retention(RetentionPolicy.SOURCE) @Target({ ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.PACKAGE }) public @interface Generated { String[] value(); // 生成该代码的工具名称 String date() default ""; // 生成日期(可选) String comments() default ""; // 备注信息(可选) }
- 使用示例:
 import javax.annotation.processing.Generated; @Generated(value = "MyCodeGenTool", date = "2025-09-17", comments = "自动生成类") public class AutoGeneratedClass { public void doWork() { System.out.println("工作中..."); } }
元注解(Meta-Annotation)
- 元注解是用于修饰注解的注解,用于规定自定义注解的行为特性,例如:注解的作用范围、保留周期、是否可继承、是否可重复等。
 - 它们位于
 java.lang.annotation包中。
@Retention
- 作用:指定注解的保留策略(生命周期),即注解在什么阶段可被保留和访问。
 - 可选值
 
策略 说明 示例注解 SOURCE 仅在源码阶段存在,编译后会被丢弃 @OverrideCLASS 编译进 .class文件,但运行时不可通过反射访问(默认)@DeprecatedRUNTIME 编译进 .class,运行时依然可通过反射访问自定义注解常用 
- 注意事项:
 
- JDK 1.5 引入
 - 通常用于修饰自定义注解
 - 源码
 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { RetentionPolicy value(); }
- 示例
 import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) // 运行时可通过反射读取 @interface MyAnno {} public class Demo { @MyAnno public void test() {} }
@Target
- 作用:指定注解可以应用到哪些程序元素(类、方法、字段等)。
 - 典型取值(ElementType 枚举):
 
ElementType 说明 备注 TYPE 类、接口、枚举 常用于类级注解 FIELD 成员变量(包括枚举常量) METHOD 方法 PARAMETER 方法参数 CONSTRUCTOR 构造方法 LOCAL_VARIABLE 局部变量 ANNOTATION_TYPE 注解类型 修饰其他注解 PACKAGE 包 TYPE_PARAMETER 类型参数 Java 8+ TYPE_USE 类型使用 Java 8+ MODULE 模块 Java 9+ 
注意事项:
JDK 1.5 引入
不写默认可用于所有元素
源码定义:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); }
- 使用示例:
 import java.lang.annotation.*; @Target({ElementType.METHOD, ElementType.FIELD}) @interface MyAnno {} public class Demo { @MyAnno public void test() {} }
@Documented
- 作用:标记注解使用的元素在生成 Javadoc 时包含注解信息。
 - 注意事项:
 
- JDK 1.5 引入
 - 默认情况下,注解不会出现在 Javadoc 中
 - 常用于公共 API 注解
 - 源码定义:
 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Documented {}
- 使用示例:
 import java.lang.annotation.*; @Documented @interface PublicApi {} /** * @PublicApi 注解会出现在 Javadoc 中 */ @PublicApi public class Service {}
@Inherited
- 作用:表示注解可以被子类继承(仅对类有效)。
 - 注意事项:
 
- JDK 1.5 引入
 - 只对
 @Target(ElementType.TYPE)注解有效- 不会继承到方法、字段、构造器等
 - 仅通过反射读取时可见
 - 源码定义:
 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Inherited {}
- 使用示例:
 import java.lang.annotation.*; @Inherited @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @interface MyAnno {} @MyAnno class Parent {} class Child extends Parent {} public class Demo { public static void main(String[] args) { System.out.println(Child.class.isAnnotationPresent(MyAnno.class)); // true:子类继承了父类的注解(反射可见) } }
@Repeatable
- 作用:允许同一位置多次使用同一个注解。
 - 注意事项:
 
- JDK 1.8 引入
 - 需要定义一个容器注解存放多个实例
 - 编译器自动打包多次使用的注解到容器注解
 - 反射可通过
 getAnnotationsByType获取所有注解- 源码定义:
 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Repeatable { Class<? extends Annotation> value(); }
- 使用示例:
 import java.lang.annotation.*; @Repeatable(Roles.class) @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @interface Role { String value(); } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @interface Roles { Role[] value(); } @Role("admin") @Role("user") class Demo {} public class Test { public static void main(String[] args) { Role[] roles = Demo.class.getAnnotationsByType(Role.class); for (Role r : roles) System.out.println(r.value()); // 输出:admin user } }
自定义注解
注解的定义语法
- 作用:自定义注解允许开发者为类、方法、字段等程序元素添加自定义元信息,从而供编译器、工具或框架处理。
 - 关键字:使用
 @interface定义注解,本质是一个特殊接口,继承自java.lang.annotation.Annotation。- 语法模板:
 [修饰符] @interface 注解名称 { // 属性列表 }
- 示例:
 import java.lang.annotation.*; // 定义一个简单注解 public @interface MyAnnotation {} // 使用自定义注解 @MyAnnotation public class Person { @MyAnnotation private String name; }
注解的属性与默认值
- 作用:在自定义注解中,可以通过定义方法来声明注解的属性(也叫元素),用于存储附加信息。
 - 注意事项:
 
- 注解的属性必须是有限的可支持类型:基本数据类型、
 String、Class、枚举类型、其他注解类型,或以上类型的一维数组。- 可以通过
 default指定默认值,如果未指定,则使用注解时必须为该属性赋值。- 注解属性不能有普通方法实现体。
 - 注解属性名不能重复。
 - 示例:
 import java.lang.annotation.*; // 定义一个注解,包含属性及默认值 public @interface MyAnnotation { String name() default "Unknown"; // 默认值为 "Unknown" int age() default 18; // 默认值为 18 } // 使用示例 @MyAnnotation public class Person { @MyAnnotation(name = "Alice", age = 25) private String name; @MyAnnotation // 使用默认值 private int id; }
value 属性的简化写法
- 作用:如果注解中只有一个属性,并且属性名为
 value,在使用注解时可以省略value=,让代码更简洁。- 注意事项:
 
- 仅适用于单个
 value属性的情况,如果属性名不是value,或者注解中有多个属性,则无法省略。- 如果有多个属性,即使其中一个是
 value,也不能省略其他属性。- 示例:
 import java.lang.annotation.*; // 定义一个只含 value 属性的注解 public @interface MyAnnotation { String value(); } // 使用简化写法 @MyAnnotation("Hello") // 等价于 @MyAnnotation(value = "Hello") // 如果有多个属性,必须显式赋值 public @interface MultiAttributeAnnotation { String value(); int age(); } // 使用示例 @MultiAttributeAnnotation(value = "Hi", age = 20) // 必须写 value=
自定义注解使用示例
- 作用:展示如何定义并使用自定义注解,包括类、方法和字段的应用。
 - 注意事项:
 
- Java 从 JDK 1.5 引入注解机制,自定义注解成为开发中元数据的重要手段。
 - 自定义注解的属性可有默认值,也可在使用时显式赋值。
 - 自定义注解可以为程序元素添加额外信息,被编译器、框架或运行时反射读取处理。
 - 注解可以加上元注解(如
 @Retention、@Target)来控制生命周期和适用范围。- 示例
 示例 1:简单注解
import java.lang.annotation.*; // 定义自定义注解 @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD}) public @interface MyAnnotation { String value() default "默认值"; } @MyAnnotation("应用于类") public class Person { @MyAnnotation("应用于字段") private String name; @MyAnnotation("应用于方法") public void sayHello() { System.out.println("Hello"); } }示例 2:带多个属性的注解
import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Info { String author(); int version() default 1; } @Info(author = "sun", version = 2) public class DemoClass {}
package 注解
- 作用:用于为 Java 包(package)声明注解,提供该包的元数据。
 - 注意事项:
 
- 从 JDK 5 引入,允许开发者在
 package-info.java文件中对包进行注解。- 常用于标记包级别信息,如作者、版本、文档说明或自定义元数据。
 package注解必须放在package-info.java文件中,文件必须存在于包目录下。- 该文件只能包含 package 注解和 package 声明,不能有其他类或接口。
 - 注解作用域为整个包,对包下所有类可统一说明或处理。
 - 示例:
 // 定义自定义包注解 import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PACKAGE) public @interface PackageInfo { String author(); String version() default "1.0"; }// 文件:com/example/package-info.java @PackageInfo(author = "sun", version = "2.0") package com.example; import com.example.PackageInfo;
注解在反射中的使用
反射机制允许在运行时获取类、方法、字段等程序元素的注解信息,从而实现动态处理逻辑,如自动注入、权限校验、配置加载等。
获取类的注解
- 作用:获取类上声明的注解实例。
 - 方法:
 
Class<?>.getAnnotation(Class<A> annotationClass):返回指定类型的注解,如果不存在返回null。Class<?>.getAnnotations():返回类上所有注解的数组。Class<?>.getDeclaredAnnotations():返回类上所有直接声明的注解,不包含继承的。- 示例:
 import java.lang.annotation.*; import java.lang.reflect.*; // 定义注解 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @interface Info { String author(); String version() default "1.0"; } // 使用注解 @Info(author = "sun", version = "2.0") class MyClass {} // 反射读取类注解 public class Demo { public static void main(String[] args) { Class<MyClass> clazz = MyClass.class; // 获取单个注解 Info info = clazz.getAnnotation(Info.class); if (info != null) { System.out.println("Author: " + info.author()); System.out.println("Version: " + info.version()); } // 获取所有注解 Annotation[] annotations = clazz.getAnnotations(); for (Annotation a : annotations) { System.out.println(a); } } }