一、类加载器简介

java中自带的类加载器可以分为根类加载器(BootStrap classloader),扩展类加载器,应用类加载器,这三个都不是用java语言实现的。

其中根类加载器和扩展类加载器用来加载java自带的一些类,而应用类加载器用来加载我们自己写的java类编译后的class文件,也就是classpath中的class文件。

类加载器在java中的继承体系是这样的,顶层父类是ClassLoader

注意扩展类加载器和APPClassloader实际不是用java语言实现的。自定义类加载器时可以继承UrlClassLoader

类加载器加载资源的时候采用的是双亲委派机制:1.每个类加载器对它加载过的类都会缓存,

2.向上委托查找,向下委托加载。只有它的父亲中找不到某个类时才会自己去加载某个类

所以加载某个类时是按这样的顺序去加载

要注意类加载之间的父子关系不是通过继承实现的,是通过持有parent属性实现的

BootStrap classloader ==> ExtClassLoader ==> AppClassLoader,

AppClassLoader中持有的parent属性是ExtClassLoader ,

ExtClassLoader中持有的parent属性是BootStrap classloader,也就是根类加载器

BootStrap classloader中持有的parent属性是null

二. ClassLoader中方法介绍

ClassLoader是类加载器的顶层抽象类,其中定义了类加载的一些通用方法

AppClassLoader,ExtClassLoader,还有自定义类加载器都会继承它

2.1 loadClass(String name, boolean resolve)

这方法是双亲委派机制的实现,

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            //某个类加载器从自己的缓存中看是否加载过某个类
            Class<?> c = findLoadedClass(name);
            //没加载过,向上委托父类查找
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                	//这一块是双亲委派,让父加载器查找的实现
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                       //让根类加载器去查找类
                        c = findBootstrapClassOrNull(name);
                    }
                } 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.
                    //走到这里表示在父加载器中没找到,需要让子加载器加载
                    long t1 = System.nanoTime();
                    //调用自身的findClass方法去加载类
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

2.2 findClass(String name)

这个方法在ClassLoader中直接抛出异常,需要让子类去重写它实现自己的加载逻辑

protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
}

一个具体类加载器的findClass方法一般会先加载类的文件得到字节数组,然后调用defineClass方法转成Class对象返回,类似下面这样,这个defineClass是ClassLoader中的方法

public Class findClass(String name) {
    //读取文件得到字节数组
    byte[] b = loadClassData(name);
    //把字节数组转成Class文件,该方法定义在ClassLoader中
    return defineClass(name, b, 0, b.length);
}

2.3 defineClass

defineClass方法用来把自己数组转成Class对象,提供给findClass方法调用

三、自定义类加载

所以我们自定义类加载时,一般会继承ClassLoader,然后重写其findClass方法,这样不会破坏双亲委派机制,如果需要打破双亲委派机制则需要重写loadClass方法。