简单的说,类加载器就是JVM用来把类加载到内存中的工具。
类加载器本质上也是Java类,所以,类加载器本身也需要被JVM来加载,那么谁来加载这些类加载器呢?在JVM中,系统默认有三个类加载器:BootStrap, ExtClassLoader, AppClassLoader。其中BootStrap是采用C++编写的一段二进制代码,它嵌套在JVM内核中,当JVM启动时,这个类就会被启动,JVM用它来加载其它的类加载器。
一、类加载器加载类的过程:
Javac ClassLoaderTest.java 将java文件编译成.class文件
二、类加载器的运行委托机制
委托机制:
3、BootstrapLoader(启动类加载器)是最顶级的类加载器了,其父加载器为null.
4、如果类A中引用了类B,Java虚拟机将使用加载类A的类加载器来加载类B。
5、当所有父类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛出ClassNotFoundException,不是再去找发起者类加载器的子类加载器
看下面一段代码:
package com.cndqe.test;
public class ClassLoaderTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO 自动生成的方法存根
ClassLoader loader = ClassLoaderTest.class.getClassLoader();
while(loader != null){
System.out.println(loader.getClass().getName());
loader = loader.getParent();
}
System.out.println(loader);
}
}
运行结果:
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
null
从上面的结果可以看出,AppClassLoader加载当前线程的类,而AppClassLoader的父加载器是ExtClassLoader,而ExtClassLoader的父类加载器是BootStrap,打印null的原因是Bootstrap(启动类加载器)是用C语言实现的,找不到一个确定的返回父Loader的方式,于是就返回null
三、类加载器之间的父子关系和管辖范围:
Java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织,在实例化每个类加载器对象时,需要为其指定一个父级类加载器对象或者默认采用系统类加载器为其父级类加载。
BootStrap -> ExtClassLoader -> AppClassLoader(即通常所说的System ClassLoader)
它们的管辖范围依次是:
BootStrap------>JRE/lib/rt.jar
ExtClassLoader---------->JRE/lib/ext/*.jar
AppClassLoader---------->CLASSPATH指定的所有jar或目录。
四、类加载的三种方式
五、自定义类加载器
1.自定义的类加载器必须继承ClassLoader(抽象类),并且重写findClass()方法;(除了Bootstrap以外,所有的类加载器都要继承ClassLoader)
2.调用继承自ClassLoader的方法 loadClass(String name),此
方法返回一个name对应的Class对象。在loadClass()内部执行过程中,loadClass()方法内部采用委托机制,向父加载器上交任务,当父加载器找不到资源时,loadClass()内部会调用自定义类加载器所重写的findClass()方法。
3.将要加载的类读取成二进制数据的byte数组
4.defineClass(String name, byte[ ] , int off, int len);功能:将一个byte数组转换成一个Class类的实例
自定义一个类加载器MyClassLoader,加载类Test,代码如下:
图为测试项目架构图:
Test.class是Test.java编译后的class文件,测试期间,为了让我们自定义的类加载器加载Test.java这个,我们写一个不带包名的Test.java编译后,在项目根目录下新建一个文件夹,存放编译后的Test.class文件
代码:
package com.classloader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
public class MyClassLoader extends ClassLoader {
private String name;
private String path = "E:\\gaotong\\workspace\\test\\testlib\\";
public MyClassLoader(String name) {
this.name = name;
}
public MyClassLoader(ClassLoader parent, String name) {
super(parent);
this.name = name;
}
/**
* 重写findClass()方法
*/
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
byte[] data = loadClassData(name);
return this.defineClass(name, data, 0, data.length);
}
public byte[] loadClassData(String name) {
InputStream is = null;
ByteArrayOutputStream os = null;
try {
name = name.replace(".", "//");
is = new FileInputStream(new File(path + name + ".class"));
os = new ByteArrayOutputStream();
int i = 0;
while ((i = is.read()) != -1) {
os.write(i);
}
return os.toByteArray();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
if (os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/**
* @param args
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
*/
public static void main(String[] args) throws ClassNotFoundException,
InstantiationException, IllegalAccessException {
MyClassLoader loader = new MyClassLoader("myClassLoader");
Class<?> clazz = loader.loadClass("Test");
Date tt = (Date) clazz.newInstance();
System.out.println(tt.toString());
ClassLoader loader2 = tt.getClass().getClassLoader();
while(loader2 != null){
System.out.println(loader2.getClass().getName());
loader2 = loader2.getParent();
}
}
}
import java.util.Date;
public class Test extends Date{
@Override
public String toString() {
return "Hello,I am Test!";
}
}
运行结果 :
Hello,I am Test!
com.classloader.MyClassLoader
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
六、Java WebProject下的类加载器加载过程
Tomcat自定义了一些类加载器,当Tomcat启动时,会自动把Tomcat定义的类加载器加入到JVM中,在Servlet中加载Classpath下的类,一般情况下,类加载器的委托顺序:
org.apache.catalina.loader.WebappClassLoader
org.apache.catalina.loader.StandardClassLoader
org.apache.Launcher$AppClassLoader
org.apache.Launcher$ExtClassLoader
null
参考资料1:http://hi.baidu.com/sonmeika/item/3323651fa2c722f865eabf17
参考资料2:http://hi.baidu.com/sonmeika/item/3323651fa2c722f865eabf17