类加载器命名空间实战剖析与透彻理解
关于命名空间的重要说明:【主要看实例四和五】
- 子类加载所加载的类能够访问到父类加载器所加载的类;
- 父类加载器所加载的类无法访问到子加载器所加载的类。
-
实例一:
-
public class MyCat { public MyCat(){ System.out.println("MyCat is loaded by:"+this.getClass().getClassLoader()); } } public class MySample { public MySample() { System.out.println("MySample is loaded by :" +this.getClass().getClassLoader()); new MyCat(); //【重要】 } } public class MyTest17_1 { public static void main(String[] args) throws Exception { //MyTest16是一个自定义的类加载,在15+16章节中可以找到 MyTest16 loader1 = new MyTest16("loader1"); loader1.setPath("C:\\Users\\admin\\Desktop\\"); Class<?> clazz = loader1.loadClass("Jvm.MySample"); //先加载 System.out.println("class : " + clazz.hashCode()); //如果注释掉该行,那么并不会实例化MySample对象,即MySample构造方法不会被调用 //因此不会实例化MyCat对象,即没有对Mycat进行主动使用,这里就不会加载MyCat Class Object object = clazz.newInstance(); //再创建实例 } } 运行结果: findClass invoked:Jvm.MySample class loader name:loader1 class : 856419764 MySample is loaded by :[loader1] --------------------------------- findClass invoked:Jvm.MyCat class loader name:loader1 MyCat is loaded by:[loader1]
-
结果分析:[前提:删除out中的MySample、MyCat的class文件,copy到桌面Jvm文件夹中]
-
分割线之前的自己理解;
-
分割线之后;在MySample中调用new MyCat(),当创建该实例的时候,就会去加载MyCat的class文件,这个时候,还是有MyTest16自定义类加载器去加载文件,所以才会有:
findClass invoked:Jvm.MyCat class loader name:loader1 MyCat is loaded by:[loader1]
-
-
-
-
实例二:
- 代码还是上面的代码,[前提:不删除out中的MySample文件,删除MyCat的class文件]
- 此时在加载MySample的时候,可以在out文件找找到对应的class文件,所以是由系统类加载器加载的;当我们去在MySample中加载Mycat时候,由于用到的加载器和MySample是同一个,即系统类加载器,但是我们删除了out文件夹中的Mycat.class文件,所以加载不到:NoClassDefFoundError
运行结果: class : 1761291320 MySample is loaded by :sun.misc.Launcher$AppClassLoader@18b4aac2 Exception in thread "main" java.lang.NoClassDefFoundError: Jvm/MyCat at Jvm.MySample.<init>(MySample.java:6) ......
- 代码还是上面的代码,[前提:不删除out中的MySample文件,删除MyCat的class文件]
-
实例三:
-
代码还是上面的代码,[前提:不删除out中的MySample文件,而只删除MyCat的class文件]
-
对于MySample来说,由于out文件夹中没有对应的class文件,所以由自定义类加载加载。
-
对于MyCat来说,MyCat是由加载MySample使用的类加载器(MyTest16,即loader1)去加载,这个时候,又由双亲委托机制可以知道的是 ,loader1会委托它的父亲去加载MyCat ,所以,最终MyCat 由系统类加载器加载。
运行结果: findClass invoked:Jvm.MySample class loader name:loader1 class : 783286238 MySample is loaded by :[loader1] MyCat is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
-
-
-
实例四:
-
由子加载器所加载的类可以看到父加载器所加载的类,但,由父加载器所加载的类看不见子加载器所加载的类。
-
所有的不改变,但是在MyCat类中多加一行System.out.println("from MyCat : " +MySample.class); [同时删除out文件夹中的MySample.class文件]。
-
运行结果前五行没有任何问题,当执行到System.out.println("from MyCat : " +MySample.class);这一句的时候,由于会用到类加载器(加载MyCat的类加载器:AppClassLoader)去找MySample.class文件,**这时,由于AppClassLoader是loader(自定义类加载器)的父类加载器,他看不到子加载器所加载的类(内容),**所以,报错,找不到class文件
public class MyCat { public MyCat(){ System.out.println("MyCat is loaded by:"+this.getClass().getClassLoader()); System.out.println("from MyCat : " +MySample.class); } } 运行结果: findClass invoked:Jvm.MySample class loader name:loader1 class : 783286238 MySample is loaded by :[loader1] MyCat is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2 Exception in thread "main" java.lang.NoClassDefFoundError: Jvm/MySample ...... Caused by: java.lang.ClassNotFoundException: Jvm.MySample
-
-
-
实例五:【实例五与实例四对比】
-
MySample中多加一行:System.out.println(“from MyCat:”+MyCat.class);【删除out中MySample.class】
-
主要在运行结果的最后一行,成功打印 from MyCat : class Jvm.MyCat ; 因为MySample由自定义类加载器加载,MyCat 由父类加载器加载;所以,由子类加载所加载的类的命名空间当中,可以访问由父加载器所加载的class对象。
public class MySample { public MySample() { System.out.println("MySample is loaded by :" +this.getClass().getClassLoader()); new MyCat(); //【重要】 System.out.println("from MyCat : " +MyCat.class); } } public class MyCat { public MyCat(){ System.out.println("MyCat is loaded by:"+this.getClass().getClassLoader()); } } 运行结果: findClass invoked:Jvm.MySample class loader name:loader1 class : 783286238 MySample is loaded by :[loader1] MyCat is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2 from MyCat : class Jvm.MyCat
-
-