ClassLoader顾名思义就是用来加载Class文件到JVM,以供程序使用的,java程序可以动态加载类定义,而这个动态加载的机制就是通过ClassLoader来实现的。
ClassLoader的类型有很多,但是最重要的是Bootstrap Classloader,这个是启动类加载器,这个ClassLoader在JVM运行时候加载java的核心的API以满足java程序的基本需求,这就包括了其他的ClassLoader,其中包括用户自定义的ClassLoader和ExtClassLoader,还有一个是AppClassLoader。
其实,也就是说当运行一个程序的时候,JVM启动,运行bootstrap classloader,该ClassLoader加载java核心API(ExtClassLoader和AppClassLoader也在此时被加载),然后调用ExtClassLoader加载扩展API,最后AppClassLoader加载CLASSPATH目录下定义的Class,这就是一个程序最基本的加载流程。
在定义一个ClassLoader的时候都必须继承ClassLoader这个抽象类,而每个ClassLoader都会有一个parent ClassLoader,我们可以看一下ClassLoader这个抽象类中有一个getParent()方法,这个方法用来返回当前ClassLoader的parent,注意,这个parent不是指的被继承的类,而是在实例化该ClassLoader时指定的一个ClassLoader,如果这个parent为null,那么就默认该ClassLoader的parent是Bootstrap Classloader。
如果自定义了一个UserClassLoader,使用这个自定义的ClassLoader加载java.lang.String,那么这里String是否会被这个ClassLoader加载呢?事实上java.lang.String这个类并不是被这个UserClassLoader加载,而是由Bootstrap Classloader进行加载,这就是双亲委托模式机制,也就是在任何一个自定义ClassLoader加载一个类之前,它都会先委托它的父亲ClassLoader进行加载,只有当父亲ClassLoader无法加载成功后,才会由自己加载。
在ClassLoader中的一段源代码中有描述:
Java代码
protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { // 首先检查该name指定的class是否有被加载 Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { //如果parent不为null,则调用parent的loadClass进行加载 = parent.loadClass(name, false); } else { //parent为null,则调用BootstrapClassLoader进行加载 c = findBootstrapClass0(name); } } catch (ClassNotFoundException e) { //如果仍然无法加载成功,则调用自身的findClass进行加载 c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }
这种双亲委托模式可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。还有一个原因就是如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义类型,这样会存在非常大的安全隐患。
当ClassLoader加载class的时候会经过三步装载、连接、初始化。
其中装载就是找到相应的class文件,读入JVM;连接分三步,第一步是验证class是否符合规格,第二步是准备,就是为类变量分配内存同时设置默认初始值,第三步就是解释。初始化就是将类实例化成对象,以待其他应用程序使用。
其实ClassLoader就这样简单,知道了这些原理和理论,如果项目或者产品中遇到使用ClassLoader的情况就应该会非常轻松。