0. 前言
最近在学习java类加载器,发现网上很多文章都是给出介绍,并没有实验过,所以在此我就依照他们的介绍去实验一番。
先贴上类加载器之间的父子关系和加载范围,注意,类加载器之间的父子关系并不是java类中的继承关系
下面简单介绍一下上图的几个类加载器
- BootstrapClassLoader(根类加载器),是由C++编写的,等下测试的时候就可以发现,他并不是一个java类
- ExtClassLoader(扩展类加载器),顾名思义是加载扩展的一些类。
- AppClassLoader(系统类加载器),这个我们就比较经常用了,因为一般我们写的类都是由这个加载器加载的。
- CustomClassLoader(自定义类加载器),当然CustomClassLoader这个名字并不是它的类名,你也可义命名MyClassLoader,只要符合自定义类加载器的规则,都可以叫自定义类加载器。关于自定义类加载器,我还写过一篇关于自定义类加载器,并介绍了为什么要用自定义类加载器,带加密和解密的,大家可以去看看,http://blog.csdn.net/timheath/article/details/52884482
下面就要开始验证了。
1. 验证
1.1 验证内容一:类加载器的加载范围
我先来验证一下每种类加载器是否像上面所说的加载某一范围的类
1.1.1 验证BoostrapClassloader(根类加载器)的加载范围
上面我说到,根类加载器是加载核心的api,那我们找一个核心api中的类,来看看它是不是被根类加载器加载的,java.lang包下面的都是核心类了,我就用我们常用的Integer类吧,下面是验证代码
package edu.jyu.jvm.classloader;
/**
* 类加载器加载范围验证
* @author Jason
*
*/
public class Test {
public static void main(String[] args) {
ClassLoader classLoader = Integer.class.getClassLoader();
System.out.println("ClassLoader = " + classLoader);
}
}
输出的结果
ClassLoader = null
为什么是null呢?像我之前说的那样,根类加载器是用C++写的,并不是一个java类,完全不同品种的东西,所以只能返回一个null,可以看出核心api中的类确实是由根类加载器加载的。
1.1.2 验证ExtClasslOader(扩展类加载器)与AppClassLoader(系统类加载器)的加载范围
上面我说到,系统类加载器加classpath下的类,扩展类加载器加载JAVA_HOME\lib\ext目录下的jar包,现在可以通过一个例子验证一下,下面代码是验证系统类加载器加载的范围确实是classpath下的
package edu.jyu.jvm.classloader;
/**
* 类加载器加载范围验证
* @author Jason
*
*/
public class Test {
public static void main(String[] args) {
System.out.println(Test.class.getClassLoader());
}
}
输出结果
sun.misc.Launcher$AppClassLoader@15db9742
可以看出classpath下的类确实是由系统类加载器加载的
现在我将该类导出到JAVA_HOME\lib\ext的目录下
再运行程序,就可以发现其中不同
根据上面的情况就可以知道ExtClassLoader确实是加载JAVA_HOME\lib\ext下的jar包的
1.1.3 验证CustomClassLoade(自定义类加载器)的加载范围
这个在我的另一篇博客中有提到,这里便不再陈述
http://blog.csdn.net/timheath/article/details/52884482
1.2 验证内容二:类加载器的父子关系
先写一个类Car,等下测试要用到。记得,将Car编译后要将Car.class文件放到自定义类加载器的指定加载路径中,而且要确保Car.class不在classpath下,不然还是系统类加载器去加载的。
public class Car {
public Car(){
System.out.println("Car run......");
}
}
再写一个自定义类加载,这个具体也不再此描述了,上面我已经给出链接了
package edu.jyu.jvm.custom;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
/**
* 自定义类加载器
*
* @author Jason
*
*/
public class CustomClassLoader extends ClassLoader {
private String basPath;// 指定加载类的基本路径
private final String FILETYPE = ".class";// 加载文件的扩展名
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] data = this.loadClassData(name);
return this.defineClass(name, data, 0, data.length);
}
/**
* 加载二进制文件
* @param name 文件名,不含扩展名
* @return
*/
private byte[] loadClassData(String name) {
InputStream in = null;
byte[] data = null;
ByteArrayOutputStream bos = null;
try {
name = name.replace(".", "\\");
in = new FileInputStream(new File(basPath + name + FILETYPE));
bos = new ByteArrayOutputStream();
int ch = 0;
while (-1 != (ch = in.read())) {
bos.write(ch);
}
data = bos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
in.close();
bos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return data;
}
public void setBasPath(String basPath) {
this.basPath = basPath;
}
}
下面是测试类
package edu.jyu.jvm.custom;
public class Test {
public static void main(String[] args) throws Exception {
CustomClassLoader loader = new CustomClassLoader();
loader.setBasPath("D:\\myclasses\\des\\");// 设置自定义类加载的加载路径
Class<?> clazz = loader.loadClass("Car"); // 加载Car.class文件
ClassLoader classLoader = clazz.getClassLoader();
System.out.println(clazz.getClassLoader());// 输出加载Car类的类加载器
// 循环输出上层类加载器
while (classLoader != null) {
classLoader = classLoader.getParent();
System.out.println(classLoader);
}
Object object = clazz.newInstance();// 创建Car类对象,会调用它的构造方法
}
}
输出结果
edu.jyu.jvm.custom.CustomClassLoader@33909752
sun.misc.Launcher$AppClassLoader@7852e922
sun.misc.Launcher$ExtClassLoader@3d4eac69
null
Car run......
根据上面的结果可以看出开头那张图的类加载器之间的父子关系并不假,为什么类加载器要有父子关系呢?这就跟父委托机制有关,我在另一篇博客有对父委托机制的探究,大家可以去看一下
如果上面的内容有错误的地方或者讲的不好的地方,还请大家指点一下,我好及时修改。