ClassLoader详解

1.Java中的ClassLoader

1.1 CLassLoader的类型

Java的类加载器主要有两种类型,即系统类加载器和自定义类加载器。系统类加载器包括3中,分别是Bootstrap ClassLoader,Extensions ClassLoader和Application ClassLoader。

Bootstrap ClassLoader(引导类加载器)

C/C++代码实现的加载器,用于加载指定的JDK的核心类库,比如java.lang.等系统类。用来加载一下目录的类库:

  • %JAVA_HOME%/jre/lib目录
  • -Xbootclasspath参数指定的目录

Java虚拟机的启动就是通过Bootstrap ClassLoader创建一个初始类来完成的。由于Bootstrap ClassLoader使用C/C++实现的,不能被Java代码访问到。Bootstrap ClassLoader也不是继承java.lang.ClassLoader。可以通过如下代码得出Bootstrap ClassLoader加载的目录:

System.out.println(System.getProperty("sun.boot.class.path"));

Extensions ClassLoader(拓展类加载器)
Java中类对应是ExtClassLoader,用于加载Java的拓展类,提供除了系统类之外的额外功能,加载以下目录的类库:

  • %JAVA_HOME%/jre/lib/ext
  • 系统属性java.ext.dir所指定的目录

通过以下代码可以加载Extensions ClassLoader加载目录:

System.out.println(System.getProperty("java.ext.dirs"));

Application ClassLoader(应用程序类加载器)
Java中实现类为AppClassLoader,可以通过ClassLoader的getSystemClassLoader获取到。用来加载以下目录的类库:

  • 当前程序的ClassPath目录
  • 系统属性java.class.path指定的目录

Custom ClassLoader(自定义类加载器)
通过继承java.lang.ClassLoader来实现自定义的类加载器。ExtClassLoader和AppClassLoader也是继承了java.lang.ClassLoader类。

1.2 CLassLoader的继承关系

ClassLoader的继承关系如下所示:
在这里插入图片描述

  • ClassLoader是一个抽象类,其中定义了ClassLoader的主要功能。
  • SecureClassLoader继承了抽象类ClassLoader,但SecureClassLoader并不是ClassLoader的实现类,而是拓展了ClassLoader类加入了权限方面的功能,加强了ClassLoader的安全性。
  • URLClassLoader继承自SecureClassLoader,用来通过URl路径从jar文件和文件夹中加载类和资源。
  • ExtClassLoader和AppClassLoader都继承自URLClassLoader,它们都是Launcher
    的内部类,Launcher
  • 是Java虚拟机的入口应用,ExtClassLoader和AppClassLoader都是在Launcher中进行初始化的。

1.3 双亲委托模式

类加载器查找Class所采用的是双亲委托模式,所谓双亲委托模式就是首先判断该Class是否已经加载,如果没有则不是自身去查找而是委托给父加载器进行查找,这样依次的进行递归,直到委托到最顶层的Bootstrap ClassLoader,如果Bootstrap ClassLoader找到了该Class,就会直接返回,如果没找到,则继续依次向下查找,如果还没找到则最后会交由自身去查找。加载过程如下图所示:
在这里插入图片描述
这种加载方式可以从源码中分析:

public abstract class ClassLoader {

...

	protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
    {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);   //1
            if (c == null) {
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);  //2
                    } else {
                        c = findBootstrapClassOrNull(name);  //3
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    c = findClass(name);
                }
            }
            return c;
    }
...

}

注释1处用来检查类是否已经加载,如果已经加载则后面的代码不会执行,最后会返回该类。没有加载则会接着向下执行。
注释2处,如果父类加载器不为null,则调用父类加载器的loadClass方法。如果父类加载器为null则调用注释3处的findBootstrapClassOrNull方法,这个方法内部调用了Native方法findBootstrapClass,findBootstrapClass方法中最终会用Bootstrap Classloader来查找类。如果Bootstrap Classloader仍没有找到该类,也就说明向上委托没有找到该类,则调用注释4处的findClass方法继续向下进行查找。

