一、自定义类加载器的两个关键方法:
- loadClassData():将.class文件读取到 byte[] 字节数组中并返回。
- defineClass():将byte[] 字节数组生成Class对象,是native方法。
二、自定义类加载器实例代码
// 自定义的类加载器必须继承 ClassLoader类
public class TestCode16 extends ClassLoader {
// 类加载器名称,仅做标记使用,没有实质用途
private String clName;
// 构造函数1:不指定父类加载器,仅指定自定义类加载器的名称
public TestCode16(String clName) {
super();
this.clName = clName;
}
// 构造函数2:指定父类记载器和类加载器的名称。
public TestCode16(ClassLoader parent, String clName) {
super(parent);
this.clName = clName;
}
/**
* loadClassData,将class文件读取为byte[]数组。
* @param target 目标class文件路径
* @return
*/
private byte[] loadClassData(String target) {
InputStream is = null;
byte[] bytes = null;
ByteArrayOutputStream os = null;
int len;
try {
is = new FileInputStream(new File(target));
os = new ByteArrayOutputStream();
while(-1 != (len = is.read())) {
os.write(len);
}
bytes = os.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
return bytes;
}
/**
* 通过调用 defineClass()方法,将byte[] 字节数组转化为类对象
* @param target
* @return
*/
public Class<?> findClass(String target, String className) {
byte[] bytes = loadClassData(target);
// clName 应该是 类的binary name
return defineClass(className, bytes, 0, bytes.length);
}
// 以下代码会调用自定义的类加载器加载TestCode15
public static void main(String[] args) throws Exception {
TestCode16 loader1 = new TestCode16("loader1");
String target = "D:/develop/CCXI/jvm-project/bin/cn/com/ccxi/jvm/test/TestCode15.class";
String className = "cn.com.ccxi.jvm.test.TestCode15";
Class clazz = loader1.findClass(target, className);
System.out.println(clazz.getClassLoader()); // cn.com.ccxi.jvm.test.TestCode16@773de2bd
}
}
代码中,生成的 cn.com.ccxi.jvm.test.TestCode15 类与AppClassLoader类加载器加载的 TestCode15类时两种不同的类型,是由于类加载器的命名空间不同的原因。