Java类加载器

来源:互联网 发布:office2010破解软件 编辑:程序博客网 时间:2024/05/17 22:33

1.   系统加载器简介

  Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器(BootStrap、ExtClassLoader、AppClassLoader),每个类加载器负责加载特定位置的类。

  类加载器本身也是Java类(BootStrap除外),因为它本身也要被类加载器加载,这样显然一定有第一个类加载器不是Java类,没错,正是BootStrap类加载器。它是由C++语言编写的,嵌在了Java虚拟机内核中的类加载器,当启动Java虚拟机时,它就被加载了。

2.    类加载器的结构与管辖范围

  Java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织。在实例化一个类加载器对象时都需要为其指定一个父级类加载器对象,或者默认采用系统类加载器为其父级类加载。类加载器的树形结构与管辖范围如下图:


  


3.    类加载器的委托机制

      当Java虚拟机加载某一个类时,到底派出哪个类去加载呢?加载时遵循如下几个原则:

  原则1:首先派出当前线程的类加载器加载类

  原则2:每个类加载器加载类时又先委托给其上级加载器。当所有的祖宗加载器没有加载到类,才回到发起者加载器。如果还没有加载到类,则将会抛出ClassNotFoundException。不会再去找发起者加载器的儿子,因为没有getChild方法,即使有,那么多个儿子(父类只有一个),找哪一个呢?

  原则3:如果类A引用了类B,那么Java虚拟机将使用加载类A的加载器来加载类B。

  原则4:还可以直接指定某个加载器来加载类,如:ClassLoader.loadClass()。

  注意,每个ClassLoader本身分别只能加载特定位置和目录中的类。但它们可以委托其它类加载器去加载类,这就是类加载器的委托模式。类加载器一级一级委托到BootStrap类加载器,当BootStrap无法加载当前所要加载的类时,然后才一级一级退回到子孙加载器去加载该类。当退回到最初的类加载器时,如果它自己也不能完成类的加载,那么会抛出ClassNotFound异常。

4.    举例1

  首先我们定义一个空类TestClassLoader,代码如下:

package com.tgb.ClazzLoaders;public class TestClassLoader {}

  然后我们再定义一个测试类TestMain来输出TestClassLoader类的类加载器名称:

package com.tgb.ClazzLoaders;public class TestMain {public static void main(String[] args) throws Exception {// 输出类TestClassLoader的当类加载器的名称System.out.println(TestClassLoader.class.getClassLoader().getClass().getName());}}

  输出结果如下,为sun.misc.Launcher$AppClassLoader:


  


  然后我们将类TestClassLoader打成一个jar包放到当前使用的jre\lib\ext目录下,如下图:


  


  再次运行测试类TestMain,我们可以惊奇的发现输出结果变为了sun.misc.Launcher$ExtClassLoader。这正验证了我们上面的类加载器委托机制。当加载TestClassLoader类时,当前的类加载器会向父级加载器一级一级委托,然后退回到ExtClassLoader时,它在自己的管辖范围内jre\lib\ext\*.jar,可以找到TestClassLoader这个类。然后就将它加载了。也是就说我们运行时用到的TestClassLoader类已经不是Eclipse中我们看到的这个类了,而是jre\lib\ext目录下我们打的TestClassLoader.jar中的类。

  注意:一定要放到我们当前使用的jre目录下,否则不起作用,如下步骤可确认,右击项目---- >属性--->Run/Debug Settings找到自己的configuration,然后Edit查看JRE,如下图:


  


  我将jre\lib\ext目录下我们打的TestClassLoader.jar删除掉,然后改造测试类,循环输出类TestClassLoader的当类加载器的名称,以及所有父类加载器的名称。代码如下:

package com.tgb.ClazzLoaders;public class TestMain {public static void main(String[] args) throws Exception {// 输出类TestClassLoader的当类加载器的名称,以及所有父类加载器的名称ClassLoader loader = TestClassLoader.class.getClassLoader();while (loader != null) {System.out.println(loader.getClass().getName());loader = loader.getParent();}}}

  结果如下:


  


5.    举例2

  编写一个能打印出自己的类加载器和当前类加载器的父子结构关系链的MyServlet,正常发布后,看到打印结果如下:


  


  把MyServlet文件打Jar包,放到ext目录中,重启tomcat.发现找不到Httpservlet的错误。把servlet.jar也放到ext目录中.问题解决了,打印的结果是ExtclassLoader。由此说明,父级类加载器加载的类无法引用只能被子级类加载器加载的类。如下图:


  


6.    总结

      系统默认三个主要类加载器(BootStrap、ExtClassLoader、AppClassLoader),每个类加载器负责加载特定位置的类。Java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织。类加载器有一定的委托机制。此外,我们可以自定义自己的类加载器(继承ClassLoader),然后指定类加载器的管辖范围(加载目录),然后我们就可以在类加载的时候对类进行一些特殊处理(例如加密)。

1 0
原创粉丝点击