注解、反射、代理

404 阅读6分钟

注解Annotation

注解:在方法、类、字段上加一个 @XXX 来进行 注释 或者 限定(输入输出规则) 或者 注入代码、帮助写一些重复代码等等操作,单独的注解没有意义,但是结合反射、插桩等技术,就有无限可能。

注解声明

所有的注解都默认实现Annotation接口。

image.png

注解声明: 在Interface前面加一个@符号,表示注解。

image.png

元注解

元注解:给注解使用的注解
重要的有两:@Target({ElementType.TYPE}) 和 @Retention(RetentionPolicy.SOURCE)

1. Target:声明这个注解是用在哪的。方法?类?参数?变量? 有多个那就{ElementType.TYPE,ElementType.FIELD} “,”隔开排列。

image.png

image.png

2. Retention:注解指定标记注解的存储方式:注解保留级
SOURCE:注解保留在源码上:在编译期能够获取注解与注解声明的类包括类中所有成员信息,一般用于生成额外的辅助类。 -- 例子:APT技术、IDE语法检查

CLASS:注解保留在字节码:在编译出Class后,通过修改Class数据以实现修改代码逻辑目的。对于是否需要修改的区分或者修改为不同逻辑的判断可以使用注解。-- 例子:字节码增强(在.class文件上编写代码),AspectJ、热修复Roubust

RUNTIME:注解保留在运行时在程序运行期间,通过反射技术动态获取注解与其元素,从而完成不同的逻辑判定。-- 例子:反射

image.png

反射

反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和 方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。

Java反射机制主要提供了以下功能:

  • 在运行时构造任意一个类的对象
  • 在运行时获取或者修改任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法(属性)

Class

反射始于Class,Class是一个类,封装了当前对象所对应的类的信息。一个类中有类名,属性,方法,构造器等

获得 Class 对象:

1.通过类名获取 类名.class
2.通过对象获取 对象名.getClass()
3.通过全类名获取 Class.forName(全类名) classLoader.loadClass(全类名)

创建实例

通过反射来生成对象主要有两种方式。
1.使用Class对象的newInstance()方法来创建Class对象对应类的实例。

image.png

2.先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。(先取到构造函数,在通过构造函数的newInstance()创建)

image.png

获取构造器

image.png

Constructor 里有一个newInstance()方法,可以创建对象
public T newInstance(Object ... initargs)

成员变量(字段)

image.png

获得方法信息的

image.png

反射获取泛型真实类型

当我们对一个泛型类进行反射时,需要的到泛型中的真实数据类型,来完成如json反序列化的操作。此时需要通
Type 体系来完成。 Type 接口包含了一个实现类(Class)和四个实现接口,他们分别是: TypeVariable
泛型类型变量。可以泛型上下限等信息;
ParameterizedType
具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)
GenericArrayType
当需要描述的类型是泛型类的数组时,比如List[],Map[],此接口会作为Type的实现。
WildcardType
通配符泛型,获得上下限信息

TypeVariable

image.png

ParameterizedType

image.png

GenericArrayType

image.png WildcardType

image.png

Gson反序列化

Gson 把String转Json的代码:

image.png

fromJson()入参有两种,class或者type:

image.png

为什么不用class呢,因为泛型在编译的时候回进行类型擦除。也就是对象具体类型就变成Object了,BeseBean<>里的 ParameteBean当成Object处理,无法返回对象了。

image.png

用TypeTolen.getType() 能取到具体的type

那为啥要 new TypeTolen<>()+{}呢

{}说明的是这个创建的是一个匿名内部类,没有{}就是一个对象。如果是一个对象,那么 泛型T 还是被擦除了,变成Object了。但是+了{} 就变成声明一个匿名内部类。就 T就变成一个类,擦除以后多了一个class文件,T指向这个文件。 (大概名字)XXX.BaseBean $1.class。这个$1.class就是BaseBean的一个匿名内部类。然后这个匿名内部类里面没有T泛型,就是具体的ParameteBean,不会被擦除

代理

代理可以在运行时创建实现了一组给定接口的新类
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活 中常见的中介。
目的:(1)通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;
(2)通过代理对象对访问进行控制;

image.png
代理模式有三种角色:
抽象角色:指代理角色和真实角色对外提供的公共方法,一般为一个接口
真实角色:需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。也就是真正的业 务逻辑在此。
代理角色:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附 加自己的操作。将统一的流程控制都放到代理角色中处理!

静态代理
写死的代理。

image.png

image.png

image.png

image.png

动态代理

动态代理的实质跟静态代理一样的,最重要的是代理类是通过Proxy.newProxyInstance()来创建的。

image.png

image.png

image.png

例子:

1.自定义注解,自动注入findViewById 2.自动注入getIntent().getStringExtra()来取得.putExtra("name1","AActivty的值")的数据

1.自动填findViewById代码截图

image.png

image.png

image.png

2.自动注入getIntent().getStringExtra()代码截图

image.png

image.png

image.png

3.自动注入setOnclickLinser 实现butterknife效果

image.png

image.png

image.png

改进,上面的写法很笨!!!

Intent传值,是通过Bundle的。put方法里其实是将name,value装到Bundle里

image.png

而通过继续查看Bundle.putString(),居然是存到ArrayMap<String, Object> mMap这个Map里的

image.png

image.png

取也是从Bundle里取的,各种getStringExtra(String name),getIntExtra(String name, int defaultValue)也是从Bundle里取的

image.png

image.png

image.png

通过上面的查看Bundle就是一个ArrayMap<String, Object> 一个key,value存储的Map,所以从bundle里取值自然就是通过key取出Map里的值

image.png

优化后的代码

通过上面分析发现,完全没必要用getIntent自带的各种类型的取值,然后一堆if 来判断各种类型,在取出来。可以直接用Bundle里的get(key)取出来

image.png

测试代码

image.png
image.png

源码地址:

此上所有代码均为学习笔记,请勿用于商业
gitee.com/winding2015…