JVM在运行时会产生三个ClassLoader
1.Bootstrap ClassLoader
c++编写的启动JVM时调用的类加载器,主要用来加载JRE_HOME/lib当前目录下的核心jar,例如:rt.jar,jsse.jar等。
2.Extension ClassLoader
用来加载JRE_HOME/lib/ext当前目录下核心扩展的jar,例如:dnsns.jar等
3.AppClassLoader
AppClassLoader是加载Classpath下所有的jar和classes
这3种ClassLoader的优先级依次从高到低,使用所谓的“双亲委派模型”。确切地说,如果一个类装载器被请求装载一个java.lang.Integer,它会首先把请求发送给上一级的类路径装载器,如果返回已装载,则该类装载器将不会装载这个java.lang.Integer,如果上一级的类路径装载器返回未装载,它才会装载java.lang.Integer。
类似的,类路径装载器收到请求后(无论是直接请求装载还是下一级的ClassLoader上传的请求),它也会先把请求发送到上一级的标准扩展类装载器,这样一层一层上传,于是Bootstrap ClassLoader优先级最高,如果它按照自己的方式找到了java.lang.Integer,则下面的ClassLoader 都不能再装载java.lang.Integer,尽管你自己写了一个java.lang.Integer,试图取代核心库的java.lang.Integer是不可能的,因为自己写的这个类根本无法被下层的ClassLoader装载。这也是所谓的“沙箱”原理。
public class TestLoader {
public static void main(String[] args) {
Class clazz = null;
ClassLoader clazzLoader = ClassLoader.getSystemClassLoader();
System.out.println("ClassLoader.getSystemClassLoader() = " + clazzLoader);
while (clazzLoader != null) {
clazzLoader = clazzLoader.getParent();
System.out.println(clazzLoader);
}
System.out.println("end while.");
try {
clazz = Class.forName("java.lang.Object");
clazzLoader = clazz.getClassLoader();
System.out.println(" java.lang.Object's loader is " + clazzLoader);
clazz = Class.forName("sun.net.spi.nameservice.dns.DNSNameService");
clazzLoader = clazz.getClassLoader();
System.out.println(" sun.net.spi.nameservice.dns.DNSNameService's loader is "
+ clazzLoader);
clazz = Class.forName("test.TestLoader");
clazzLoader = clazz.getClassLoader();
System.out.println(" test.TestLoade's loader is " + clazzLoader);
} catch (Exception e) {
e.printStackTrace();
}
}
}