概念
Java 注解(Annotation)又称 Java 标注。 Java 语言中的类、方法、变量、参数和包等都可以被标注。 Java 注解可以通过反射获取标注内容。 在编译器生成类文件时,注解可以被嵌入到字节码中,在运行时可以获取到标注内容 。
简单使用
声明一个注解
//指定该注解的保留策略:运行时保留 @Retention(RetentionPolicy.RUNTIME) //指定该注解的标注位置:类、字段、方法 @Target(value={ElementType.TYPE,ElementType.FIELD,ElementType.METHOD}) public @interface MyAnnotation{ //属性 String value(); int age() default 20; }
标注位置
注解可以标注在类、方法、字段等元素上。
@MyAnnotation(value="我在类上",age=10) public class Main{ @MyAnnotation("我在字段上") private int filed; @MyAnnotation("我在方法上") public void method(){ } }
获取注解
通过反射获取注解(Class、Method、Field)。
//获取类上所有注解 Annotation[] anns = Main.class.getAnnotations(); //获取类上指定注解 MyAnnotation ann = Main.class.getAnnotation(MyAnnotation.class); //获取字段指定注解 ann = Main.class.getField("filed").getAnnotation(MyAnnotation.class); //获取方法指定注解 ann = Main.class.getMethod("method").getAnnotation(MyAnnotation.class); System.out.println(ann.value()+":"+ann.age());
注解的本质
对注解类进行反汇编,可以发现注解的本质是继承java.lang.annotation.Annotation
的一个接口。
public interface MyAnnotation extends Annotation{ //属性 }
Annotation接口
public interface Annotation { //判断两个注解实例是否相同 boolean equals(Object obj); //返回此注解的哈希码 int hashCode(); //返回此注解的字符串表示形式 String toString(); //返回此注解的注解类型 Class<? extends Annotation> annotationType(); }
注解的“属性”
注解的“属性”就是注解接口类中的方法,例如上面MyAnnotation
注解中的value
、age
都被成为属性,其本质是一个get方法。
类型限定
注解属性的返回值类型只允许以下几种:
- 基本数据类型
- String
- 枚举
- 注解
- 以上类型的数组
默认值
定义属性时,可以使用default
关键字给属性默认初始化值。在使用注解时,对有默认值的属性可以不赋值。
例如:
public @interface MyAnnotation{ int age() default 20;//默认值为20 }
之后使用注解时可以直接使用@MyAnnotation
,而无需使用@MyAnnotation(age=20)
省略value
如果只有一个属性需要赋值,并且属性的名称叫value
,那么使用时可以省略value
,直接定义值即可。
例如:
public @interface MyAnnotation{ int value(); }
之后使用注解时可以直接使用@MyAnnotation
,而无需使用@MyAnnotation(value=20)
数组赋值
public @interface MyAnnotation{ int[] value(); }
数组赋值时,值使用{}包裹,若数组中只有一个元素,则{}可以省略。
例如:@MyAnnotation({1,2,3})
,@MyAnnotation
常用相关注解
元注解
元注解是用于标注注解的注解。
@Documented
@Documented注解表明这个注解是由 javadoc记录的。
如果一个类型声明被注解了文档化,它的注解成为公共API的一部分。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Documented { }
例如:@Documented
@Retention
@Retention作用是定义被它所注解的注解保留多久。
一共有三种策略,定义在RetentionPolicy枚举中。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { /** * Returns the retention policy. * @return the retention policy */ RetentionPolicy value(); }
例如:@Retention(RetentionPolicy.RUNTIME)
RetentionPolicy枚举
public enum RetentionPolicy { /** * 注解只在源代码级别保留,编译时被忽略 */ SOURCE, /** * 注解将被编译器在类文件中记录 * 但在运行时不需要JVM保留。 */ CLASS, /** * 注解将被编译器记录在类文件中 * 在运行时保留VM,因此可以反读。 * @see java.lang.reflect.AnnotatedElement */ RUNTIME }
@Target
@Target描述了注解的使用范围。
具体元素范围在ElementType枚举中。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); }
例如:
@Target(ElementType.ANNOTATION_TYPE)
@Target(value={ElementType.ANNOTATION_TYPE,ElementType.FIELD})
ElementType枚举
public enum ElementType { /** 类, 接口(注解), 或枚举声明 */ TYPE, /** 字段声明(包括枚举常量) */ FIELD, /** 方法声明(Method declaration) */ METHOD, /** 参数声明 */ PARAMETER, /** 构造函数声明 */ CONSTRUCTOR, /** 局部变量声明 */ LOCAL_VARIABLE, /** 注解类型声明 */ ANNOTATION_TYPE, /** 包声明 */ PACKAGE, /** * 类型参数声明 * @since 1.8 */ TYPE_PARAMETER, /** * 使用的类型 * @since 1.8 */ TYPE_USE }
@Inherited
描述注解是否被子类继承。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Inherited { }
JDK内置注解
@Override
监测被该注解标注的方法是否是继承自父类的。
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
例如:@Override
@Deprecated
表示已过时,编辑器会出现删除线提示。例如:new Date().getYear();
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) public @interface Deprecated { }
@SuppressWarnings
压制警告,忽略警告
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); }
例如:@SuppressWarnings("unchecked")
如何获取注解
可以通过Java反射来获取注解(Class、Method、Field)。
//获取类上所有注解 Annotation[] anns = Main.class.getAnnotations(); //获取类上指定注解 MyAnnotation ann = Main.class.getAnnotation(MyAnnotation.class); //获取字段指定注解 ann = Main.class.getField("filed").getAnnotation(MyAnnotation.class); //获取方法指定注解 ann = Main.class.getMethod("method").getAnnotation(MyAnnotation.class); System.out.println(ann.value()+":"+ann.age());
总结
- 大多数时候,我们会使用注解,而不是自己定义注解,大部分框架都使用注解,例如:SpringBoot、Lombok。
- 合理使用注解可以节省代码量,使逻辑更清晰。
全部评论
(1) 回帖