【类的加载】
Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的加载机制。
双亲委派模型工作过程
如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载中这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在及其的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载。
整个大致过程如下:
首先,检查下制定名称的类是否已经加载过,如果加载过了,就不需要再加载,直接返回。
如果此类没有加载过,那么,再判断下是否有父加载器;如果有父加载器,则由父加载器加载(即调用parent.ladClass(name,false);)或者是调用bootstrap类加载器来加载。
如果父加载器及bootstrap类加载器都没有找到指定的类,那么调用当前类加载器的findClass方法来完成类加载。
换句话说,如果自定义类加载器,就必须重写findClass方法!
find Class
findClass的默认实现如下
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
可以看出,抽象类ClassLoader的findClass函数默认是抛出异常的。而前面我们知道,loadClass在父加载器无法加载类的时候,就会降低用我们自定义的类加载器中的findClass函数,因此我们必须在loadClass这个函数里面实现将一个指定类名称转换为class对象。
如果是读取一个指定的名称的类为字节数组的话,这个很好办。但是如何将字节数组转为class对象呢?很简单,Java提供了defineClass方法,通过这个方法,就可以把一个字节数组转为Class对象了。
defineClass
defineClass主要的功能是:
将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组。如,假设class文件时加密过的,则需要解密后作为形参传入defineClass函数。
defineClass默认实现如下:
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
throws ClassFormatError {
return defineClass(name, b, off, len, null);
}
2.2 函数调用过程
![](https://img.haomeiwen.com/i209316/648f1ff6c9942727.png)