转载请注明出处:jiq•钦's technical Blog - 季义钦
虚拟机中加载类需要经历“加载、验证、准备、解析和初始化”五个阶段。
其中加载阶段是“通过一个类的全限定名来获取描述此类的二进制字节流”,实现这个动作的代码模块称为“类加载器”。
JVM类加载器分为两类,一类是启动类加载器BootStrap ClassLoader,由C++语言实现,是JVM的一部分,一类是其他类加载器,由Java语言实现,独立于虚拟机外部,并且都继承自抽象类java.lang.ClassLoader。
1.启动类加载器BootstrapClassLoader:加载
2.拓展类加载器ExtensionClassLoader:加载
3.应用程序类加载器ApplicationClassLoader:加载classpath路径下的class。
下面用一个例子实际看一下各个类加载器所加载的类库列表:
package jiq.jvm;
import java.net.URL;
import java.net.URLClassLoader;
public classClassLoaderTest {
public static void main(String[] args)
{
//启动类加载器Bootstrap ClassLoader
System.out.println("=== 启动类加载器Bootstrap ClassLoader加载的类库: ");
URL[] paths = sun.misc.Launcher.getBootstrapClassPath().getURLs();//需手动增加rt.jar到 BuildPath
for(URL path : paths)
System.out.println(path);
//拓展类加载器Extension ClassLoader
URLClassLoader extClassLoader =(URLClassLoader)ClassLoader.getSystemClassLoader().getParent();
System.out.println("=== 扩展类加载器"+extClassLoader+"加载的类库: ");
paths = extClassLoader.getURLs();
for(URL path : paths)
System.out.println(path);
//应用程序类加载器ApplicationClassLoader
URLClassLoader appClassLoader =(URLClassLoader)ClassLoader.getSystemClassLoader();
System.out.println("=== 应用程序类加载器"+appClassLoader+"加载的类库: ");
paths = appClassLoader.getURLs();
for(URL path : paths)
System.out.println(path);
}
}
输出如下:
=== 启动类加载器Bootstrap ClassLoader加载的类库:
file:/D:/Java/jdk1.7.0_45/jre/lib/resources.jar
file:/D:/Java/jdk1.7.0_45/jre/lib/rt.jar
file:/D:/Java/jdk1.7.0_45/jre/lib/sunrsasign.jar
file:/D:/Java/jdk1.7.0_45/jre/lib/jsse.jar
file:/D:/Java/jdk1.7.0_45/jre/lib/jce.jar
file:/D:/Java/jdk1.7.0_45/jre/lib/charsets.jar
file:/D:/Java/jdk1.7.0_45/jre/lib/jfr.jar
file:/D:/Java/jdk1.7.0_45/jre/classes
=== 扩展类加载器sun.misc.Launcher$ExtClassLoader@2a788b76加载的类库:
file:/D:/Java/jdk1.7.0_45/jre/lib/ext/access-bridge-64.jar
file:/D:/Java/jdk1.7.0_45/jre/lib/ext/dnsns.jar
file:/D:/Java/jdk1.7.0_45/jre/lib/ext/jaccess.jar
file:/D:/Java/jdk1.7.0_45/jre/lib/ext/sunec.jar
file:/D:/Java/jdk1.7.0_45/jre/lib/ext/sunjce_provider.jar
file:/D:/Java/jdk1.7.0_45/jre/lib/ext/sunmscapi.jar
file:/D:/Java/jdk1.7.0_45/jre/lib/ext/zipfs.jar
file:/D:/Java/jdk1.7.0_45/jre/lib/ext/localedata.jar
=== 应用程序类加载器sun.misc.Launcher$AppClassLoader@5552bb15加载的类库:
file:/E:/jiq_src/java/Eclipse_WrokSpace/JavaTest/bin/
file:/D:/Java/jre7/lib/rt.jar //注意这是我手动加到buildpath的
注:图片来自网络
图中展示的这种类加载器之间的层次关系,就称为类加载器的双亲委派模型(Parents Delegation Model),要求除顶层的启动加载器外,其余的所有类加载器都应该有自己的父类加载器。这里的类加载器之间的父子关系不是继承(Inheritance),而是组合(Composition)以复用父类代码。
原理:如果一个类加载器收到一个类加载请求,它首先不会自己尝试加载此类,而是先把加载请求委派给父类加载器去完成。所有加载请求最终都会传递到顶层的启动类加载器,当父类反馈无法响应此加载请求时,子类才会自己去加载。
保障加载的类class文件的唯一性!!!
类的唯一性:对于任意一个类,需要由加载它的类加载器和这个类本身一同确立其在JVM中的唯一性,也就是说即使两个类来自同一个class文件,被同一个虚拟机加载,但是主要加载它们的类加载器不同,则这两个类必然不相等。
由此如果类加载器尝试加载一个类的时候不先委派给父类自己就加载了,因为不同类加载器加载的类的唯一性,就会发生多个类加载器加载了多个同名的类的情况。比如用户可以自己编写一个java.lang.Object类放在classPath中,Bootstrap ClassLoader加载了正宗的Object类,而Application ClassLoader加载了这个冒牌的Object类,那么系统中同时出现了多个不同的Object类,使得java类型体系中最为基础的行为都无法得到保证。