细说,Java中的注解

1. 什么是注解

注解,又称元数据,是为程序源代码添加额外信息的一种形式方法,使得程序能够在某个时刻方便地利用这些额外的数据。
也就是说,注解里存储着一些额外的数据,如果源代码中有这些注解,那么就可以将这些数据取出来,留以己用。

举个栗子:程序猿小A想追求设计组的小C,就找到了C的朋友小B,向她了解了一些C的喜好,最后B给了A一张纸条,说:这上面有C的电话、生日。
情况1:A在随后的日子里对C有了更深一步的了解,觉得她是个好女孩,于是想继续追求她,根据纸条上的生日信息和电话,在她生日那天,买了束花,向小C表白了。
情况2:随着A对C越来越多的了解,A觉得C并不是他理想中的另一半,就放弃了对她的追求,于是呢,当初那张写着小C电话和生日的纸条也就没什么用途了。

注解,就可以理解成那张纸条,它存储着程序源代码的额外信息(小C的电话、生日信息),至于源代码会不会用到这些信息,那就取决于源代码自己了,它可以选择用或不用。

1.1 注解的格式

通过 @interface 关键字来定义,看起来和接口的定义很相似,并且注解也会被编译成class文件。
下面是定义一个@MyTest注解:

public @interface MyTest {
}

1.2 元注解

元,会想到元祖这个词。注解,它的使用本身会受到限制,譬如说,注解存活的时间、什么情况下注解可以修饰类或者方法、注解有没有特殊作用等。元注解的作用就是用来限制其他注解的,规范其他一般注解的使用,可以说是一般注解的元祖。

Java 中内置了六种元注解,分别如下:

元注解 描述
@Target JDK1.5 引入,标注注解声明的类型,比如:方法声明、类声明等,详见下面的ElementType
@Retention JDK1.5 引入,注解存留的时间,比如:注解是否能保留到编译时,或者被保留到运行时,详见下面的RetentionPolicy
@Documented JDK1.5 引入,能够将注解中的元素包含到 Javadoc 中去
@Inherited JDK1.5 引入,表示允许子类继承父类中的注解
@Repeatable JDK1.8 引入,表明标记的注解可以多次应用于相同的声明或类型使用
@Native JDK1.8 引入,用于对属性做提示

ElementType:

类型 解释
ANNOTATION_TYPE 应用于注解类型
CONSTRUCTOR 应用于构造函数声明
FIELD 应用于属性或enum实例
LOCAL_VARIABLE 应用于局部变量声明
METHOD 用于方法声明
PACKAGE 应用于包声明
PARAMETER 应用于方法的参数
TYPE 应用于类、接口或者enum声明

RetentionPolicy:

类型 解释
SOURCE 注解只在源码阶段保留,在编译器编译时它将被丢弃忽视。
CLASS 注解只被保留到编译进行的时候,不会被加载到JVM 中。
RUNTIME 注解会被加载到JVM中,可以在程序运行时通过反射机制获取到注解的信息。

2. 自定义注解

大多数时候,我们主要是使用自定义注解,并且要有读取注解的处理器来获取注解中包含的信息。

2.1 自定义一个注解并通过反射获取注解的元素值

注解可以包含一些元素(形式类似接口方法),通过给这些元素赋值,使得程序可以在需要的时候获取到这些值 ,这其中用到的就是反射机制。
例如,下面自定义一个@Vip注解,它包含一个【vipPrice】元素,如果用户被@Vip注解标记,那么这个用户享受的就是VIP会员价格,即vipPrice的值。
定义@Vip:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Vip {
    /**
     * VIP用户享有的价格
     */
    double vipPrice();
}

使用@Vip标记VipUser的price属性:

public class VipUser extends User {
    @Vip(vipPrice = 99.00)
    private Double price;
    
    ...
}

通过使用反射获取@Vip注解的price值

public static void main(String[] args) throws Exception{
    //获取 VipUser 的 price 属性
    Field price = VipUser.class.getDeclaredField("price");
    price.setAccessible(true);
    //获取 @Vip 注解对象
    Vip vip = price.getAnnotation(Vip.class);
    //获取vipPrice,并打印到控制台
    if (vip != null) {
        double vipPrice = vip.vipPrice();
        System.out.println(vipPrice);
    }
}

控制台打印如下:

99.0
Process finished with exit code 0

上述用到了两个反射方法:getDeclaredField()和getAnnotation(),其中getAnnotation()方法返回指定类型的注解对象,这里就是Vip,如果未使用该类型的注解,则返回null,随后,通过调用price()方法获取到vip对象中的price值。

另外,还可以为注解的元素设定默认值default,如:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Vip {
    /**
     * VIP用户享有的价格,默认值66.00
     */
    double vipPrice() default 66.00;
}

2.2 注解元素的类型

上述例子中,注解元素price的类型是基本类型double,除此之外,注解可用的其他类型有:

  • 基本型(int、float、boolean、long等)
  • String
  • 枚举类型enum
  • class
  • Annotation(注解可以作为元素的类型,即注解可以嵌套)
  • 元素类型是上述类型的数组

【未完待续......】


版权声明:本文为博主原创文章,欢迎转载,转载请注明作者、原文超链接。
✿ 获取更多,请戳这儿

Comments
Write a Comment