假设我们已经定义好自己的加载器MyClassLoader,我们使用如下的代码便能够构造出例子中的类加载器树形结构。
MyClassLoader loadB = new MyClassLoader(“loadB”);
loadB.setPath(“D://app//serverlib”);
MyClassLoader loadA = new MyClassLoader(loadB,“loadA”);
loadB.setPath(“D://app//cientlib”);
MyClassLoader loadC = new MyClassLoader(null,”loadC”)//null代表父加载器为Bootstrap
loadC.setPath(“D://app//otherlib”);
我们一个测试类Sample 和一个测试类Dog进行加载测试。
Class Sample{
Static{
New Dog();
}
}
Class文件的存放路径如下:
d:app/syslib/MyClassLoader.class
d:app/serverlib/Sample.class
d:app/clientlib/Dog.class
d:app/otherlib/Sample.class,Dog.class
TestCase1 :
Class clazz = loadA.loadClass(“Sample”);
Clazz.newInstance();
TestCase2 :
Class clazz = loadB.loadClass(“Sample”);
Clazz.newInstance();
TestCase3 :
Class clazz = loadC.loadClass(“Sample”);
Clazz.newInstance();
在case1 中 由加载器的树形结构可以看出:loadA加载Sample时委托父亲LoadB加载Sample,由于再向上委托并不能加载Sample,所以 Sample 由LoadB在 app/serverlib 下加载,
对于Dog类,LoadA的所有父加载器都不能加载,所以有loadA在 app/clientlib下加载
在case2 中 有loadB在 app/serverlib中加载 Sample,由于loadB 与其父加载器都不能加载Dog,所以会抛出ClassNotfoundException。此时如果把Dog拷贝到 syslib下,Dog类就会被appCloader加载,而不会出现ClassNotFound错误。
在Case3 中LoadC直接委托Bootstrap加载Sample,由于无法加载只能有自己加载,所以Sample 与Dog都会从 app/otherlib/下加载.
假设 我们在MyClassLoader中写意给main方法测试
TestCase4 :
Class clazz = loadA.loadClass(“Sample”);
Sample sample = Clazz.newInstance();
此时会导致一个NoClassDefError,这主要是JVM的类加载器命名空间规则导致的,在jvm中子加载器的命令空间包含了父加载器加载的所有类,反过来则不成立,因为MyClassLoader类是有appLoader加载的,所以其看不见有LoadB与loadA加载的类。
在这里顺便提一下,在一个类主动使用时,该类就开始起生命周期 加载,链接,初始化,使用,卸载。Jvm自带的加载器加载的Class是不能够被卸载的,只有用户自定义加载器加载的类才能被卸载,卸载机制是根据对类的引用计数情况而定,这与GC根据引用情况回收垃圾差不多