类加载器的的双亲委派机制:
启动类加载器没有父加载器,扩展类加载器的父加载器为启动类加载器,应用程序类加载器的父加载器为扩展类加载器。自定义加载器默认以getSystemLoader的加载器为父加载器,也就是以系统类加载器为父加载器。双亲委托是指当系统要加载某个类的时候,自己先不加载,让父加载器加载,父加载器再让他的父加载器进行加载,直到根类加载器没有父加载器,于是开始加载类,如果不能加载类,向下返回给子加载器加载,一直到有加载器可以加载 这个类,那么这个加载器成为定义类加载器,,能成功返回class对象的加载器称为初始类加载器。例如,用自定义加载器A去加载自己写的Sample类,这时候双亲委派的流程是:自定义加载器A请求系统类加载器加载,系统类加载器请求扩展类加载器加载,扩展类加载器请求根类加载器加载,由于根类加载器没有父加载器,根类加载器开始尝试加载,但是根类加载器加载的是rt.jar中的类,所以加载失败,返回失败信息给扩展类加载器,扩展类加载器尝试加载,但是扩展类加载器加载的是ext.jar中的类,同样加载失败,返回信息给系统类加载器,系统类加载器尝试加载Sample类,因为系统类加载器加载的是classpath路径的类,所以能够加载成功,所以真正加Sample类的加载器是系统类加载器,系统类加载器是定义类加载器,同样,他能返回Sample类的class对象,自定义类加载器A当然也能够返回,那么系统类加载器和自定义类加载器A是初始类加载器。
自定义加载器的代码:
package licaiwen.jvm.test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
public class MyClassLoader extends ClassLoader {
// 定义自定义类加载器的名字
private String classLoaderName;
// 定义扩展名(.class)
private String fileExtention = ".class";
// 构造函数
public MyClassLoader(String classLoaderName) {
super();// 不写也会默认调用,调用相同参数的父类的构造函数,默认夫类加载器是系统类加载器
this.classLoaderName = classLoaderName;
}
// 构造函数
public MyClassLoader(ClassLoader parent, String classLoaderName) {
super(parent);// 不写也会默认调用,调用相同参数的父类的构造函数,默认夫类加载器是系统类加载器
this.classLoaderName = classLoaderName;
}
// 将class文件转换成字节数组,参数name是要加载的类的二进制名
private byte[] loadClassData(String name) {
// 返回的字节数组
byte[] data = null;
// 输入流
InputStream in = null;
// 字节输出流
ByteArrayOutputStream bos = null;
try {
name = name.replace(".", "//");
in = new FileInputStream(new File(name + fileExtention));
int ch = 0;
while ((ch = in.read()) != -1) {
bos.write(ch);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
data = bos.toByteArray();
return data;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
MyClassLoader myloader1=new MyClassLoader("myloader1");
Class clazz=myloader1.loadClass("licaiwen.jvm.test.Test1");//loadClass方法返回的是参数中的类的Class对象,这个方法内部会调用findClass方法
System.out.println(clazz.getClassLoader());//输出Class对象对应的类的加载器,按照双亲委派机制,应该是有系统类加载器加载
Object object=clazz.newInstance();//Class对象调用newInstance()方法可以返回对应的类的实例
System.out.println(object);
}
}
需要注意的地方:
虽然自定义了一个加载器,但是实际上并没有用到这个加载器来加载Test1类,原因是双亲委派机制。Test1位于classpath路径下,实际是由myloader1的父加载器系统类加载器加载的Test1,如果将Test1放在不在classpath下,则由myloader1加载。原理如下:
在loadClass()方法中,含有以下步骤:
1,调用findLoadedClass(String)以检查类是否已经被加载。
2,在父类加载器上调用loadClass方法。 如果父级是null ,则使用虚拟机内置的类加载器。
3,调用findClass(String)方法来查找该类。
所以,并没有执行到自定义类加载器的findClass方法。