Java的类加载器

1、类加载器负责将.class文件(可能在磁盘上,也可能在网络上)加载到内存中,并为之生成一个java.lang.Class对象。
  JVM在运行时会产生三个ClassLoader:根装载器、扩展类装载器(ExtClassLoader)、系统应用类装载器(AppClassLoader)。
  现分别对这几个类的装载器说明如下:
    (1)、根装载器(bootstrap class Loader):计算机本地语言所编写(汇编、C/C++),加载jre最核心的类,如JRE扩展目标下的rt.jar、charsets.jar...,如java.lang.String类;

import java.net.URL;

/**
 * @Description:根类加载器
 * @ClassName: BootStartTest
 * @Project: base-info
 * @Author: zxf
 * @Date: 2011-7-19
 */
public class BootStartTest {

	public static void main(String[] args) {
		URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
		// 遍历、输出根加载器加载的全部URL
		for (URL u : urls) {
			System.out.println(u.toExternalForm());
		}
	}
}

    (2)、扩展类装载器(extesion class loader):加载jre/lib/ext包里的jar类,如;
    (3)、系统应用类装载器(applicatioin class loader):负责加载classpath路径下的包类,通常是用户定义的类,也是系统默认的装载方式;

import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;

/**
 * @Description:
 * @ClassName: ClassLoaderProTest
 * @Project: base-info
 * @Author: zxf
 * @Date: 2011-7-19
 */
public class ClassLoaderProTest {

	public static void main(String[] args) throws IOException {
		ClassLoader sysClassLoader = ClassLoader.getSystemClassLoader();
		System.out.println("系统类加载器:" + sysClassLoader);

		// 系统类加载器加载的路径
		Enumeration<URL> enums = sysClassLoader.getResources("");
		while (enums.hasMoreElements()) {
			System.out.println(enums.nextElement());
		}
		ClassLoader extClassLoader = sysClassLoader.getParent();
		System.out.println("扩展类加载器:" + extClassLoader);
		System.out.println("扩展类加载器加载路径:" + System.getProperty("java.ext.dirs"));
		ClassLoader bootClassLoader = extClassLoader.getParent();
		System.out.println("根类加载器:" + bootClassLoader);
	}
}
 

2、JVM类加载机制主要有以下三种机制:

   (1)、全盘负责:所谓全盘负责就是当一个类加载器负责加载一个Class的时候,该Class所依赖和引用的Class类也将由该类加载器载入。除非显式的指定使用另外一个类加载器来载入。

   (2)、父类委托:所谓父类委托就是先让parent(父)类先加载器视图先加载该Class,只有当父类加载无法加载该Class类时,才尝试自己从类路径加载该Class类。

   (3)、缓存机制:缓存机制会保证每个被类加载器加载过的Class类都会被缓存,当程序使用某个Class类时,首先会去缓存中查找该Class类,只有当缓存中不存在该Class类时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,并存入缓存。这就是我们修改了Class类,要重新启动JVM,所修改的程序才生效的原因。

 

3、创建并使用自定义类加载器:

     JVM中除了根加载器之外,其它加载器都是classLoader子类的实例。开发者可以通过扩展ClassLoader类的子类,并重写ClassLoader所包含的方法来实现自定义的类加载器。

/**
 * @Description:
 * @ClassName: A
 * @Project: base-info
 * @Author: zxf
 * @Date: 2011-5-25
 */
public class A {
	public String print(String n) {
		System.out.println("带参数带返回值的方法!");
		return n + "哈哈大笑";
	}

	public void print() {
		System.out.println("不带参数不带返回值的方法!");
	}
}
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;

/**
 * @Description:编写自己的类加载器
 * @ClassName: FileSystemClassLoader
 * @Project: base-info
 * @Author: zxf
 * @Date: 2011-6-22
 */
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";
	}

	public static void main(String args[]) {
		// A.class存放的目录包括com.yt.manager.classloader
		String classDataRootPath = "E:\\WorkSpaces";
		FileSystemClassLoader fscl1 = new FileSystemClassLoader(
				classDataRootPath);
		String className = "com.yt.manager.classloader.A";
		try {
			// 加载并初始化类
			Class<?> class1 = fscl1.loadClass(className);
			// 利用反射机制调用了print方法
			Method m = class1.getMethod("print");
			// class1.newInstance()实例化了A类,调用了A类对象的print方法。
			m.invoke(class1.newInstance());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

  ClassLoader类有如下三个主要方法:

  (1)、loadClass(String name,String resolve):该方法为ClassLoader的入口点,根据指定的二进制名称来加载类,系统就是调用ClassLoader的该方法来加载类的。

  (2)、findClass(String name):根据二进制名称来加载类。

注:如果需要实现自定义的ClassLoader,推荐使用findClass(String name)方法,因为该方法继承了父类委托和缓存机制的类加载机制。

   在ClassLoader中还有一个核心的方法:defineClass(String name,byte[] b,int off,int len),该方法负责将类的字节文件读入数组,并把它转化为Class对象,该字节码文件可以来源于网络、文件等。

 

参考文章:http://www.ibm.com/developerworks/cn/java/j-lo-classloader/index.html

你可能感兴趣的:(java,jvm,c,虚拟机,ext)