类加载器

751 阅读3分钟

什么是类加载器

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。

学习于:第05讲:深入理解 ClassLoader 的加载机制

记一次ClassLoader发现之旅(详细)