深入探讨 Java 类加载器

本文详细阐述了Java类加载器的工作原理,包括类加载器的分类、工作目录、代理模式以及加载类的过程。同时,提供了自定义类加载器的代码示例,帮助开发者深入了解并实践Java类加载机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ClassLoader 中与加载类相关的方法

方法

说明

getParent()

返回该类加载器的父类加载器

loadClass(String name)

加载名称为name的类,返回的结果是java.lang.Class类的实例

findClass(String name)

查找名称为name的类,返回的结果是java.lang.Class类的实例

findLoadedClass(String name)

查找名称为name的已经被加载过的类,返回的结果是java.lang.Class类的实例

defineClass(String name, byte[] b, int off, int len)

把字节数组b中的内容转换成 Java 类,返回的结果是java.lang.Class类的实例。这个方法被声明为final的

resolveClass(Class<?> c)

链接指定的 Java 类



系统提供的类加载器

•引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自java.lang.ClassLoader。
•扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
•系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java类。一般来说,Java 应用的类都是由它来完成加载的。可以通过ClassLoader.getSystemClassLoader()来获取它。

三个加载器工作的目录

•这三个加载器就构成我们的Java类加载体系。他们分别从以下的路径寻找程序所需要的类:
•BootstrapLoader:sun.boot.class.path
•ExtClassLoader:java.ext.dirs
•AppClassLoader:java.class.path
•这三个系统参量可以通过System.getProperty()函数得到具体对应的路径。
public class ClassLoaderPathTest {
	public static void main(String args[]) {
		try{
			   System.out.println("BootstrapLoader's Load path sun.boot.class.path="+
									System.getProperty("sun.boot.class.path"));
			   System.out.println("ExtClassLoader's Load path java.ext.dirs="+
									System.getProperty("java.ext.dirs"));
			   System.out.println("BootstrapLoader's Load path java.class.path="+
									System.getProperty("java.class.path"));
		}catch(Exception e){
			   e.printStackTrace();
		}
	}
}




类加载器的树状组织结构

•有些 JDK 的实现对于父类加载器是引导类加载器的情况,getParent()方法返回null



类加载器的代理模式

•类加载器在尝试自己去查找某个类的字节代码并定义它时,会先代理给其父类加载器,由父类加载器先去尝试加载这个类,依次类推。
•Java虚拟机不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同的情况,才认为两个类是相同的。即便是同样的字节代码,被不同的类加载器加载之后所得到的类,也是不同的。
•代理模式是为了保证 Java 核心库的类型安全。通过代理模式,对于 Java 核心库的类的加载工作由引导类加载器来统一完成,保证了 Java 应用所使用的都是同一个版本的 Java 核心库的类,是互相兼容的。

加载类的过程

•真正完成类的加载工作的类加载器和启动这个加载过程的类加载器,有可能不是同一个。真正完成类的加载工作是通过调用defineClass来实现的;而启动类的加载过程是通过调用loadClass来实现的。前者称为一个类的定义加载器(defining loader),后者称为初始加载器(initiating loader)。在Java 虚拟机判断两个类是否相同的时候,使用的是类的定义加载器。也就是说,哪个类加载器启动类的加载过程并不重要,重要的是最终定义这个类的加载器。两种类加载器的关联之处在于:一个类的定义加载器是它引用的其它类的初始加载器。如类com.example.Outer引用了类com.example.Inner,则由类com.example.Outer的定义加载器负责启动类com.example.Inner的加载过程。


开发自己的类加载器

•一般来说,自己开发的类加载器只需要覆写findClass(String name)方法即可。java.lang.ClassLoader类的方法loadClass()封装了前面提到的代理模式的实现。该方法会首先调用findLoadedClass()方法来检查该类是否已经被加载过;如果没有加载过的话,会调用父类加载器的loadClass()方法来尝试加载该类;如果父类加载器无法加载该类的话,就调用findClass()方法来查找该类。因此,为了保证类加载器都正确实现代理模式,在开发自己的类加载器时,最好不要覆写loadClass()方法,而是覆写findClass()方法。

自定义类加载器代码示例:

package classloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class FileSystemClassLoader extends ClassLoader {
	private String rootDir;

	public FileSystemClassLoader(String rootDir) {
		this.rootDir = rootDir;
	}

	protected Class<?> findClass(String name) throws ClassNotFoundException {
		byte[] classData = getClassData(name);
		if (classData == null) {
			throw new ClassNotFoundException();
		} else {
			return defineClass(name, classData, 0, classData.length);
		}
	}

	private byte[] getClassData(String className) {
		String path = classNameToPath(className);
		try {
			InputStream ins = new FileInputStream(path);
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			int bufferSize = 4096;
			byte[] buffer = new byte[bufferSize];
			int bytesNumRead = 0;
			while ((bytesNumRead = ins.read(buffer)) != -1) {
				baos.write(buffer, 0, bytesNumRead);
			}
			return baos.toByteArray();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	private String classNameToPath(String className) {
		return rootDir + File.separatorChar
				+ className.replace('.', File.separatorChar) + ".class";
	}
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值