简介:
classloader是class类的引导者,jvm对于Class类是需要时才加载,加载有两种方法是:
- 显示的:Class.forname() or loaderClass.load()
- 隐式加载:通过引用new一个类。自动加载Class类,并生成对象。
Class的加载模式
Class类加载模式为代理模式,

- bootClassloader主要负载加载的jre中的核心库C:\Java\jdk1.8.0_60\jre\lib文件下main的jar包 与 C:\Java\jdk1.8.0_60\jre\classes下面的class类
- extclassloader外部类加载器,主要加载JAVA_HOME/jre/lib/ext/文件下的jar包
- 系统加载器,加载环境变量Classpath的文件路径 加载是有父classloader的,系统加载器的父加载器是外部加载,外部加载器的父是boot加载器。需要加载Class类的时候,首先让父加载器进行类的加载,如果父类不能加载,自己再加载。这样做的好处,是保护了核心Class类,我们调用核心类的时候总是在调用的jre的jar包。 加载过程如下: loadClass()
- 首先检查类是否加载过,如加载过返回。
- 没有加载过,调用父类的findClass(),如加载成功直接到4
- 如返回null,调用findClass()进行加载
- 根据2 3的结果将字节转换成class类------这一步进行了java名字注册。
- 根据flag的标志位,决定是否经resolveClass(),resolveClass就是对类进行检查,加载类对象的静态部分。
类加载函数
加载的过程如下:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {// 同步,多进程进行保护
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);// 本地函数实现
if (c == null) {
long t0 = System.nanoTime();
try { //下面几行对父加载器进行find,如果父加载器是null,说明是boot加载器
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {//Classloader本身进行加载
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {//进行link,检查。生成静态对象。
resolveClass(c);
}
return c;
}
}
类的加载过程示意图
- 加载是defineClass()实现,
- 连接部分是resolveClass实现,在使用之前必须进行resolve

定义自己的类加载器
好处:可以对类加载器进行一些处理,例如是加密的类,在使用的时候首先进行解密。从类加载函数中知道,要进行类加载器需要重新findClass()函数,框架如下:
public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
private byte[] loadClassData(String name) {
// load the class data from the connection
. . .
}
具体实现:只加载固定文件中的两个类。Class1 Classone。注意FileUtils.readFileToByteArray是apache的commens-io包的类。
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if(name.equalsIgnoreCase("Classone")){
return load(1,name);
}else if((name.equalsIgnoreCase("class2"))){
return load(2,name);
}else
throw new ClassNotFoundException(name);
}
private Class<?> load(int i,String name) {
if(i==1 | i==2){
try {
byte[] bytes1= FileUtils.readFileToByteArray(new File("D:\\java_temp\\jsp\\out\\production\\day288\\"+name+".class"));
Class<?> classtype= defineClass(name, bytes1, 0, bytes1.length);
resolveClass(classtype);
return classtype;
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
return null;
}
web项目的加载顺序
注意web项目中有两个类加载器:tomcat类加载器,与项目加载器。tomcat加载是项目加载的父类。这两个加载是先寻找自身的类,再让父类加载(不包含核心类)。
- 项目加载器:先寻找WEB-INF下的lib与class文件,如果没有让tomcat加载器加载
- tomcat加载先加载lib文件下的类,如果没有交给系统加载器。
其他
不同类加载器加载相同的Class文件,Class类是不相同的。可以用对象的静态语句验证,Class会加载两次。
Class文件内部有类名字信息,所以在defineClass()中name变量要么为空,要么为类名字(与Class文件名字一致),不能是其他名字,如果是其他名字,会报error,找不到对象的类。
引用
- :segmentfault.com/a/119000000… Java ClassLoader 工作机制
- 源码与java库文件文档 3