双亲委托模式的好处
1.避免重复加载,如果已经加载过一次Class,就不需要再次加载,而是先从缓存中直接读取。
2.更加安全,如果不使用双亲委托模式,就可以自定义一个String类来替代系统的String类,这显然会造成安全隐患,采用双亲委托模式会使得系统的String类在Java虚拟机启动时就被加载,也就无法自定义String类来替代系统的String类,除非我们修改类加载器搜索类的默认算法。还有一点,只有两个类名一致并且被同一个类加载器加载的类,Java虚拟机才会认为它们是同一个类。

2.Android中的ClassLoader

2.1 ClassLoader的类型

Java中的ClassLoader是加载Class文件,而Android中的ClassLoader是加载dex文件,所以两者并不相同。
Android中的ClassLoader包括系统类加载器和自定义类加载器。系统类加载器包含:BootClassLoader、PathClassLoader和DexClassLoader。

BootClassLoader
BootClassLoader是ClassLoader的内部类,继承自ClassLoader,它的访问修饰符默认的,只有在同一个包中才可以访问,因此我们在应用程序中是无法直接调用的

PathClassLoader
PathClassLoader用来加载系统类和应用程序的类。

public class PathClassLoader extends BaseDexClassLoader {

public PathClassLoader(String dexPath, ClassLoader parent) {
       super(dexPath, null, null, parent);
}

public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
        super(dexPath, null, librarySearchPath, parent);
}

}

PathClassLoader继承自BaseDexClassLoader,实现也都在BaseDexClassLoader中。PathClassLoader的构造方法中没有参数optimizedDirectory,这是因为PathClassLoader已经默认了参数optimizedDirectory的值为:/data/dalvik-cache,很显然PathClassLoader无法定义解压的dex文件存储路径,因此PathClassLoader通常用来加载已经安装的apk的dex文件(安装的apk的dex文件会存储在/data/dalvik-cache中)。

DexClassLoader
DexClassLoader可以加载dex文件以及包含dex的压缩文件(apk和jar文件),不管是加载哪种文件,最终都是要加载dex文件,为了方便理解和叙述,将dex文件以及包含dex的压缩文件统称为dex相关文件。

public class DexClassLoader extends BaseDexClassLoader {
    public DexClassLoader(String dexPath, String optimizedDirectory,
            String librarySearchPath, ClassLoader parent) {
        super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
	    }
}

DexClassLoader的构造方法有四个参数:

dexPath:dex相关文件路径集合,多个路径用文件分隔符分隔,默认文件分隔符为‘:’
optimizedDirectory:解压的dex文件存储路径,这个路径必须是一个内部存储路径,一般情况下使用当前应用程序的私有路径:/data/data//…。不过在Android8.0及以上的源码中,在BaseDexClassLoader中可以看到,这个参数已被取消并且无效。
librarySearchPath:包含 C/C++ 库的路径集合,多个路径用文件分隔符分隔分割,可以为null。
parent:父加载器。

在android8.0以上,optimizedDirectory可以不传,所以PathClassLoader和DexClassLoader均可以用来加载外部apk(如果要访问sd卡,注意添加权限)。

2.1 ClassLoader的继承关系

Android中ClassLoader的继承关系如下所示:
在这里插入图片描述

  • ClassLoader是一个抽象类,其中定义了ClassLoader的主要功能。BootClassLoader是它的内部类。
  • SecureClassLoader类和JDK8中的SecureClassLoader类的代码是一样的,它继承了抽象类ClassLoader。SecureClassLoader并不是
  • ClassLoader的实现类,而是拓展了ClassLoader类加入了权限方面的功能,加强了ClassLoader的安全性。
  • URLClassLoader类和JDK8中的URLClassLoader类的代码是一样的,它继承自SecureClassLoader,用来通过URl路径从jar文件和文件夹中加载类和资源。
  • InMemoryDexClassLoader是Android8.0新增的类加载器,继承自BaseDexClassLoader,用于加载内存中的dex文件。
  • BaseDexClassLoader继承自ClassLoader,是抽象类ClassLoader的具体实现类,PathClassLoader和DexClassLoader都继承它。

总结

本篇文章分析了Java的ClassLoader的类型以及继承关系以及Android的ClassLoader的类型以及继承关系。

参考链接:
https://blog.csdn.net/itachi85/article/details/78088701
https://blog.csdn.net/itachi85/article/details/78276837

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值