java提供的加载器根加载器,扩展加载器,系统加载器都只能加载指定位置的class和jar,如果我们想要加载其他位置,就比如D盘下某个文件加下的class文件就需要自定义ClassLoader。
自定义ClassLoader最主要的就是要重写findClass方法。
具体代码如下:
package main;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class DefineClassLoader extends ClassLoader {
private String rootDir;//加载文件根路径
public DefineClassLoader(String rootDir){
this.rootDir=rootDir;
}
@Override
public Class<?> findClass(String className) throws ClassNotFoundException {
Class clazz=null;
byte[]classData=getClassData(className);//根据类名获取类的二进制字节数组
if(classData==null){//获取不到二进制数组,抛出异常
throw new ClassCastException();
}
clazz=defineClass(className,classData, , classData.length);//根据class文件的字节数组转换为Class类实例
return clazz;
}
public byte[]getClassData(String className){
InputStream is=null;
ByteArrayOutputStream baos =null;
String path=classNameToPath(className);
try{
is=new FileInputStream(path);
baos = new ByteArrayOutputStream();
byte [] buffer=new byte[*];
int bytesNumRead =-;
while((bytesNumRead=is.read(buffer))!=-){
baos.write(buffer, , bytesNumRead);
}
return baos.toByteArray();
}catch (Exception e) {
e.printStackTrace();
}finally {
if(baos!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(baos!=null){
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
//根据类名获取路径
public String classNameToPath(String className){
return rootDir+className.replace(".", File.separator)+".class";
}
}
我们在d盘下创建如下目录,并新建DefineClass类
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2Lc9mUyo1a1cVWvR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2LcRHelR3LcJzLctmch1mclRXY39zMxQjMxgTNzITNxUDM4EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
package com.test;
public class DefineClass {
public String toString () {
return "加载自定义类:DefineClass!";
}
}
并用dos命令进行编译,注意类里面有中文,还要加上编码进行编译,命令为:
编译后再创建测试类DefineClassLoaderTest ,代码如下
package test;
import org.junit.Test;
import main.DefineClassLoader;
public class DefineClassLoaderTest {
@Test
public void test() throws ClassNotFoundException, InstantiationException, IllegalAccessException{
String rootdir="D:\\";
String className="com.test.DefineClass";
DefineClassLoader classloader=new DefineClassLoader(className);
Class clazz=classloader.loadClass(className);
System.out.println(className+"的加载器是"+clazz.getClassLoader());
System.out.println(clazz.newInstance().toString());
}
}
执行结果
可见DefineClass类的加载器正是我们自定义的加载器。
这里再次为了证明双亲委托加载模式,我们在工程下也创建一个与D盘下面一样的com.test.DefineClass类,那程序的输出的加载器肯定是App加载器。
程序执行结果与预期一样: