Java语言特性-注解

注解定义

Java注解(Annotation)又称Java标注,是JDK1.5引入的一种注释机制。是元数据的一种形式,提供有关于程序但不属于程序本身的数据,注解对她们注解的程序没有直接影响。
注解本身没有任何意义,单独的注解就是注释,它需要结合反射,插桩等技术才有意义。

注解声明

Java中所有的注解,默认实现Annotation接口

package java.lang.annotation;
public interface Annotation{
       boolean  equals(Object obj);
       int hashCode();
       String toString();
       Class<? extends Annotation> annotationType();
 }

与声明一个“Class”不同的是,注解的声明使用@interface关键字,比如:

public @interface  注解名{
}

元注解

在定义注解时,注解类也能够使用其他的注解声明。对注解类进行注解的注解类,称为meta-annotation(元注解)。一般我们在定义自定义注解时,需要指定的元注解有两种@Target与@Retention。还有两种元注解@Documented 与 @Inherited 元注解,前者用于被javadoc工具提取成文档,后者表示允许子类继承父类中定义的注解。由于后两者并不常用,所以不做过多介绍。
@Target
该注解标记另一个注解,用来限制可以应用注解Java元素类型。

  • ElementType.ANNOTATION_TYPE 可应用于注解类型
  • ElementType.CONSTRUCTOR 可应用于构造函数
  • ElementType.FIELD 可应用于字段或属性
  • ElementType.LOCAL_VARIABLE 可应用于局部变量
  • ElementType.METHOD 可应用于方法级注解
  • ElementType.PACKAGE 可应用于包声明
  • ElementType.PARAMETER 可应用于方法的参数
  • ElementType.TYPE 可应用于类的任何元素

@Retention
该注解用来指定标记注解的存储方式:

  • RetentionPolicy.SOURCE - 标记的注解仅保留在源级别中,并被编译器忽略
  • RetentionPolicy.CLASS - 标记的注解在编译时由编译器保留,但 Java 虚拟机(JVM)会忽略
  • RetentionPolicy.RUNTIME - 标记的注解由 JVM 保留,因此运行时环境可以使用它
    这三者的关系是RUNTIME 包含CLASS 跟SOURCE ;CLASS 包含SOURCE 。
    使用示例:
 // 允许在类与类属性上标记该注解
@Target({ElementType.TYPE,ElementType.FIELD}
 //注解保留在源码中
@Retention(RetentionPolicy.SOURCE)
public @interface 自定义的注解名{
}

注解类型元素

自定义注解也能像元注解一样在使用时传递参数,声明如下:

public @interface 自定义的注解名{
        String name();//没有默认值
        int age() default 20;//有默认值
        String value;//是一个特殊的元素
}

注意:在使用注解时,如果定义的注解中的类型元素没有默认值,则必须传值。

public class xx{
 @自定义的注解名(“老实人”)//如果只存在一个value元素需要传值的情况,可以省略:元素名=
 @自定义的注解名(value = "老实",name = "人"//age有默认值可不传
   int  i; 
}

注解应用场景

按照@Retention 元注解定义的注解存储方式,注解可以被在三种场景下使用:

RetentionPolicy.SOURCE :作用于源码级别的注解,可提供给IDE语法检查、APT等场景使用。

IDE语法检查
在Android开发中, support-annotations 与 androidx.annotation) 中均有提供 @IntDef 注解,此注解的定义如下

@Retention(SOURCE) //源码级别注解
@Target({ANNOTATION_TYPE})
public @interface IntDef {
int[] value() default {};
boolean flag() default false;
boolean open() default false;
}

Java中Enum(枚举)的实质是特殊单例的静态成员变量,在运行期所有枚举类作为单例,全部加载到内存中。比常量多5到10倍的内存占用。此注解的意义在于能够取代枚举,实现如方法入参限制。
如:我们定义方法 test ,此方法接收参数 teacher 需要在:Li、Wang中选择一个。如果使用枚举能够实现为

public enum Teacher{
    Li,
    Wang
}
public void test(Teacher teacher) {
}

而现在为了进行内存优化,我们现在不再使用枚举,则方法定义为

public static final int Li = 1;
public static final int Wang = 2;
public void test(int teacher) {
}

然而此时,调用 test 方法由于采用基本数据类型int,将无法进行类型限定。此时使用@IntDef增加自定义注解:

public static final int Li = 1;
public static final int Wang = 2;
@IntDef(value = {Li, Wang}) //限定为Li,Wang
@Target(ElementType.PARAMETER) //作用于参数的注解
@Retention(RetentionPolicy.SOURCE) //源码级别注解
public @interface Teacher {
}
public void test(@Teacher int teacher) {
}

此时,我们再去调用 test 方法,如果传递的参数不是 Li 或者 Wang则会显示 Inspection 警告(编译不会报错)。

APT注解处理器
APT全称为:“Anotation Processor Tools”,意为注解处理器。顾名思义,其用于处理注解。编写好的Java源文件,需要经过 javac 的编译,翻译为虚拟机能够加载解析的字节码Class文件。注解处理器是javac 自带的一个工具,用来在编译时期扫描处理注解信息。你可以为某些注解注册自己的注解处理器。 注册的注解处理器由 javac调起,并将注解信息传递给注解处理器进行处理。

注解处理器是对注解应用最为广泛的场景。在Glide、EventBus3、Butterknifer、Tinker、ARouter等常用框架中都有注解处理器的身影。但是你可能会发现,这些框架中对注解的定义并不是 SOURCE 级别,更多的是 CLASS 级别,别忘了:CLASS包含了SOURCE,RUNTIME包含SOURCE、CLASS。

RetentionPolicy.CLASS:定义为 CLASS 的注解,会保留在class文件中,但是会被虚拟机忽略(即无法在运行期反射获取注解)。此时完全符合此种注解的应用场景为字节码操作(直接修改字节码Class文件以达到修改代码执行逻辑的目的)。如:AspectJ、热修复Roubust中应用此场景。在程序中有多处需要进行是否登录的判断。

if(isLogin){
//已经登录,通过验证
}else{
//没有登录,进入登录

如果我们使用普通的编程方式,需要在代码中进行 if-else 的判断,也许存在十个判断点,则需要在每个判断点加入此项判断。此时,我们可以借助AOP(面向切面)编程思想,将程序中所有功能点划分为: 需要登录 与 无需登录两种类型,即两个切面。对于切面的区分即可采用注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface Login {
}
@Login
public void jumpA(){
startActivity(new Intent(this,AActivity.class));
}
public void jumpB(){
startActivity(new Intent(this,BActivity.class));
}

在上诉代码中, jumpA 方法需要具备登录身份。而 Login 注解的定义被设置为 CLASS 。因此我们能够在该类所编译的字节码中获得到方法注解 Login 。在操作字节码时,就能够根据方法是否具备该注解来修改class中该方法的内容加入 if-else 的代码段:

//Class字节码
@Login
public void jumpA() {
  if (this.isLogin) {
      this.startActivity(new Intent(this, LoginActivity.class));
   } else {
       this.startActivity(new Intent(this, AActivity.class));
   }
}
public void jumpB() {
    startActivity(new Intent(this,BActivity.class));
}

RetentionPolicy.RUNTIME:注解保留至运行期,意味着我们能够在运行期间结合反射技术获取注解中的所有信息,从而完成不同的逻辑判定。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值