本文聊一聊类加载器的应用。我们知道,java文件经过javac指令的编译后会生成class文件,class文件经过java指令被JVM加载之后会在其内部生成描述该class文件的的Class对象。在这个过程中,java指令起的就是类加载的作用。
Java中的类加载有三种,按加载顺序(指的是某class文件需要被加载时按以下顺序寻找应该加载它的加载器)如下:Bootstrap类加载器、Extension类加载器、Application类加载器。其中Bootstrap负责加载系统类,通常是从rt.jar文件中加载。Extension类加载器是从jre/lib/ext目录下加载“标准的拓展”。也就是说这俩类加载器是加载一些Java设计者为我们提供的类。而第三个Application类加载器是加载第三方的jar包和src下我们自己编写的类。
除了这些理论性的东西之外,对于Java EE开发者来说类加载器最大的作用就是获得资源文件的类路径(class文件所在的路径)。获得类路径有三步,获得字节码对象、获得字节码对象的类加载器、获得资源的URL、获得URL中的路径部分。
看完这里例子后其实我们还有一个疑点:字节码对象应该获取谁的?答案是只要是通过Application类加载器加载的类就可以。比如小码农还有一个Second类,打印出来的是相同的效果。当然使用的时候一般使用其所在的类就可以了。
说到这里我们就必须得说一个问题,不是所有类都能通过getClassLoader()方法获得他的类加载器(获取不到返回null),只有Extension类加载器和Application类加载器能通过getClassLoader()方法获得它的类加载器。为什么呢?因为Bootstrap类加载器通常是使用C编写的,它没有对应任何的JClassLoader对象,而ExtClassLoader和AppClassLoader是使用Java写的Java类。
到这里,还剩下一个Extension类加载器没有演示过,下图演示一下。不过Extension类加载器的演示比较困难,小码农在jre/lib/ext下找了好几个类都不能使用,所以采取了一种投机取巧的方法来演示。首先把我们的测试程序打包成jar包,然后拷贝到jre/lib/ext下,这样再运行我们的类时由于先使用Extension类加载器加载,所以会运行jre/lib/ext的文件。如下图所示。
我们获得资源的相对路径一般是为了通过InputStream读取,所以还需要距离实用还有一步读取。完整的操作如下:
然而Java的设计者就特别贴心了,不用我们这么麻烦的操作,它提供了一个getResourceAsStream()方法。
而更贴心的是我们连获取类加载器这一步都可以省略,通过Class类对象就可以直接使用getResourceAsStream(),当然它在底层最终使用的还是类加载器的getResourceAsStream()。
最常用的功能的解析已经结束了,但在Java核心技术卷中把类记载这部分放在了“安全”这一章,这是由于类加载器在加载类到虚拟机中时会检查类的完整性。而且所有的类加载器都会负责控制代码运行安全的安全管理器类进行合作来保证Java的安全性。