一.概述
1.自定义类加载器时需要继承ClassLoader,并且重写其findClass()方法
2.具体实现过程:
①定义类加载器的加载路径属性
②通过带参构造为属性赋值
③重写findClass()
1>确定class文件的全路径,并启动字节流读取class文件
2>创建ByteArrayOutputStream用于将字节流读入至字节数组中
*****数据将写入缓冲区中,缓冲区随着数据的写入会自动增长
3>使用byte[] classData接收字节数组并判断字节数组是否为null
4>若classData为null,则说明该类文件未被加载,亦说明该类文件不能被找到,则抛出ClassNotFoundException
5>若classData不为空,则调用defineClass创建该类文件的对象
二.代码实现
1.被加载class文件对应类的定义(TargetTest.java)
/**
* @author :weihuanwen
* @date :Created in 2019/5/23 00:14
* @description :
* @version: 1.0
*/
public class TargetTest {
/**
* 重写toString方法
* 类加载器加载该类class文件后可以创建该类对象
* 使用对象可以调用该toString方法
* @return
*/
@Override
public String toString() {
return "Hi! You find me!";
}
}
在java文件所在目录下使用javac -encoding UTF-8 TargetTest.java可以将其编译为class文件
2.自定义类加载器代码实现(CustomClassLoader.java)
import java.io.*;
/**
* @author :weihuanwen
* @date :Created in 2019/5/23 00:14
* @description :自定义File类加载器
* @version: 1.0
*/
public class CustomClassLoader extends ClassLoader{
//类加载器加载类文件范围
private String rootDir;
public CustomClassLoader(String rootDir) {
this.rootDir = rootDir;
}
/**
* 重写ClassLoader的findClass方法
* ①读取class文件通过字节流写入字节数组
* ②调用defineClass将字节数组转为class对象
* @param name
* @return
* @throws ClassNotFoundException
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
//拼接class文件的全路径
String classFilePath = rootDir+ File.separator+name.replace(".",File.separator)+ ".class";
try {
//字节流读取class文件
InputStream is = new FileInputStream(classFilePath);
//该字节流可以将数据写入字节数组,数据写入缓冲区时缓冲区自动增长
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] bytes = new byte[4096];
int length = 0;
//读取类文件并写入字节数组中
while ((length = is.read(bytes))!= -1){
baos.write(bytes,0,length);
}
//获取类文件的字节数组
byte[] classData = baos.toByteArray();
if (classData == null){
throw new ClassNotFoundException();
}else{
return defineClass(name, classData,0,classData.length);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
3.测试类定义(CustomClassLoaderTest.java)
/**
* @author :weihuanwen
* @date :Created in 2019/5/23 00:14
* @description :
* @version: 1.0
*/
public class CustomClassLoaderTest {
//测试自定义的类加载器
public static void main(String[] args) {
String root = "D:\\study\\";
//创建指定搜索路径的自定义类加载器对象
CustomClassLoader ccl = new CustomClassLoader(root);
try {
Class<?> demoObj = ccl.loadClass("TargetTest");
System.out.println(demoObj.newInstance().toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试结果输出:
Hi! You find me!
三.自定义类加载器实现热加载
1.修改测试类结构:
import java.util.Timer;
import java.util.TimerTask;
/**
* @author :weihuanwen
* @date :Created in 2019/5/23 00:14
* @description :
* @version: 1.0
*/
public class test {
public static void main(String[] args) {
//设置定时任务
new Timer().schedule(new TimerTask() {
@Override
public void run() {
String rootPath = "D:\\study\\";
CustomDefineLoader cdl = new CustomDefineLoader(rootPath);
try {
Class<?> targetTest = cdl.loadClass("TargetTest");
System.out.println(targetTest.newInstance().toString());
} catch (Exception e) {
e.printStackTrace();
}
}
},0,1001);
}
}
2.将TargetTest.java放置在D:\study文件夹中
3.在CMD命令框中将TargetTest.java编译为TargetTest.class文件
输入:
4.执行测试类CustomClassLoaderTest.java
执行结果如下:
5.程序运行过程中修改TargetTest.java文件
/**
* @author :weihuanwen
* @date :Created in 2019/5/23 00:14
* @description :
* @version: 1.0
*/
public class TargetTest {
/**
* 重写toString方法
* 类加载器加载该类class文件后可以创建该类对象
* 使用对象可以调用该toString方法
* @return
*/
@Override
public String toString() {
return "GoodBye!";
}
}
6.将修改后的文件再次进行编译,生成新的TargetTest.class文件
打印结果如下: