深入理解JVM内核|类加载器的双亲委派和自定义类加载器

类加载器的的双亲委派机制:
在这里插入图片描述
在这里插入图片描述
启动类加载器没有父加载器,扩展类加载器的父加载器为启动类加载器,应用程序类加载器的父加载器为扩展类加载器。自定义加载器默认以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方法。

猜你喜欢

转载自blog.csdn.net/qq_44280408/article/details/106854841