类加载的生命周期包括:加载、验证、准备、解析、初始化、使用、卸载。
在加载阶段,虚拟机主要完成以下3件事:
1.通过一个类的全限定名来获取定义此类的二进制字节流;
2.将这个字节流所代表的静态储存结构转化为方法区的运行时数据结构;
3.在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据访问入口。
虚拟机设计团队把类加载阶段中“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类,实现这个动作的代码模块称为“类加载器”。(深入理解java虚拟机P227)
类加载器虽然只用于实现类的加载动作,但它在java程序中起到的作用缺远远不限于类的加载阶段;对于任意一个类,都选哟由加载它的类加载器和这个类本身一同确立其在java虚拟机中的唯一性;每一个类加载器,都拥有一个独立的类名称空间。
双亲委派模型的实现
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { //首先,检查请求的类是否已经被加载过了 Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClass0(name); } } catch (ClassNotFoundException e) { // 如果父类加载器抛出ClassNotFoundException // 说明父类加载器无法完成加载请求 // 在父类加载器无法加载的时候 // 再调用本身的findClass方法来进行类加载 c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }
自定义类加载器时,从上面对于java.lang.ClassLoader的loadClass(String name, boolean resolve)方法的解析来看,可以得出以下2个结论:
1、如果不想打破双亲委派模型,那么只需要重写findClass方法即可
2、如果想打破双亲委派模型,那么就重写整个loadClass方法
当然,我们自定义的ClassLoader不想打破双亲委派模型,所以自定义的ClassLoader继承自java.lang.ClassLoader并且只重写findClass方法;另外注意一下defineClass方法可以把二进制流字节组成的文件转换为一个java.lang.Class----只要二进制字节流的内容符合Class文件规范。