浅析java类加载器ClassLoader
作为一枚java猿,了解类加载器是有必要的,无论是针对面试还是自我学习。
本文从JDK提供的ClassLoader、委托模型以及如何编写自定义的ClassLoader三方面对ClassLoader做一个简要的总结。
JDK中提供的ClassLoader
1. Bootstrap ClassLoader
Bootstrap加载器是用C++语言写的,它是在Java虚拟机启动后初始化的,它主要负责加载%JAVA_HOME%/jre/lib以及%JAVA_HOME%/jre/classes中的类,是最顶级的ClassLoader。
2. Ext ClassLoader
Ext ClassLoader是用java写的,且它的父加载器是Bootstrap,具体来说就是sun.misc.Launcher$ExtClassLoader,Ext ClassLoader主要加载%JAVA_HOME%/jre/lib/ext,此路径下的所有classes目录以及java.ext.dirs系统变量指定的路径中的类库。
3. App ClassLoader
系统类加载器,负责加载应用程序classpath目录下的所有jar和class文件。它的父加载器为Ext ClassLoader。
具体关系如下图:

委托模型
进入官方的Java doc里看到ClassLoader类的一段说明:
The ClassLoader class uses a delegation model to search for classes and resources. Each instance of ClassLoader has an associated parent class loader. When requested to find a class or resource, a ClassLoader instance will delegate the search for the class or resource to its parent class loader before attempting to find the class or resource itself. The virtual machine's built-in class loader, called the "bootstrap class loader", does not itself have a parent but may serve as the parent of a ClassLoader instance.
ClassLoader类使用一种委托模型来查找类和资源。每个ClassLoader实例都会关联1个父ClassLoader。当需要查询类和资源的时候,一个ClassLoader实例在查询类或资源之前会先委托给它的父ClassLoader去查询。Bootstrap ClassLoader是最顶层的加载器,并且可以作为其它ClassLoader实例的父ClassLoader。
由此看见,这个“委托模型”的安全性是很高的,Bootstrap是最顶层的加载器,这样比如加载 java.lang.String 的时候,永远都会被Bootstrap加载(Bootstrap ClassLoader会加载%JAVA_HOME%/jre/lib中rt.jar里的String类)。 这样用户自定义的java.lang.String永远都不会被加载,这样就避免了多个java.lang.String造成的混乱现象。
下面通过jdk里的ClassLoader源码来验证一下查找过程:
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
Class c = findLoadedClass(name); //先查找这个类是否已经加载过,每个加载器都有自己的缓存
if (c == null) {
try {
if (parent != null) { //父加载器存在的话先使用父加载器加载
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name); //没有父加载器的话使用bootstrap加载
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name); //如果父加载器没有找到,那么自身查找
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
通过代码看,这里的查找过程符合委托模型。
如何编写自定义的ClassLoader
编写自定义的ClassLoader注意2点即可:
1. 想遵循委托模型的话重写findClass方法即可。
2. 不遵循委托模型的话重写loadClass。
其他:defineClass方法把字节数组b中的内容转换成Java 类,返回的结果是 java.lang.Class类的实例。这个方法被声明为final的。该方法也是jvm预留给我们处理ClassLoader与类文件关系的入口。
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
throws ClassFormatError
下面就来写一个基于文件系统的ClassLoader:
先来看下定义的一个类:
package org.format.classloader;
public class Obj {
@Override
public String toString() {
return "org.format.classloader.Obj";
}
}
自定义的ClassLoader:
public class FileSystemClassLoader extends ClassLoader{
private String directory;
public FileSystemClassLoader(String directory) {
this.directory = directory;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] clsBytes = getClassBytes(name);
ifclsBytes == null)
throw new ClassNotFoundException();
return defineClass(name, clsBytes, 0, clsBytes.length);
}
private byte[] getClassBytes(String name) {
String location = getClassLoc(name);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
FileInputStream fis = null;
try {
fis = new FileInputStream(location);
byte[] buffer = new byte[4096];
int readLen = 0;
while( (readLen = fis.read(buffer)) != -1 ) {
baos.write(buffer, 0, readLen);
}
baos.flush();
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
private String getClassLoc(String name) {
return this.directory + File.separatorChar + name.replace('.', File.separatorChar) + ".class";
}
}
在测试之前,使用javac命令将Obj.java编译成Obj.class,然后放与/tmp/java/classloader目录下。
下面就用自定义的ClassLoader来进行几个测试:
FileSystemClassLoader fscl = new FileSystemClassLoader("/tmp/java/classloader");
try {
System.out.println(fscl.loadClass("org.format.classloader.Obj").newInstance());
} catch (Exception e) {
e.printStackTrace();
}

很明显,自定义的FileSystemClassLoader加载到了自定义的Obj类。
FileSystemClassLoader fscl1 = new FileSystemClassLoader("/tmp/java/classloader");
FileSystemClassLoader fscl2 = new FileSystemClassLoader("/tmp/java/classloader");
try {
Class cls1 = fscl1.loadClass("org.format.classloader.Obj");
Class cls2 = fscl2.loadClass("org.format.classloader.Obj");
System.out.println("class1: " + cls1);
System.out.println("class2: " + cls2);
System.out.println("class1 == class2? " + (cls1 == cls2));
} catch (Exception e) {
e.printStackTrace();
}

我们可以,使用不同的类加载器加载同一class文件得出的Class对象是不一样的。
这是因为Java 虚拟机不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同的情况,才认为两个类是相同的。
参考资料
http://imtiger.net/blog/2009/11/09/java-classloader/
http://www.ibm.com/developerworks/cn/java/j-lo-classloader/
http://jiangbo.me/blog/2012/02/14/jetty-classloader/
浅析java类加载器ClassLoader的更多相关文章
- 深入理解Java类加载器(ClassLoader)
深入理解Java类加载器(ClassLoader) Java学习记录--委派模型与类加载器 关于Java类加载双亲委派机制的思考(附一道面试题) 真正理解线程上下文类加载器(多案例分析) [jvm解析 ...
- 深入理解Java类加载器(ClassLoader) (转)
转自: http://blog.csdn.net/javazejian/article/details/73413292 关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Ja ...
- Java 类加载器(ClassLoader)
类加载器 ClassLoader 什么是类加载器? 通过一个类的全限定名来获取描述此类的二进制字节流这个动作放到Java虚拟机外部去实现, 以便让应用程序自己决定如何去获取所需要的类.实现这个动作的代 ...
- 潜水 java类加载器ClassLoader
类加载器(class loader)用于装载 Java 类到 Java 虚拟机中.一般来说.Java 虚拟机使用 Java 类的方式例如以下:Java 源程序(.java 文件)在经过 Java 编译 ...
- java类加载器——ClassLoader
Java的设计初衷是主要面向嵌入式领域,对于自定义的一些类,考虑使用依需求加载原则,即在程序使用到时才加载类,节省内存消耗,这时即可通过类加载器来动态加载. 如果你平时只是做web开发,那应该很少会跟 ...
- Java类加载器ClassLoader总结
JAVA类装载方式,有两种: 1.隐式装载, 程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中. 2.显式装载, 通过class.forname()等方法,显 ...
- Java类加载器(ClassLoader)
类加载的机制的层次结构 每个编写的”.java”拓展名类文件都存储着需要执行的程序逻辑,这些”.java”文件经过Java编译器编译成拓展名为”.class”的文件,”.class”文件中保存着Jav ...
- Java类加载器(死磕5)
Java类加载器( CLassLoader ) 死磕5: 自定义一个文件系统classLoader 本小节目录 5.1. 自定义类加载器的基本流程 5.2. 入门案例:自定义文件系统类加载器 5 ...
- Java类加载器( 死磕9)
[正文]Java类加载器( CLassLoader ) 死磕9: 上下文加载器原理和案例 本小节目录 9.1. 父加载器不能访问子加载器的类 9.2. 一个宠物工厂接口 9.3. 一个宠物工厂管理 ...
随机推荐
- 《HeadFirst设计模式》读后感——对学习设计模式的一些想法
最近看完了<HeadFirst设计模式>,GOF的<设计模式——可复用面向对象软件的基础>的创建型模式也读完了,经历了从一无所知到茅塞顿开再到充满迷惑的过程. 不得不说< ...
- sublime学习
1:goto anything 特性 快捷键 ctrl + P @ : # 使用 2:多行游标功能 快捷键 ctrl + D ctrl + K 跳过选择 alt + F3 多选 产生 ...
- WCF宿主端检验队列
ServiceHost host = new ServiceHost(typeof(Service1)); if (MessageQueue.Exists(@".\private\MyMes ...
- 开创学习的四核时代-迅为iTOP4412学习开发板
产品特点: 处理器: Exynos 4412 处理器,Cortex-A9四核,功耗性能俱佳! 性能: 1GB(可选2GB) 双通道 64bit数据总线 DDR3: 4GB(可选16GB)固态硬盘EMM ...
- HADOOP namenode HA
参考的文章:http://www.cnblogs.com/smartloli/p/4298430.html 当然,在操作的过程中,发现与上述文章中描述的还是有一些小小的区别. 配置好后,start-d ...
- linux网络相关配置文件
linux系统一般来说分为两大类:1.RedHat系列:Redhat.Centos.Fedora等:2.Debian系列:Debian.Ubuntu等. linux系统中,TCP/IP网络是通过若干个 ...
- 深入理解UIApplication和ios程序启动过程
在深入理解UIApplication前我们先了解ios程序的启动过程: UIApplication类在ios里面为app的管理和协调提供一个集中的点,每一个app有一个UIApplication的实例 ...
- bzoj-2338 2338: [HNOI2011]数矩形(计算几何)
题目链接: 2338: [HNOI2011]数矩形 Time Limit: 20 Sec Memory Limit: 128 MB Description Input Output 题意: 思路 ...
- Unity3D MainCamera和NGUI UICamera的小插曲
集成NGUI 在实际的项目中,经常会使用NGUI来制作UI,用Main Camera来表现3D,但是NGUI的Camer的投射是正交视图而非透视,它绑定UICamer的脚本而且它的Tag默认是Unta ...
- 真人动作捕捉系统 for Unity
真人动作捕捉 在Asset Store中浏览Mecanim相关的资源时,发现了这个 资源信息 Asset Store:https://www.assetstore.unity3d.com/#/cont ...