在此之前我们要先了解几个概念:
- JVM加载Class文件
- 一个类可以创建很多对象,但是他的Class文件在内存中只有一份
- 不同的类加载器加载相同的Class文件,根据Class生成的对象是没有任何关系的
- 双亲委派机制中一个Class只会被类加载器加载一次
现在我们就会有一个很明确的思路,我们让类加载器每次都去加载Class文件不就可以了。
真正的热加载会比我们说的复杂的多,我们用被简单的方式带大家去了解
目录
代码实现:
1、自定义类加载器
public class MyClassLoad extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
StringBuilder sb = new StringBuilder(name.length() + 6);
sb.append(name.replace('.', '/')).append(".class");
/*加载我们target/classes下的class*/
InputStream is = getResourceAsStream(sb.toString());
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len;
while ((len = is.read(buf)) >= 0) {
baos.write(buf, 0, len);
}
buf = baos.toByteArray();
return this.defineClass(name, buf, 0, buf.length);
} catch (Exception e) {
throw new RuntimeException("自定义类加载", e);
} finally {
try {
is.close();
} catch (IOException ioe) {
//ignore
}
}
}
}
2、创建实体类
public class Dzl {
/**
* test
*/
public void print() {
System.out.print("热加载");
}
}
3、类加载器去加载
public class Factory {
/**
* 原生的类加载器进行加载
*/
public static void getObject() {
try {
Class<?> dzl = Factory.class.getClassLoader().loadClass("Dzl");
Object o = dzl.newInstance();
Method print = dzl.getDeclaredMethod("print", null);
print.invoke(o, null);
} catch (Exception e) {
throw new RuntimeException("类加载异常", e);
}
}
/**
* 自定义的类加载器进行加载
*/
public static void getObjectNew() {
try {
/*每次加载都创建一个新的类加载器*/
MyClassLoad classLoad = new MyClassLoad();
Class<?> dzl = classLoad.findClass("Dzl");
Object o = dzl.newInstance();
Method print = dzl.getDeclaredMethod("print", null);
print.invoke(o, null);
} catch (Exception e) {
throw new RuntimeException("类加载异常", e);
}
}
}
4、mian测试
public class Test {
public static void main(String[] args) throws Exception {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
while (true) {
System.out.print(format.format(new Date())+" ");
Factory.getObject();
System.out.println();
TimeUnit.SECONDS.sleep(4);
}
}
}
运行结果:
修改Dzl类的print方法,通过javac生成class文件替换target/class下的Dzl.class:
结果并没有发生该改变
我们使用我们自定义的加载器去执行改方法:
到此一个简单的原理证明就实现了
5、疑问
这里为什么不进行强制类型转换然后调用我们的print()方法:他们是通过不同的类加载器进行加载的结果无法进行类型转换,会报错。