什么是类加载器
class文件需要被加载到jvm才能使用,而负责加载这些class文件的就是类加载器(ClassLoader)
类加载时机
-
调用类的构造器(如:new一个对象,初始化子类,反射)
-
访问类的静态(static)变量或者方法
jvm内置的类加载器
JVM中自带3个类加载器:
- 启动类加载器BootClassLoader
- 扩展类加载器ExtClassLoader(JDK1.9之后,改名为PlatformClassLoader)
- 系统加载器(APPClassLoader)
BootClassLoader
由C++语言编写,不是ClassLoader的子类。
它属于虚拟机的一部分,java代码是无法获取到它的引用的
它用来加载 Java 的核心库,载系统属性“sun.boot.class.path”配置下类文件
比如8个基本类型对应的封装类就是通过BootClassLoader加载的
BootClassLoader
继承URLClassLoader
加载的文件:加载系统属性“java.ext.dirs”配置下类文件
APPClassLoader
加载的文件;加载系统属性“java.class.path”配置下类文件,也就是环境变量 CLASS_PATH 配置的路径
因此 AppClassLoader 是面向用户的类加载器,我们自己编写的代码以及使用的第三方 jar 包通常都是由它来加载的。
实践例子:
public class AppClassLoaderTest {
public static void main(String[] args) {
ClassLoader classLoader = Test.class.getClassLoader();
System.out.println(classLoader);
System.out.println(classLoader.getParent());
}
private static class Test {
}
}
执行结果:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@1540e19d
双亲委派模式
可以看到 AppClassLoader 传入的 parent 就是 ExtClassLoader,而 ExtClassLoader 并没有传入任何 parent,也就是 null。
所谓双亲委派模式就是,当类加载器收到加载类或资源的请求时,通常都是先委托给父类加载器加载,也就是说,只有当父类加载器找不到指定类或资源时,自身才会执行实际的类加载过程。
优点
- 避免重复加载
- 安全,java核心api中定义类型(如String)不会被替换,防止被篡改。
自定义类加载器
当现有ClassLoader无法满足我们当需求时,就得自定义类加载器(比如加载网络的一个class文件)
通过继承java.lang.ClassLoader 类,来自定义类加载器,如果想保持双亲委派就重写findClass(name) 方法;如果想破坏双亲委派模型,可以重写 loadClass(name) 方法。
自定义ClassLoader步骤:
- 自定义一个类继承抽象类ClassLoader。
- 重写findClass方法。
- 在findClass中,调用 defineClass 方法将字节码转换成 Class 对象,并返回。
动态加载调用:
- 类加载器加载指定的class文件
- 生成的class,反射调用
android中的ClassLoader
在Aandroid虚拟机无法直接加载的class文件,会将这些class文件转换成一个dex文件,将加载dex文件的实现封装在BaseDexClassLoader中,而我们一般只使用它的两个子类:PathClassLoader 和 DexClassLoader。
PathClassLoader
用来加载apk和apk内的dex文件。
比如MainActivity就是通过PathCclassLoader加载的
实践检测:
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import com.example.sduty.reflect.ReflectPerson;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ReflectPerson reflectPerson = new ReflectPerson();
ClassLoader classLoader = reflectPerson.getClass().getClassLoader();
ClassLoader classLoader1 = MainActivity.this.getClass().getClassLoader();
System.out.println("classLoader --classLoader=" + classLoader);
System.out.println("classLoader --classLoader=" + classLoader);
String xx ="sss";
System.out.println("String --classLoader=" + xx.getClass().getClassLoader());
}
}
执行结果
DexClassLoader
DexClassLoader没有PathClassLoader的限制。这也是插件化和热修复的基础
使用DexClassLoader实现热修复
将正确的java文件打包成jar文件,再通过dx工具将其优化成dex文件。保存到指定目录,再通过DexClassLoader加载。
扩展
一个运行的Android应用至少有2个ClassLoader。
public class MainActivity extends Activity {
private String TAG = “ClassLoader”;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ClassLoader classLoader = getClassLoader();
if (classLoader != null){
Log.d(TAG, " classLoader: " + classLoader.toString());
while (classLoader.getParent()!=null){
classLoader = classLoader.getParent();
Log.d(TAG, " classLoader: " + classLoader.toString());
}
}
}
}
会输出2个ClassLoader,
dalvik.system.PathClassLoader。
java.lang.BootClassLoader。
由此也可以看出,一个运行的Android应用至少有2个ClassLoader。