加载。
换句话说,如果自定义类加载器,就必须重写findClass
方法!
2.1.1 find Class
findClass
的默认实现如下:
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
可以看出,抽象类ClassLoader
的findClass
函数默认是抛出异常的。而前面我们知道,loadClass
在父加载器无法加载类的时候,就会调用我们自定义的类加载器中的findeClass
函数,因此我们必须要在loadClass
这个函数里面实现将一个指定类名称转换为Class
对象.
如果是是读取一个指定的名称的类为字节数组的话,这很好办。但是如何将字节数组转为Class
对象呢?很简单,Java
提供了defineClass
方法,通过这个方法,就可以把一个字节数组转为Class对象啦~
2.1.1 defineClass
defineClass
主要的功能是:
将一个字节数组转为Class
对象,这个字节数组是class
文件读取后最终的字节数组。如,假设class
文件是加密过的,则需要解密后作为形参传入defineClass
函数。
defineClass
默认实现如下:
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
throws ClassFormatError {
return defineClass(name, b, off, len, null);
}
2.2 函数调用过程
上一节所提的函数调用过程如下:
自定义函数调用过程
2.3 简单示例
首先,我们定义一个待加载的普通Java
类:Test.java
。放在com.huachao.cl
包下:
package com.huachao.cl;
public class Test {
public void hello() {
System.out.println("恩,是的,我是由 " + getClass().getClassLoader().getClass()
+ " 加载进来的");
}
}
注意:
如果你是直接在当前项目里面创建,待Test.java
编译后,请把Test.class
文件拷贝走,再将Test.java
删除。因为如果Test.class
存放在当前项目中,根据双亲委派模型可知,会通过sun.misc.Launcher$AppClassLoader
类加载器加载。为了让我们自定义的类加载器加载,我们把Test.class文件放入到其他目录。
在本例中,我们Test.class文件存放的目录如下:
class文件目录
接下来就是自定义我们的类加载器:
import java.io.FileInputStream;
import java.lang.reflect.Method;
public class Main {
static class MyClassLoader extends ClassLoader {
private String classPath;
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
private byte[] loadByte(String name) throws Exception {
name = name.replaceAll("\\.", "/");
FileInputStream fis = new FileInputStream(classPath + "/" + name
+ ".class");
int len = fis.available();
byte[] data = new byte[len];
fis.read(data);
fis.close();
return data;
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] data = loadByte(name);
return defineClass(name, data, 0, data.length);
} catch (Exception e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
}
};
public static void main(String args[]) throws Exception {
MyClassLoader classLoader = new MyClassLoader("D:/test");
Class clazz = classLoader.loadClass("com.huachao.cl.Test");
Object obj = clazz.newInstance();
Method helloMethod = clazz.getDeclaredMethod("hello", null);
helloMethod.invoke(obj, null);
}
}
最后运行结果如下:
恩,是的,我是由 class Main$MyClassLoader 加载进来的