1、PathClassLoader与DexClassLoader的区别是什么?
1.1 概述
- 程序运行时需要将class加载到JVM才能用,classLoader就是用来加载class文件的。每个class内部都有一个classLoader字段来标识自己是哪个ClassLoader加载的。
- BootClassLoader : 加载FrameWork层的class文件
- DexClassLoader : 用于android应用程序类加载器。用于加载指定的dex。jar、zip、apk中的classes.dex
- PathClassLoader: 用于加载指定的dex。jar、zip、apk中的classes.dex
- InMemoryDexClassLoader:Android 8.0新增 用于加载内存中的dex
1.2 区别
- 4.4及以下
- DexClassLoader:可加载jar、apk、dex,可从SD卡中加载
- PathClassLoader:只能加载已安装到系统中(data/app)的apk文件
- 5.0~8.0
- DexClassLoader:可加载jar、apk、dex,可从SD卡中加载
- PathClassLoader:可加载jar、apk、dex,可从SD卡中加载。但是无法进行dex2oat
- 8.1及以上
- 两者一样。
2、什么是双亲委托机制
2.1 当一个类加载器收到一个类加载请求时,先把请求委托给父类加载器,只有父类找不到指定的类的时候,他才会自己去加载
2.2 好处是什么?
2.2.1 防篡改
- 如果用于自己编写一个java.lang.object。而且没有双亲委托,那么它写的类可能就覆盖掉系统的这个类,就会出错。
2.2.2 防重复加载
- 只有类名和加载器名相同才会判定同一个类,双亲委派机器能够保证是同一个加载器加载,方式同一个类不同加载器加载导致不是同一个类。
3、Android 中加载类的方法有哪些?有啥区别?
- 和Java加载类的方式一样,加载、校验、准备、解析和初始化
3.1具体方法5种
- 使用new实例化对象,创建子类会先加载父类
- 访问类的静态方法
- 访问类的静态属性
- 对类进行反射调用
- Java程序中定义main类,启动java方法时,类会被嘉爱。
- ClassLoader#loadClass(name)
- Class.forName(name) 内部也是采用上述方法。除了上方法外还会类初始化,执行static块
- 也就使用用它的重载函数,有个参数选择是否初始化。
4、ClassNotFound有可能什么原因
- APK中dex未包含需要加载的类数据
- dex分包,前边已加载的dex加载了还未加载的dex中的类。为了避免这种情况,确保正确地配置和使用多dex加载机制
- 类数据校验合法失败,但在log中可以看到。
5、Odex是什么?解释型语言和变异型语言啥区别?
- Odex 就是优化过的dex文件,OptimizedDex。
- APK就是个zip压缩包。Android 虚拟机需要从Zip压缩中读取classes.dex 文件完成类加载,odex就是把dex文件从Zip压缩包中提取,每次运行就不用再去APK里去拿了。
- 编译型语言
- 利用编译器将源码一次性编译成二进制指令,生成可执行程序,C/C++
- 解释型语言
- 边执行边用解释器转换。比如javaScript PHP
- java有点特殊,是用编译器编译成字节码,然后JVM将字节码解释执行
- 解释型语言基本有跨平台能力,代码一样,在不同平台上将源码解释为不同的机器码就好啦。
6、说下反射的应用场景,哪些框架?
- 判断
- 运行时判断一个对象所属类用.getClass()
- 运行时判断一个所有的成员变量和方法。
- 获取
- 运行时构建任意一个对象
- 运行时获取任意对象的方法、成员变量。即使是private的
- 生成动态代理
-哪些框架用?
- 插件化(动态加载插件)、热修复(修改dex数组)、retrofit(通过泛型类型生成类哪里,用了反射)
7、反射为什么这啥这么慢?
- 频繁拆装箱,invoke的参数是Object[],那么意味着基本类型要转为包装类,然后封装为Object数组。然后使用再拆箱。
- 需要检查方法可见性。setAccessible
- 需要检验参数
- JIT无法优化,比如反射方法就无法内联
8、动态代理是什么,如何实现?
- 代理模式,使用一个类代理另外一个类的功能,当一个对象不适合或者不能直接引用另外一个对象,而代理对象可以再 客户端和 目标对象之间起到中介作用。
- 一般分为
- 抽象角色
- 提供接口给 代理角色和真实角色
- 真实角色
- 实现抽象角色提供的接口,写上具体 逻辑
- 代理角色
- 实现抽象角色接口,是 真实角色的代理,通过 调用真实角色来实现抽象方法
- 抽象角色
8.1、静态代理
- 可维护性差,一个方法改了,要改3个文件 ,维护性 查。
- 静态代理一对一,静态代理对象多了。就要写很多文件。
- 如果一对多,则扩展能力差。
8.2、动态代理
其实就是通过抽象接口和真实角色,通过Proxy的getProxyClass方法生成了一个代理的实例(有个代理的class可以看下),这个实例内部就是通过反射区调用真实角色的。 - 优点 - 运行时动态创建代理类,不要手动创建 - 可以处理多接口情况,不需要为每个接口创建一个代理类。 - 可以来灵活地为目标类扩展功能。 - 缺点 - 学习成本高 - 因为用到反射,所以性能相对低 - 的使用场景优先,一般框架设计,远程方法(Binder)调用。
8.3 我写的详细版
设计模式(2)-动态代理-大白话版 - 掘金 (juejin.cn)
9 动态代理的方法,怎么初始化的。
- java动态大力中 会根据代理的接口生成Proxy派生类作为代理类。这个类会生成一段静态代码块,静态代码块中使用了放射获取到被代理的所有方法,假设代理方法名字叫buyMethod,并赋值给成员属性,供后续使用。
- 动态代理对象调用代理方法buyMethod时,内部是会调用 InvocationHandler的invoke方法,并把通过反射 的来 的buyMethod传过去。在invoke里执行了内容,其中就有通过反射得来的buyMethod反射执行。就ok了。
10、讲一下CGlib
- 基于ASM的字节码生成库,Android现在用不了。允许在运行时对字节码修改和动态生成。
- 通过继承某类实现代理。在子类中采取 拦截的计数拦截父类方法调用。然后在前后加东西。
- 优点
- JDK只能处理接口,CGlib没有要求,成为被代理类的子类(代理类)。很有意思
- 缺点
- 速度比jdk动态代理慢一些
- final 的方法无法重写
- 优点
- 步骤
- 生成代理类的二进制字节码文件
- 加载二进制 字节码,生成Class对象
- 通过反射构建实例。
- 总结
- CGLIB运行时生成代理类 继承被代理类, final 方法无法代理
- 代理类为委托方法假设为doIt生成两个方法,doIt和CGLIB0
- 代理对象执行doIt,代理对象则调用Enhancer.setCallback设置的MethodInterceptor.intercept方法,传入了CGLIB0。
- intercept里在CGLIB0前后做操作,CGLIB0,里则是调用父类。
11、为什么IO是耗时操作
- IO操作包括从磁盘读取写入数据,网络发送或者接收数据。这些操作都需要一定时间来完成,因此可以被视为耗时操作。
- 计算机上是使用DMA来访问磁盘等IO,发出请求之后,cpu就不管了,直到DMA将磁盘内容(或者网络内容)加载到内存后,通过中断通知CPU完成了。所以单独的一个IO时间对CPU占用很少。但是IO的速度太慢了,远远慢于内存和CPU。虽然后是不会耗费CUP但是会阻塞进程直到IO操作完成,从而影响程序的执行效率和相应速度。
- 所以一般使用缓存、或者子线程处理IO完成后再通知主线程。