零、关键方法
- Class.forName(String className, boolean initialize, ClassLoader loader)
1)initialize:是否执行类初始化操作,即是否执行<cinit>方法。 - Class.forName(String className)
1)等价于:Class.forName(String className, true, currentLoader)
2)currentLoader为真正加载当前类的classLoader
(最后被委托的类加载器,执行defineClass方法的类加载器实例),非线程上下文的ClassLoader
; - ClassLoader.load(String className, boolean resolve)
1)resolve:是否解析类,即是否解析符号为直接地址; - ClassLoader.load(String className)
1)等价:ClassLoader.load(String className, false)
一、 关联点
Class.forName最终也是调用ClassLoader去加载类的。
二、区别
1. 是否执行类初始化操作
1)Class.forName可以控制是否执行类初始化操作;
2)ClassLoader不会触发类初始化操作;
案例:数据库驱动需要使用Class.forName来触发静态库注册驱动;
2. 是否会解析数组类型
1)Class.forName会解析数组类型,如[Ljava.lang.String;
2)ClassLoader不会解析数组类型,加载时会抛出ClassNotFoundException;
3. 是否有缓存
1)类是缓存真正加载类的ClassLoader里的,此时需要避免委托给应用类加载器,而使用自定义类加载器去加载类
;
考虑打破双亲委派或指定AppClassLoader不可见路径;
2)只要指定每次加载类的ClassLoader即可实现热加载;
public class HotLoadClassTest {
private final File path = new File("/Users/kivi/Documents");
@Test
public void testClassForName() throws Throwable {
Class aClass = Class.forName("MyString", true, new MyClassLoader(new URL[]{path.toURL()}));
aClass.getDeclaredMethod("echo").invoke(null);
/** 替换MyString实现,可加载最近类 **/
aClass = Class.forName("MyString", true, new MyClassLoader(new URL[]{path.toURL()}));
aClass.getDeclaredMethod("echo").invoke(null);
}
@Test
public void testClassLoader() throws Throwable {
Class<?> aClass = new MyClassLoader(new URL[]{path.toURL()}).loadClass("MyString");
aClass.getDeclaredMethod("echo").invoke(null);
/** 替换MyString实现,可加载最近类 **/
aClass = new MyClassLoader(new URL[]{path.toURL()}).loadClass("MyString");
aClass.getDeclaredMethod("echo").invoke(null);
}
public static class MyClassLoader extends URLClassLoader {
public MyClassLoader(URL[] urls) {
super(urls);
}
}
public class MyString {
private static int invokeTime = 0;
static {
System.out.println("here is MyString static block");
}
public static void echo() {
System.out.println("第" + (invokeTime++) + "次被调用");
}
}
网友评论