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