注解的详细介绍和使用

1. 定义:

定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释

2. 为什么要使用注解

  1. 使用注解可以提升开发效率,因为注解中已经封装好了我们需要的代码
  2. 当我们开发项目的时候,我们会用到很多第三方库,里面都用注解的形式开发的,
    比如: Spring , Mybatis, butterknife , EventBus等等.我们可以去更好的阅读他们的代码,了解底层实现原理

3. 注解的分类

3.1 按照来源分

  • 标准注解(Jdk自带的注解): 如Override, Deprecated,SuppressWarnings等
  • 第三方注解:如 butterknife的@bindview,EventBus的@Subscribe(threadMode = ThreadMode.MAIN),来自第三方库的注解
  • 自定义注解: 自己写自己用,为了装b使用

3.2 按运行机制分 (也可以说是生命周期吧)

简单的说下他的生命周期吧:
Java源文件(.java文件) ---> .class文件 ---> 内存中的字节码
  1. 源码注解:注解只在源码中存在,编译成.class文件就不存在了,也就是生命周期最短的一个
  2. 编译时注解:注解在源码和.class文件中都存在(如:@Override、@Deprecated、@SuppressWarings),不是通过反射实现的,不影响性能,butterknife.EventBus等都用的是编译型注解
  3. 运行时注解:在运行阶段还起作用,甚至会影响运行逻辑的注解,注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在,本质通过反射实现的,影响性能

4. 定义注解

先看一下简单的定义一个注解

@Retention(RetentionPolicy.RUNTIME) 
@Target({ElementType.FIELD, ElementType.METHOD})
@Documented
public @interface InJect {
    String name();
    int age() default 18;
}

语法: 以 @interface 这个为代表注解

5 元注解

定义: 定义注解的注解,主要包括@Retention @Target @Document @Inherited四种,下面一一说一下这四个的意思

5.1 @Retention 定义注解的保留,也就是注解存在的生命周期.有三个

  • RetentionPolicy.SOURCE :也就是上面所说的源码注解,只会存在源码内
  • RetentionPolicy.CLASS :也就是存在.class文件中,运行时不需要Vm保留
  • RetentionPolicy.RUNTIME: 运行时注解将被编译器记录在类文件中
    在运行时可以通过反射获取到。

5.2 @Target 定义注解作用目标 可以使用在哪里

ElementType的枚举数组包、类、接口、注解、枚举、字段(类的属性)、构造方法、普通方法、参数(方法的参数)、局部变量.
枚举类型的常量值有

  • ElementType.TYPE(类,或者接口,或者枚举上面)
  • ElementType.FIELD(字段上)
  • ElementType.METHOD(方法上)
  • ElementType.PARAMETER(参数)
  • ElementType.CONSTRUCTOR(构造方法上)
  • ElementType.LOCAL_VARIABLE(局部变量上)
  • ElementType.ANNOTATION_TYPE(注释声明)
  • ElementType.PACKAGE(包)
  • ElementType.TYPE_PARAMETER(类型参数) JDK 1.8以后才有
  • ElementType.TYPE_USE(类型声明和类型参数声明) JDK 1.8以后才有

上面我感觉最重要的ElementType(比如GreenDao).TYPE,ElementType.FIELD(比如butterknife),ElementType.METHOD(比如EventBus),其他的可以了解一下

5.3 @Documented 和 @Inherited 两个都是标示型注解

@Documented:生成API帮助文档时显示注解
@Inherited:允许子类继承父类的注解

6 注解的语法

6.1 以@interface为标识

6.2 变量语法

比如上面String desc() ,int age() default 18

  • 成员变量必须以无参无异常的方式生明 如果有参数 直接爆红
  • 可以使用default修饰 默认值
  • 成员类型可以是 基本类型。 String。 Class。 Annotation 。Enumeration(咱们主要用到基本类型,String ,class 后面两个很少用到)
  • 如果注解只有一个成员的时候,成员名必须取名为 value(), 但是如果不取名value ,不报错,不符合规定(java界的约定成俗)
  • 当有一个成员的时候 ,使用的时候,可有直接省略value 比如@SuppressWarnings(“deprecation”); //不用@SuppressWarnings(value=”deprecation”)(java界的约定成俗)
  • 注解类可以没有成员变量,没有成员的注解成为标识注解. 比如override 表明是继承父类的标识

7 使用

<注解名>(<成员名1>=<值1>,<成员名2>=<值2>。。。)

public class DaHa(){
            @InJect (name="哈士奇",age=1) //定义在方法上根据@Target({ElementType.FIELD,ElementType.METHOD})
            public void like(){ 
            }
        }
    }

7.1 模仿butterknife为例简单实现(运行时实现方式)。当然butterknife是编译型注解,稍后再说

7.1.1 定义butterknife注解
//运行时注解
@Retention(RetentionPolicy.RUNTIME)
//声明在变量上
@Target({ElementType.FIELD})
@Documented
public @interface BindView {
  // 因为只有一个成员所以起名为value,当然了可以随便起
    int value();
}
7.1.2 写一个Demo使用这个注解
public class Demo {
    @BindView(100)
    View view;
    @BindView(101)
    View view1;
    @BindView(102)
    View view2;
    @BindView(103)
    View view3;
}
7.1.3 写一个单元测试类
 @Test
public void text() throws Exception {
    //1.使用类加载器加载类
    Class c = null;
    // c = Class.forName("nzy.cn.annotationdemo.Demo"); 或者下面的也可以得到
    c = ClassLoader.getSystemClassLoader().loadClass("cn.nzy.annotationdemo.Demo");
    //2.找到类中所有的成员变量
    Field[] declaredFields = c.getDeclaredFields();
    // 3. 循环所有的成员变量 查看每个成员变量上是否存在BindView的注解 返回是boolean值
    for (Field field : declaredFields) {
        boolean isExist = field.isAnnotationPresent(BindView.class);
        //如果存在
        if (isExist) {
            //4. 拿到注解的实例对象
            BindView bind = (BindView) field.getAnnotation(BindView.class);
            //5. 得到里面所给你的值
            int id = bind.value();
            // 打印出来 变量类型 变量名字  
            System.out.println("field是 "+field.getType()+"----"+field.getName()+"--------" + "id是" + id);
        }
    }

打印结果为:

 field是 class android.view.View----view--------id是100
field是 class android.widget.TextView----view1--------id是101
field是 class android.widget.ImageView----view2--------id是102
field是 class android.widget.Button----view3--------id是103

此时既拿到 类型, 变量名字, 跟id 然后再执行findViewById就可以得到了
以下是伪代码,知道大概意思就可以了

// 伪代码 执行不过去滴
 View field.getName()= findViewById(id)

7.2 简单的介绍一下ButterKnife的原理

其实ButterKnife的大致流程跟上面所说的差不多,当然了,ButterKnife用的是编译时期-注解处理器AbstractProcessor类, 下一篇文章再讲解这个类.
其实ButterKnife最后还是执行的findViewById这个方法,他走的流程

layout -> Window -> PhoneWindow ->DecorView->在这个里面findviewbyid
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值