1 类的加载过程
用Java语言编写的源代码会被翻译成以class为后缀的文件,这种class文件是一种字节码文件。那么,JVM是如何把这种字节码文件加载进内存的呢?
JVM是通过一个称为ClassLoader东西来加载class文件的,每当JVM启动,它就会生成三个ClassLoader,它们分别是Bootstrap Loader,ExtClassLoader和AppClassLoader。这三个ClassLoader作用是不同的,它们所加载的class文件也是不同的。
(1)Bootstrap Loader是用C++语言实现的,它所加载的是JVM中最底层的类,它加载时的搜索路径是由sun.boot.class.path所指定的。
(2)ExtClassLoader是用来加载Java的一些库的,它加载时的搜索路径是由java.ext.dirs来决定的,该加载器在加载时不同于其他加载器,它加载时会搜索指定路径下的所有子目录,搜索java.ext.dirs所指定下的所有子目录下的class文件或jar文件。同时也可以用参数-Djava.ext.dirs来改变它的搜索路径。
(3)AppClassLoader也称System ClassLoader,它的搜索路径是由java.class.path来指定的,而且要注意AppClassLoader不会搜索java.class.path下的子目录的。
ExtClassLoader 和AppClassLoader在中只会存在一个实例,一旦运行了是无法改变它们的搜索路径的,也就是无法在程序中通过调用System.setProperty()来改变java.class.path或java.ext.dirs来改变它们的搜索路径的。
JVM加载class文件时采用了双亲委托模型,当需要加载一个class文件时,JVM会首先让这个加载器的父亲去加载(父类加载器指的是ExtClassLoader),如果父亲无法加载,JVM会让父亲的父亲去加载(父类的父类指的是BootstrapClassLoader),如果仍然无法加载,则让最初的加载器去加载(委托加载后如果都不成功就调用AppClassLoader加载)。这三个加载器的委托关系如图:
从上图可以看出,每当加载我们自己所编写的class文件时,首先会交给AppClassLoader的父亲ExtClassLoader去加载,如果它不能加载则会交给ExtClassLoader的父亲Bootstrp Loader去加载,如果仍无法加载的情况下则交给AppclassLoader加载。
在加载完Class文件之后,ClassLoader如何将class文件变成一个java类的。在ClassLoader类中有一个非常重要的方法叫findClass,它接收要加载的类的名字作为它的参数,在该方法里会找到class文件并读取文件中的内容到一个byte数组,然后再调用另外一个重要的方法defineClass,该方法能够将byte数组中的内容转化成一个相应的Class Object。defineClass方法的调用是通过JNI方式的,它的实现是用C++语言实现的本地化方法。
2 类加载方式
(1)显示加载发生在使用以下方法调用进行装载类的时候:
(1)ClassLoader.loadClass():使用指定的Classloader进行装载,结果返回一个类。
(2)Class.forName():使用当前类的Classloader进行装载,结果也是返回一个类。
(2)隐式类装载发生在由于引用、实例化或继承导致需要装载类的时候。
隐式类装载是在幕后启动的,JVM会解析必要的引用并装载类。使用new关键字创建实例。
3 类加载的委托机制的好处-安全性
JVM装载类使用的是“全盘负责委托机制”。
全盘负责,指当一个ClassLoader装载类的时候,除非显示的使用另外一个ClassLoader,该类所依赖和引用的其他类都由这个ClassLoader载入。
委托机制,首先委托父装载器寻找目标类,只有在找不到的情况下才从自己的类路径中查找并装载类,这一点是从安全角度考虑的,试想如果有人编写了一个恶意的基础类,比如String类,并装载到JVM中将会引起多么可怕的后果呢。但是,由于有了全盘负责委托机制,String类永远是由根装载器装载,这样就避免了事件的发生。另外,我们经常遇到的NoSuchMethodError的错误往往就是因为加载了不同版本的包造成的。
public class Person {
static
{
System.out.println("I am loading");
}
}
public class Test {
public static void main(String[] args) {
Person p = new Person ();
Class pclass = p.getClass();
ClassLoader cl = pclass.getClassLoader();
System.out.println("person类加载器"+cl);
System.out.println("person类加载器的父类加载器"+cl.getParent());
System.out.println("person类加载器的祖父类加载器"+cl.getParent().getParent());
}
}