浅析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. 一个宠物工厂管理 ...
随机推荐
- JS 中html 动态替换
一.定义通用替换js函数,或调用JQuery验证的$.format函数: //----通用JS操作// var a = "我喜欢吃{0},也喜欢吃{1},但是最喜欢的还是{0},偶尔再买点{ ...
- nginx设置反向代理后,页面上的js css文件无法加载
问题现象: nginx配置反向代理后,网页可以正常访问,但是页面上的js css文件无法加载,页面样式乱了. (1)nginx配置如下: (2)域名访问:js css文件无法加载: (3)IP访问:j ...
- ASP.NET Session的共享
注: 在ashx文件中使用Session 首先添加引用 using System.Web.SessionState; 实现接口 public class XXXX: IHttpHandler ==&g ...
- C#程序调用cmd执行命令
对于C#通过程序来调用cmd命令的操作,网上有很多类似的文章,但很多都不行,竟是漫天的拷贝.我自己测试整理了一下. 代码: string str = Console.ReadLine(); Syste ...
- Spring学习笔记之 Spring IOC容器(一)之 实例化容器,创建JavaBean对象,控制Bean实例化,setter方式注入,依赖属性的注入,自动装配功能实现自动属性注入
本节主要内容: 1.实例化Spring容器示例 2.利用Spring容器创建JavaBean对象 3.如何控制Bean实例化 4.利用Spring实现bean属性sett ...
- java 根据 根节点及所有子成员 构造树tree
实体类entity package com.ompa.biz.entity; import java.util.ArrayList; import java.util.List; public cla ...
- linux chromuim安装常用插件(flash,手势,拖拽,广告屏蔽)
1.本机kali2.0 64位,kali基于Debian.文章所用的所有插件请到http://files.cnblogs.com/files/yuuyuu/chromium_plugins.tar. ...
- Git指令总结和图表
Git 是一个很强大的分布式版本控制系统.它不但适用于管理大型开源软件的源代码,管理私人的文档和源代码也有很多优势. Git常用操作命令: 1) 远程仓库相关命令 检出仓库:$ git clone g ...
- 在vscode中使用Git
用了git最方便的就是比如在公司写了很多代码后回到家打开vscode只需要点击一下pull就能全部同步过来.是不是很方便....毕竟之前我都是拿u盘拷贝回家或者存到云盘再下载下来.. 我这里用的是国内 ...
- LeetCode题解-----Majority Element II 摩尔投票法
题目描述: Given an integer array of size n, find all elements that appear more than ⌊ n/3 ⌋ times. The a ...