android study 5-java

Author Avatar
Xzhah 6月 16, 2022
  • 在其它设备中阅读本文章

java反射机制

1.什么是java反射

​ java反射是指在运行状态中,对于任意类,都可以得到这个类的属性,方法信息,以及得到这个类的对象。对于任意对象,都可以调用它的方法,以及访问它的属性。这种动态获取信息,动态调用对象方法的功能叫做java的反射机制。

1.1 java反射使用场景

​ 比如不知道类或者对象的具体信息,比如类的名称放在某个文件中,需要运行时读取文件以动态获取类的信息。或者注入后想要覆写函数地址完成hook,也可以通过java反射来完成。

1.2 基础知识

​ 在JVM中,会为每个加载的类维护一个类对象,然后根据类对象来创造实例对象或者提供静态变量。

​ class对象的获取:

1. 通过对象.getClass()
2. 通过类.class字面量
3. 通过class类的forName方法
4. 通过类加载器的loadClass

那么如何对class对象进行操作呢?先获取到类型的Class对象,通过Class对象的newInstance方法可以得到类的实例。通过Class对象可以获取到Constructor对象,进一步可以使用Constructor对象来得到类的实例。通过Class对象可以获取到Method对象,通过Method的invoke方法可以调用一些方法。通过Class对象可以获取到Field对象,我们可以对这个实例的一些字段进行赋值取值操作。流程如下

1
2
3
4
actionClass = Class.forName(“ MyClass”);
action = actionClass.newInstance();
method = actionClass.getMethod(“ myMethod”,null);
method.invoke(action,null);

2.动态代理与静态代理

​ 代理本身不实现具体服务,只是利用委托类完成服务,在此过程中对输入输出做一些处理。

2.1 静态代理

​ 静态代理中,代理类和被代理类会实现同样的接口。代理类同时会持有有被代理类的对象。可以通过调用代理类的方法以调用被代理类的对应方法,相当于可以增加一些前置准备工作。比如给该方法添加一个计时的操作。

2.2 动态代理

​ 动态代理类需要实现InvocationHandler接口以执行动态代理,需要重写invoke函数以实现动态代理。里面是具体的代理服务。动态代理具体步骤如下:

​ 1.通过实现InvocationHandler接口创建自己的调用处理器

2. 通过为Proxy指定ClassLoader对象和一组interfaces来创建动态代理类
3. 通过反射机制获得动态代理类的构造函数,唯一参数是调用处理器的接口类型
4. 通过构造函数创建动态代理类型实例,构造时调用器类型作为参数传入(具体来说,是jdk根据传入的参数信息,在内存中动态创建.class文件,然后通过newInstance创建动态代理类的实例)

​ Java动态代理实际上通过反射技术,把代理对象和被代理对象(真实对象)的代理关系建立延迟到程序运行之后,动态创建新的代理类去完成对真实对象的代理操作(可以改变原来真实对象的方法行为),这一点成为了当前主流的AOP框架和延迟加载功能的基础。

3.双亲委派

3.1 类的初始化

​ 大多数情况下,类加载后直接就初始化了。以下三种情况是,类加载后,会延迟初始化。

​ 1.通过子类调用父类的静态成员时,只需要完成父类的初始化,子类初始化被延迟了。

​ 2.使用某个类的静态常量时,可以延迟类的初始化。

​ 3.使用某个类声明并创建数组时,如果还未真正创建该类的对象时,可以延迟类的初始化

​ 以上三种以外的,都会直接对类进行初始化:

​ 1.new对象

​ 2.调用某个类的静态变量

​ 3.通过反射的API调用该类成员等

​ 4.main方法所在的类,一定先初始化完成,再执行main方法的。

​ 5.当子类初始化时,如果父类还未初始化,会先初始化父类。

3.2 类加载器

​ 类加载器的类型是java.lang.classloder,是负责类的初始化工作。

​ 1.引导类加载器(Bootstrap ClassLoader),由C语言编写,负责加载最核心的类库。

2. 扩展类加载器(Extension ClassLoader),负责加载扩展库。JRE的lib中的ext目录下的扩展库
 3. 应用程序类加载器(Application ClassLoader),负责加载用户自定义的类型,在classpath下的类
 4. 自定义类加载器。加载classpath以外的类,或者需要在加载时做一些操作如解密,这时候需要使用自定义类加载器。

3.3 双亲委托模式

​ 应用程序类加载器把扩展类加载器视为双亲,扩展类加载器把引导类加载器 视为双亲。其原理是当JVM接到一个”类的加载任务时“,AppClassLoader先相应,它会在当前方法区找找看,如果加载过了,就不会重复加载。

​ 如果发现这个类没有加载过,它会将加载类的任务再次提交给”parent“,此时就是引导类加载器,他也会先找一下是不是被加载过了。加载过了就不再重复加载了。

​ 如果引导类加载器发现这个类没有加载过,它就把加载类的任务提交给自己的”parent“,此时就是JRE核心类库的rt.jar,如果自己负责的目录下有,就正常加载,返回Class对象即可。

​ 如果引导类加载器在自己负责的目录下找不到这个类,就会把任务传给ExtClassLoader,ExtClassLoader接下来也在自己负责的目录下尝试加载,此处时JRE的lib中的ext目录下的扩展库,如果自己负责的目录下有,就正常加载,返回Class对象即可。

​ 如果AppClassLoader在自己负责的目录下还找不到这个类,就报异常ClassNotFoundException异常。

​ 如果在前面的加载过程中,加载失败会报NoClassDefFoundError,ClassFormatError等。

​ 这样做的好处是安全,用户定义的类就不会覆盖到核心类库中的类。