1. 定义:
定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释
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文件 ---> 内存中的字节码
- 源码注解:注解只在源码中存在,编译成.class文件就不存在了,也就是生命周期最短的一个
- 编译时注解:注解在源码和.class文件中都存在(如:@Override、@Deprecated、@SuppressWarings),不是通过反射实现的,不影响性能,butterknife.EventBus等都用的是编译型注解
- 运行时注解:在运行阶段还起作用,甚至会影响运行逻辑的注解,注解不仅被保存到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