新建類MyTest17_1:
public class MyTest17_1 {
public static void main(String[] args) throws Exception{
MyTest16 loader1 = new MyTest16("loader1");
loader1.setPath("E:\\data\\classes\\");
Class<?> clazz = loader1.loadClass("com.twodragonlake.jvm.classloader.MySample");
System.out.println("class :"+clazz.hashCode());
//如果註釋掉改行,那麼並不會實例化MySample對象,即MySample構造方法不會被調用
//因此不會實例化MyCat對象,既沒有對MyCat進行主動使用,這裏就不會加載MyCat class
Object object = clazz.newInstance();
}
}
和MyTest17不同的是我們指定了Path,運行結果還是:
class :1735600054
MySample is loaded by : sun.misc.Launcher$AppClassLoader@18b4aac2
MyCat is loaded by : sun.misc.Launcher$AppClassLoader@18b4aac2
ok,現在我們把當前工程下的MySample和MyCat的class文件刪除掉,然後copy一份到E:\data\classes\下邊,運行程序:
findClass invoked com.twodragonlake.jvm.classloader.MySample【加載MySample時MyTest16的打印】
this.classLoaderName : loader1 【MySample是由MyTest16加載,MyTest16的打印】
class :2133927002 【MyTest17_1的打印】
MySample is loaded by : com.twodragonlake.jvm.classloader.MyTest16@677327b6 【MySample構造器】
findClass invoked com.twodragonlake.jvm.classloader.MyCat 【MySample的構造器new MyCat的時候要先加載MyCat】
this.classLoaderName : loader1 【MyCat是由MyTest16加載的】
MyCat is loaded by : com.twodragonlake.jvm.classloader.MyTest16@677327b6 【MyCat構造器的打印】
好,我們繼續做一個實驗,重新編譯當前工程,讓MyCat和MySample的class文件出現,然後刪除MyCat的class文件,注意此時MySample在當前工程和【E:\data\classes\】下都有一份,而MyCat只在【E:\data\classes\】有一份,當前工程不存在MyCat的class文件,運行程序:
class :1735600054
Exception in thread "main" java.lang.NoClassDefFoundError: com/twodragonlake/jvm/classloader/MyCat
MySample is loaded by : sun.misc.Launcher$AppClassLoader@18b4aac2
at com.twodragonlake.jvm.classloader.MySample.<init>(MySample.java:6)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at com.twodragonlake.jvm.classloader.MyTest17_1.main(MyTest17_1.java:11)
Caused by: java.lang.ClassNotFoundException: com.twodragonlake.jvm.classloader.MyCat
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 7 more
分析:【 Class
findClass invoked com.twodragonlake.jvm.classloader.MySample
this.classLoaderName : loader1
class :2133927002
MySample is loaded by : com.twodragonlake.jvm.classloader.MyTest16@677327b6
MyCat is loaded by : sun.misc.Launcher$AppClassLoader@18b4aac2
首先MySample在當前工程已經被刪除了,所以應用類加載器無法加載,會使用我們自定義的類加載器MyTest16去加載MySample,因此打印:
findClass invoked com.twodragonlake.jvm.classloader.MySample
this.classLoaderName : loader1
之後【class :2133927002】是MyTest17_1的打印,在MySample的構造器new MyCat的時候,加載器了MySample的加載器(MyTest16)會嘗試加載MyCat,MyTest16自己並不會去加載MyCat,它首先會委託應用類加載器去加載,,應用類加載器能不能加載呢?答案是可以加載,因爲MyCat在當前classPath下(當前工程裏邊的,不是【E:\data\classes\】下邊的),所以後邊不會出現自定義加載MyTest16的log日誌,之後直接打印MyCat構造器的輸出【MyCat is loaded by : sun.misc.Launcher$AppClassLoader@18b4aac2】。MySample和MyCat是由2個不同的加載器加載出來的。
ok,繼續下一個實驗:我們在MyCat 構造器裏邊加入一行代碼:
public class MyCat {
public MyCat(){
System.out.println("MyCat is loaded by : "+this.getClass().getClassLoader());
System.out.println("from MyCat : "+MySample.class);//加入打印MySample的一行代碼
}
}
重新build當前工程讓MySample和MyCat文件出現在當前工程下,然後copy MyCat的class文件到【E:\data\classes\】下邊,之後刪除當前工程下的MySample的class文件,運行MyTest17_1程序。打印如下:
findClass invoked com.twodragonlake.jvm.classloader.MySample
this.classLoaderName : loader1
Exception in thread "main" java.lang.NoClassDefFoundError: com/twodragonlake/jvm/classloader/MySample
at com.twodragonlake.jvm.classloader.MyCat.<init>(MyCat.java:6)
at com.twodragonlake.jvm.classloader.MySample.<init>(MySample.java:6)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at com.twodragonlake.jvm.classloader.MyTest17_1.main(MyTest17_1.java:11)
Caused by: java.lang.ClassNotFoundException: com.twodragonlake.jvm.classloader.MySample
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 8 more
class :2133927002
MySample is loaded by : com.twodragonlake.jvm.classloader.MyTest16@677327b6
MyCat is loaded by : sun.misc.Launcher$AppClassLoader@18b4aac2
分析:
首先這個例子和上一個例子只有一個地方不同就是在MyCat的構造器裏邊加了一行打印MySample的代碼,而且MySample和MyCat是由不同的類加載器加載的:
MySample is loaded by : com.twodragonlake.jvm.classloader.MyTest16@677327b6 【MySample由MyTest16加載】
MyCat is loaded by : sun.misc.Launcher$AppClassLoader@18b4aac2 【MyCat由AppClassLoader加載】
但是爲什麼出現MySample的ClassNotFoundException的異常呢,原因就是類的命名空間:
即,子加載器可以看到父加載器加載的類,但是父加載器看不到子加載器加載的類,這個例子當中,MySample由子加載器MyTest16加載,MyCat由父加載器AppClassLoader加載,因此在父加載器裏邊看不到子加載器MyTest16加載的類MySample,所以拋出ClassNotFoundException異常。
ok,最後一個例子,
MySample(加入MyCat的打印) :
public class MySample {
public MySample(){
System.out.println("MySample is loaded by : "+this.getClass().getClassLoader());
new MyCat();
System.out.println("form MySample :"+MyCat.class);
}
}
MyCat (註釋掉MySample的打印):
public class MyCat {
public MyCat(){
System.out.println("MyCat is loaded by : "+this.getClass().getClassLoader());
// System.out.println("from MyCat : "+MySample.class);
}
}
重新buid當前工程,生成MyCat 和MySample的class文件,拷貝一份到【E:\data\classes\】下邊,然後刪除當前工程的MySample的class文件,運行MyTest17_1程序,打印結果:
findClass invoked com.twodragonlake.jvm.classloader.MySample
this.classLoaderName : loader1
class :2133927002
MySample is loaded by : com.twodragonlake.jvm.classloader.MyTest16@677327b6
MyCat is loaded by : sun.misc.Launcher$AppClassLoader@18b4aac2
form MySample :class com.twodragonlake.jvm.classloader.MyCat
分析:
首先MySample的class不在當前工程下,因此會使用自定義加載器MyTest16加載,MySample的構造器裏邊出現new MyCat,因此會使用子加載器MyTest16的父加載器應用類加載器加載MyCat,之後【System.out.println(“form MySample :”+MyCat.class);】這行代碼是在子類加載器MyTest16裏邊出現,由於子類加載器可以看到父類加載器加載的類,因此不會拋出異常。
關於命名空間的重要說明
1、子加載器所加載的類能夠訪問到父加載器所加載的類
2、父加載器所加載的類無法訪問到子類加載器所加載的類