JVM的艺术—类加载器篇(二) (2)

从上面的运行结果,我们可以看出,当我们用自定义类加载器去加载我们的Person的时候,根据双亲委托模型,我们的Person并没有被自定义类加载(Test01ClassLoader)加载,而是被AppClassloader加载成功,同时根据全盘委托规则,我们的Dog类也被AppClassLoader加载了。所以大家一定要记住这个至关重要的结论。为我们后面的学习打下坚实的基础。

下面我们在看一个例子。我们把类路径下的Person.class文件删除掉,然后再运行一下上面的main函数,看看结果。代码如下:

JVM的艺术—类加载器篇(二)

通过那行结果我们看出,Person类是由我们的自定义类加载器加载的。那为什么Dog类没有进行全盘委托的,这是因为双亲委托模型的缘故,我们的类路径下并没有Person类,故此AppClassLoader是无法加载我们的路径I:\\test\\下的com.test.Person.class文件的。所以Person类是由我们自定的类加载器加载的。再看Dog类,由于它的加载要遵循双亲委托模型,因为类路径下有Dog.class文件,所以AppClassLoader就可以加载Dog类。故此加载Dog类的ClassLoader是AppClassLoader。写到这里,大家对类加载已经有了一个非常深刻的理解。那么java为什么使用双亲委托模型的好处我相信已经不言而喻了。那么下面来说说双亲委托模型,有没有他的弊端呢,或者说有什么不好的地方嘛?我们可以打破这种双亲委托的方式去加载类嘛?下面我们来看一个例子。

类加载器的命名空间

说到双亲委托模型的弊端,那我就离不开命名空间的概念。

类加载器的命名空间 是由类加载器本身以及所有父加载器所加载出来的binary name(full class name)组成.

①:在同一个命名空间里,不允许出现二个完全一样的binary name。

②:在不同的命名空间种,可以出现二个相同的binary name。当时二者对应的Class对象是相互不能感知到的,也就是说Class对象的类型是不一样的。

解释:同一个Person.class文件 被我们的不同的类加载器去加载,那么我们的jvm内存中会生成二个对应的Person的Class对象,而且这二个对应的Class对象是相互不可见的(通过Class对象反射创建的实例对象相互是不能够兼容的不能相互转型**

③:子加载器的命名空间中的binary name对应的类中可以访问 父加载器命名空间中binary name对应的类,反之不行

下面准备了一张图,以便于大家的理解。

JVM的艺术—类加载器篇(二)

上面这张图就很好的解释了命名空间的概念。大家可以再好好的体会一下。

我们光画图,光用嘴说并不是一种很有力的证据,就如同我写在这篇博文的时候所提,我们在学习和掌握某个概念的时候,就必须要拿出有力的证据,来证明自己的猜想或者是观点,那我们就举一个例子。来验证一下我们上面的理论是否正确。代码如下:

这是Person类的代码。

package com.test; public class Person { public Person() { new Dog(); System.out.println("Dog的classLoader:-->"+ Dog.class.getClassLoader()); } static{ System.out.println("person类被初始化了"); } }

这是Dog类的代码。

package com.test; public class Dog { public Dog(){ System.out.println("Dog 的构造函数"); } }

具体的验证思路是这样的,首先我们把Person类的Class文件放到启动类加载器的加载目录下(C:\Program Files\Java\jdk1.8.0_144\jre\classes 这是启动类加载器的加载目录)来达到Person类交给启动类加载器加载的目的。

然后呢,我们让Dog类去被AppClassLoader(系统类加载器去加载)。然后我们在Person类中去访问Dog类。看看能否访问成功。

测试环境:把我们的Person.class放置在C:\Program Files\Java\jdk1.8.0_131\jre\classes这个目录下,那么我们的Person.class就会被我们的启动类加载器加载,而我们的Dog类是被AppClassLoader进行加载,我们的Person类 中引用我们的Dog类会抛出异常.

创建main方法进行测试:

package com.test; import java.lang.reflect.Method; /** * jvm 类加载器 第一章 * @author 奇客时间-时光 * 自定义类加载器——命名空间 * 测试父加载所加载的类,不能访问子加载器所加载的类。 */ public class MainClass02 { public static void main(String[] args) throws Exception { System.out.println("Person的类加载器:"+Person.class.getClassLoader()); System.out.println("Dog的类加载器:"+Dog.class.getClassLoader()); Class<?> clazz = Person.class; clazz.newInstance(); } } 运行结果: "C:\Program Files\Java\jdk1.8.0_144\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=59226:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar;I:\jvm\out\production\jvm-classloader" com.test.MainClass02 Person的类加载器:null Dog的类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2 person类被初始化了 Exception in thread "main" java.lang.NoClassDefFoundError: com/test/Dog at com.test.Person.<init>(Person.java:7) 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.test.MainClass02.main(MainClass02.java:20) Process finished with exit code 1

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgyxwp.html