Java注解-----Annotation浅析

时间:2021-09-19 20:14:56

最近项目要添加摇一摇和试吃功能(感觉low到爆),开发周期比较紧,博客也有几天没更新了,今天抽空再来一篇。
注解是JDK1.5之后才有的新特性,现在使用已经非常普遍了。最近刚好项目里用到了,现在做一下总结。

初识注解

我们先看JDK1.5之后提供的三个注解:

注解 释义
@Deprecated 废弃的,过时的,不建议使用的
@Override 重写、覆盖
@SuppressWarnings 压制警告信息,防止报黄线警告
public class Test {

@SuppressWarnings(":deprecation") //压制警告信息
private void method1() {

}

@Deprecated //JDK自带注解,过时的,不建议使用
private void method2() {
}

@Override //JDK自带注解,重写(覆盖)Object父类的toString()
public String toString() {
return super.toString();
}
}

注解的使用

上面我们介绍了三种JDK自带的注解,其实注解的使用和普通修饰符(public,static,void等)区别并不大。使用注解相当于为程序加上某种标记,这个标记可以加在包,类,属性,方法,方法参数以及局部变量上。
下面列举注解在代码中的使用

@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.TYPE)
public @interface ModelPanel {
}

@interface:是一个关键字,在设计注解的时候必须把这个类型定义为@interface,而不能用class或interface关键字
@Target:用来约束注解可以应用的地方(如方法、类或字段),其中ElementType是枚举类型,其定义如下,也代表可能的取值范围。
注意:当注解未指定Target值时,则此注解可以用于任何元素之上,多个值使用{}包含并用逗号隔开,例:

   //这是我们项目中用到的注解类
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.FIELD,ElementType.METHOD,ElementType.CONSTRUCTOR})
public @interface ModelPanel {
}
public enum ElementType {
/** 标明该注解可以用于类、接口(包括注解类型)或enum声明 */
TYPE,

/** 标明该注解可以用于字段(域)声明,包括enum实例 */
FIELD,

/** 标明该注解可以用于方法声明 */
METHOD,

/** 标明该注解可以用于参数声明 */
PARAMETER,

/** 标明注解可以用于构造函数声明 */
CONSTRUCTOR,

/** 标明注解可以用于局部变量声明 */
LOCAL_VARIABLE,

/** 标明注解可以用于注解声明(应用于另一个注解上)*/
ANNOTATION_TYPE,

/** 标明注解可以用于包声明 */
PACKAGE,

/**
* 标明注解可以用于类型参数声明(1.8新加入)
* @since 1.8
*/

TYPE_PARAMETER,

/**
* 类型使用声明(1.8新加入)
* @since 1.8
*/

TYPE_USE
}

@Retention用来设置注解的生命周期,分别有三个值,源码级别(source),类文件级别(class)或者运行时级别(runtime),其含义如下:

  • @Retention(value = RetentionPolicy.SOURCE)
    这个注解的意思是让注解只在java源文件中存在,编译成.class文件后注解被丢弃了
  • @Retention(value = RetentionPolicy.CLASS)
    这个注解的意思是让注解在java源文件(.java文件)中存在,编译成.class文件后注解也还存在,被ModelPanel注解类标识的类被类加载器加载到内存中后ModelPanel注解就不存在了,当注解未定义Retention值时,默认值是CLASS
  • @Retention(value = RetentionPolicy.RUNTIME)
    这个注解的意思是让注解在程序运行期(JVM)一直保留,因此可以通过反射机制读取注解的信息。

当在Java源程序上加了一个注解,这个Java源程序要由javac去编译,javac把java源文件编译成.class文件,在编译成class时可能会把Java源程序上的一些注解给去掉,java编译器(javac)在处理java源程序时,可能会认为这个注解没有用了,于是就把这个注解去掉了,那么此时在编译好的class中就找不到注解了, 这是编译器编译java源程序时对注解进行处理的第一种可能情况,假设java编译器在把java源程序编译成class时,没有把java源程序中的注解去掉,那么此时在编译好的class中就可以找到注解,当程序使用编译好的class文件时,需要用类加载器把class文件加载到内存中,class文件中的东西不是字节码,class文件里面的东西由类加载器加载到内存中去,类加载器在加载class文件时,会对class文件里面的东西进行处理,如安全检查,处理完以后得到的最终在内存中的二进制的东西才是字节码,类加载器在把class文件加载到内存中时也有转换,转换时是否把class文件中的注解保留下来,这也有说法,所以==说一个注解的生命周期有三个阶段:java源文件是一个阶段,class文件是一个阶段,内存中的字节码是一个阶段,javac把java源文件编译成.class文件时,有可能去掉里面的注解,类加载器把.class文件加载到内存时也有可能去掉里面的注解,因此在自定义注解时就可以使用Retention注解指明自定义注解的生命周期,自定义注解的生命周期是在RetentionPolicy.SOURCE阶段(java源文件阶段),还是在RetentionPolicy.CLASS阶段(class文件阶段),或者是在RetentionPolicy.RUNTIME阶段(内存中的字节码运行时阶段),根据JDK提供的API可以知道默认是在RetentionPolicy.CLASS阶段== (JDK的API写到:the retention policy defaults to RetentionPolicy.CLASS.)
(摘自https://www.cnblogs.com/xdp-gacl/p/3622275.html)

注解元素及其数据类型

上述@ModelPanel内部没有定义其他元素,所以@ModelPanel也称为标记注解(marker annotation),但在自定义注解中,一般都会包含一些元素以表示某些值,方便处理器使用,这点在下面的例子将会看到:

@Retention(value = RetentionPolicy.RUNTIME)  //在程序运行中一直存在
@Target(value = ElementType.TYPE) //只能作用到类上
public @interface ModelPanel {
boolean needLogin() default false; //该界面是否需要登录才能访问,默认不需要
}

如上定义名为ModelPanel的注解,与前面注解不同的是,我们声明一个boolean类型的needLogin元素,其默认值为false,但是必须注意到对应任何元素的声明应采用方法的声明方式,同时可选择使用default提供默认值,@ModelPanel使用方式如下:

//在类上使用该注解

@ModelPanel(needLogin = false)
public class MainActivity{

}

关于注解支持的元素数据类型除了上述的String,还支持如下数据类型


  • 所有基本类型(int,float,boolean,byte,double,char,long,short)
  • String
  • Class
  • enum
  • Annotation
  • 上述类型的数组

倘若使用了其他数据类型,编译器将会丢出一个编译错误,注意,声明注解元素时可以使用基本类型但不允许使用任何包装类型,同时还应该注意到注解也可以作为元素的类型,也就是嵌套注解。
  public static void main(String[] args) {
/*
* 用反射方式获得注解对应的实例对象后,在通过该对象调用属性对应的方法
*/

ModelPanel modelPanel = currentClass.getAnnotation(ModelPanel.class);
System.out.println(modelPanel.needLogin());
}

Value 属性

如果注解中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只有一个value属性),那么可以省略掉“value=”部分。
定义如下:

@Retention(value = RetentionPolicy.RUNTIME)  //在程序运行中一直存在
@Target(value = ElementType.TYPE) //只能作用到类上
public @interface ModelPanel {
String value(); //定义一个名称为value的属性
boolean needLogin() default false; //该界面是否需要登录才能访问,默认不需要
}

使用如下:

//在类上使用该注解

@ModelPanel("test") // 等于@ModelPanel(value="test")
public class MainActivity{

}

以上为个人理解的注解用法,更深入的用法暂时没用到,等以后有时间再补上,趁着中午发了博客,下午还得继续赶项目。(略略略。。。)