我们在学习JVM的一个类加载体系的时候,应该会学到这样一个名词“双亲委托模式”。而当我们跟踪ClassLoader里的loadClass方法,也可以很清楚的看到这种算法的具体实现。但是当我们跟踪Class.forName方法时,没有发现这种算法,只是有一个ClassLoader.getCallerClassLoader()这样的方法来获取ClassLoader?那我的问题来了,这两种方法所加载的类,可以让JVM找到吗?
我们知道,要让一个类可以运行,需要将些类转化为Class对象,而且这个对象必需可以让JVM找到,这样我们才可以使用。有一种说法是这样的,需要同一个类加载器加类的类才能使用,其实我认为这种说是不准确的,其实哪个类加载器加载类都无谓的,举个反例,就是String类是由Bootstrap启动类加载器加载的(我们可以用String.class.getClassLoader()来输出它的加载器,可以发现输出的结果是null,所以表明它是Bootstrap启动的),而我们调用String的类并不是Bootstrap加载的,那我们的类岂不是不能用String类,显示答案是可以用的,这就说明跟哪个类加载器加载类是没有关系的,只要按JVM的算法让JVM可以找到此类的Class对象就可以了,所以回到上面的问题,这两种加载方法,产生的结果是一样的吗?下面我做了一个实验,我想是可以证明到,它们是一样的:
1、创建一个类ClazzLoaderClass.java,它的作用就是打印一条信息。
package xxx.com;
public class ClazzLoaderClass {
public ClazzLoaderClass(){
System.out.println("clazz loader load class");
}
}
2、我们再创建一个类ClazzForName.java,它的作用就是打印出一条信息,并且调用了上面的ClazzLoaderClass.java类。
package xxx.com;
public class ClazzForName {
public ClazzForName(){
System.out.println("clazz for name");
new ClazzLoaderClass();
}
}
3、写一个main方法。每句的代码的作用,如下面的注析。需要注意的是,我们会在加载完ClazzLoaderClass类后,删除ClazzLoaderClass.class文件;在加载完ClazzForName类后,将ClazzForName.class从硬盘中删除。
package xxx.com;
public class TestLoader {
public static void main(String[] args) throws Exception{
ClassLoader loader = TestLoader.class.getClassLoader();//获取main所在类的ClassLoader
loader.loadClass("xxx.com.ClazzLoaderClass");//加载ClazzLoaderClass
System.out.println("ClazzLoaderClass类加载完成-------------------------");
Thread.sleep(15000);//在这个15秒里,我们会删除ClazzLoaderClass.java这个类
Class.forName("xxx.com.ClazzForName");//加载ClazzForName
System.out.println("ClazzForName类加载完成-------------------------");
Thread.sleep(15000);//在这个15秒里,我们会删除ClazzForName.java这个类
new ClazzForName();//执行
}
}
4、运行后的结果如下:
ClazzLoaderClass类加载完成-------------------------
ClazzForName类加载完成-------------------------
clazz for name
clazz loader load class
结论:
1、这说明不管用forName还是loadClass,它们之间都是可以互相访问。因为我们在执行new之前,就将两个class文件都删除了。