Java注解解析

概念

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注解中的valueage都被成为属性,其本质是一个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());

总结

  1. 大多数时候,我们会使用注解,而不是自己定义注解,大部分框架都使用注解,例如:SpringBoot、Lombok。
  2. 合理使用注解可以节省代码量,使逻辑更清晰。
#Java##学习路径#
全部评论
感谢参与【创作者计划3期·技术干货场】!欢迎更多牛油来写干货,瓜分总计20000元奖励!!技术干货场活动链接:https://www.nowcoder.com/link/czz3jsgh3(参与奖马克杯将于每周五结算,敬请期待~)
点赞 回复
分享
发布于 2021-05-17 16:44

相关推荐

3 7 评论
分享
牛客网
牛客企业服务