一、类加载器简介

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方法。