Java 注解浅析
一、什么是注解
注解(Annotation)即代码里的特殊标记,JDK5.0 之后引入了 Annotation 的概念来描述元数据,在 Java 中元数据以标签的形式存在于 Java 代码中,但是元数据标签的存在并不影响程序代码的编译和执行
在 JavaSE 中,注解的使用目的比较简单,例如标记过时的功能(@Deprecated)、限定必须重写父类的方法(@Override)、抑制编译器警告(@SuppressWarning)等.但是在JavaEE 中注解占据了更加重要的角色,例如之前需要实现 AOP 时,需要繁冗的 XML 配置,有了注解之后实现 AOP 变得更加的简洁.注解是一种趋势,在一定程度上可以说 框架 = 注解 + 反射 + 设计模式
二、常见的注解示例
1、文档注释相关注解

1 2 3 4 5 6 7 8 9 10 11 | @Documented @Retention (RetentionPolicy.RUNTIME) @Target (ElementType.ANNOTATION_TYPE) public @interface Retention { /** * Returns the retention policy. * @return the retention policy */ // @Retention 的取值 RetentionPolicy value(); } |
@Rentention 的取值选项如下
@Rentention(RententionPolicy.SOURCE / RententionPolicy.CLASS / RententionPolicy.RUNTIME)
RententionPolicy.SOURCE: 注解只在源码阶段保留,编译器编译的时候将会被丢弃
RententionPolicy.CLASS: 注解只被保留至编译器编译后的字节码文件,它不会被虚拟机加载,如果没有注明使用其它的值,默认的取值就是 RententionPolicy.CLASS
RententionPolicy.RUNTIME: 注解能够保留至运行期,通过反射可以获取该注解的信息
3.2、@Target
1 2 3 4 5 6 7 8 9 10 11 12 | @Documented @Retention (RetentionPolicy.RUNTIME) @Target (ElementType.ANNOTATION_TYPE) public @interface Target { /** * Returns an array of the kinds of elements an annotation type * can be applied to. * @return an array of the kinds of elements an annotation type * can be applied to */ // @Target 的取值 ElementType[] value(); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | public enum ElementType { /** Class, interface (including annotation type), or enum declaration */ // 类、接口(包括注解类型)、或者是枚举声明上 TYPE, /** Field declaration (includes enum constants) */ // 字段上 FIELD, /** Method declaration */ // 方法上 METHOD, /** Formal parameter declaration */ // 参数上 PARAMETER, /** Constructor declaration */ // 构造方法上 CONSTRUCTOR, /** Local variable declaration */ // 局部变量上 LOCAL_VARIABLE, /** Annotation type declaration */ // 注解上 ANNOTATION_TYPE, /** Package declaration */ // 包上 PACKAGE, /** * Type parameter declaration * * @since 1.8 */ // 参数类型声明上 TYPE_PARAMETER, /** * Use of a type * * @since 1.8 */ TYPE_USE } |
3.3、@Documented
如果某一个注解上面使用了 @Documented 注解,那么该注解将会被 JavaDoc 工具提取成文档,定义了 @Documented 的注解必须包含@Rentention(RetentionPolicy.RUNTIME)
3.4、@Inherited
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | // 自定义 @TestInheritedAnnotation 注解,然后在该注解上使用元注解 @Inherited @Inherited @Retention (RetentionPolicy.RUNTIME) @Target (ElementType.TYPE) public @interface TestInheritedAnnotation { String value(); } // 定义动物类作为父类,并且使用了 @TestInheritedAnnotation 注解 @TestInheritedAnnotation (value = "xiaomaomao" ) public class Animal { } // 定义 Animal 的子类 Cow,测试 Cow 中是不是也有 @TestInheritedAnnotation 注解 public class Cow extends Animal { public static void main(String[] args) { System.out.println(isHasInheritedAnnotation()); } public static String isHasInheritedAnnotation() { String value = "" ; // 判断 Cow 类上是否使用了 @TestInheritedAnnotation 注解 boolean hasAnnotation = Cow. class .isAnnotationPresent(TestInheritedAnnotation. class ); if (hasAnnotation) { TestInheritedAnnotation annotation = Cow. class .getAnnotation(TestInheritedAnnotation. class ); value = annotation.value(); } return value; } } |
3.5、@Repeatable
可重复注解, @Repeatable 注解是 JDK 8 之后才加入进来的,是 JDK 8 新特性,它的意思是注解的值可以取多个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | // 自定义容器注解 Person @Retention (RetentionPolicy.RUNTIME) @Target (ElementType.TYPE) public @interface Person { // @Person 注解是 @RepeatableAnnotation 的容器,定义类型为注解数组 RepeatableAnnotation[] value(); } // 自定义注解 RepeatableAnnotation // 使用 @Repeatable 注解的时候,必须要为当前注解定义一个容器注解,我们这里是 @Person @Repeatable (Person. class ) @Retention (RetentionPolicy.RUNTIME) @Target (ElementType.TYPE) public @interface RepeatableAnnotation { String role() default "coder" ; } // 测试类 @RepeatableAnnotation (role = "president" ) @RepeatableAnnotation (role = "product manager" ) @RepeatableAnnotation (role = "technology manager" ) @RepeatableAnnotation (role = "cleaner" ) public class RepeatableAnnotationDemo { public static void main(String[] args) { StringBuffer stringBuffer = new StringBuffer(); // 判断 RepeatableAnnotationDemo 类上是否使用了@Person 注解 boolean hasPersonAnnotation = RepeatableAnnotationDemo. class .isAnnotationPresent(Person. class ); if (hasPersonAnnotation) { // 获取 RepeatableAnnotationDemo 类上 Person 注解 Person personAnnotation = RepeatableAnnotationDemo. class .getAnnotation(Person. class ); // 通过 Person 注解容器获取到该容器内的所有 @RepeatableAnnotation 注解 RepeatableAnnotation[] repeatableAnnotations = personAnnotation.value(); // 获取所有的 @RepeatableAnnotation 中的 role 值并拼接输出 for (RepeatableAnnotation repeatableAnnotation : repeatableAnnotations) { stringBuffer.append(repeatableAnnotation.role() + "===>" ); } System.out.println(stringBuffer); } } } // 测试结果 president===>product manager===>technology manager===>cleaner===> |
三、注解的属性
注解的属性也叫做成员变量,注解只有成员变量,没有方法,注解的成员变量是以 无形式参数的方法 来声明的,其方法中定义了该成员变量的类型和成员方法的名字
注意: 在注解中定义属性,属性的类型必须是八种基本数据类型、类、接口、注解以及它们的数组
如下自定义的注解中有 int 类型的 id,它有一个默认值 10086、String 类型的 userName、以及 String 数组类型的 hobby
1 2 3 4 5 6 7 | @Retention (value=RetentionPolicy.RUNTIME) @Target (ElementType.TYPE_USE) public @interface TestAnnotation { // 注解中可以有默认值,使用 default 关键字来指定 int id() default 10086 ; String userName(); String[] hobby(); } |
1、如何给注解赋值
1 2 3 4 5 | // 根据 @TestAnnotation 的属性类型和属性名称依次给注解赋值,多个值用逗号隔开,由于 id 属性有默认值, // 那么这里可以不用给id赋值,如果显示的给id赋值的话,那么它会覆盖默认值 @TestAnnotation (userName = "xiaomaomao" ,hobby={ "film" , "novel" , "play computer games" }) public class AnnotationDemo { } |
2、如果注解只有一个属性的情况
1 2 3 4 5 6 | @Retention (value=RetentionPolicy.RUNTIME) @Target (ElementType.TYPE_USE) public @interface TestAnnotation { // 如果注解只有一个属性,请使用 value 来命名该属性 String value(); } |
取值的时候需要注意
1 2 3 4 | // 如果注解只有一个属性可以不写 value,直接 @TestAnnotation("xiaomaomao") 赋值也是一样的 @TestAnnotation (value= "xiaomaomao" ) public class AnnotationDemo { } |
四、自定义注解
1、定义新的 Annotation 类型使用 @interface 关键字.
2、自定义注解自动继承了java.lang.annotation.Annotation 接口.
3、Annotation 的成员变量在 Annotation 定义中以无参数方法的形式来声明,其方法名和返回值定义了该成员的名字和类型,我们称为配置参数.类型只能是八种基本数据类型、String 类型、Class 类型、enum 类型、Annotation 类型、以上所有类型的数组.
4、可以在定义 Annotation 的成员变量时为其指定初始值,指定成员变量的初始值可使用 default 关键字.
5、如果只有一个参数成员,建议使用参数名为 value.
6、如果定义的注解含有配置参数,那么使用时必须指定参数值,除非它有默认值.格式是 "参数名 = 参数值",如果只有一个参数成员,且名称为 value,可以省略 "value="
7、没有成员定义的 Annotation 称为标记;包含成员变量的 Annotation 称为元数据 Annotation.
特别注意:自定义注解必须配上注解的信息处理流程才有意义.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | // 自定义注解 TestAnnotation @Retention (value=RetentionPolicy.RUNTIME) @Target (ElementType.TYPE_USE) public @interface TestAnnotation { int id() default 10086 ; String userName(); String[] hobby(); } // 测试类 @TestAnnotation (id= 10001 ,userName = "xiaomaomao" ,hobby={ "film" , "novel" , "play computer games" }) public class AnnotationDemo { public static void main(String[] args) { // 判断 AnnotationDemo 类上是否使用了 @TestAnnotation 注解 boolean hasAnnotation = AnnotationDemo. class .isAnnotationPresent(TestAnnotation. class ); StringBuffer stringBuffer = new StringBuffer(); if (hasAnnotation) { // 获取 AnnotationDemo 上的所有注解,这里只有一个 @TestAnnotation 注解 Annotation[] annotations = AnnotationDemo. class .getAnnotations(); for (Annotation annotation : annotations) { // 获取 @TestAnnotation 注解中的属性并进行拼接 if (TestAnnotation. class .equals(annotation.annotationType())) { TestAnnotation testAnnotation = (TestAnnotation) annotation; int id = testAnnotation.id(); String userName = testAnnotation.userName(); String[] hobbys = testAnnotation.hobby(); stringBuffer.append(id); stringBuffer.append( "\r\n" ); stringBuffer.append(userName); stringBuffer.append( "\r\n" ); stringBuffer.append( "[" ); for (String hobby : hobbys) { stringBuffer.append(hobby+ "、" ); } stringBuffer.append( "]" ); stringBuffer.append( "\r\n" ); // 获取 @TestAnnotation 上的注解类型并进行拼接 Annotation[] underTestAnnotations = testAnnotation.annotationType().getAnnotations(); for (Annotation underTestAnnotation : underTestAnnotations) { Class<? extends Annotation> aClass = underTestAnnotation.annotationType(); stringBuffer.append(aClass); stringBuffer.append( "\r\n" ); } } System.out.println(stringBuffer); } } } } // 测试结果 10001 xiaomaomao [film、novel、play computer games、] interface java.lang.annotation.Retention interface java.lang.annotation.Target |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?