ClassLoader除了能将Class加载到JVM中以外,还有一个重要的作用就是审查每个类应该由谁加载,它是一种父优先的等级加载机制。
此外,ClassLoader也会将Class字节码重新解析成JVM统一要求的对象格式。
ClassLoader类结构分析
defineClass 方法用来将byte字节流解析成JVM能够识别的Class对象。有了这个方法意味着我们不仅仅可以通过class文件实现实例化对象,还可以通过其他方式实例化对象,如果我们通过网络接收到一个类的字节码,就可以拿这个字节码流直接创建类的Class对象形式化实例对象。
注意,如果直接调用这个方法生成类的Class对象,这个类的class对象还没有resolve,这个resolve将会在这个对象真正实例化的时候才进行。
ClassLoader的等级加载机制
整个JVM平台提供三层ClassLoader,这三层ClassLoader可以分为两种类型,可以理解为为接待室服务的接待室和为会员服务的接待室两种。
(1) Bootstrap ClassLoader,这个ClassLoader就是接待室服务自身的,它主要加载JVM自身工作需要的类,这个ClassLoader完全是由JVM自己控制的,需要加载哪个类,怎么加载都有JVM自己控制,别人也访问不到这个类,所以这个ClassLoader是不遵守前面介绍的加载规则的,它仅仅是一个类的加载工具而已,既没有更高一级的父加载器,有没有子加载器。
(2) ExtClassLoader,这个类加载器有点特殊,它是JVM自身的一部分,但是它的血统也不是很纯正,它并不是JVM亲自实现的。我们可以理解为这类加载器是那些与这个大会合作单位的员工会员,这些会员既不是JVM内部的,也和普通的外部会员不同,所以就由这个类加载器来加载。它服务的特定目标在System.getProperty("java.ext.dirs")目录下。
(3) AppClassLoader,这个类加载器就是专门为接待会员服务的,它的父类是ExtClassLoader。它服务的目标是广大普通会员,所有在System.getProperty("java.class.path"); 目录下的类都可以被这个类加载器加载,这个目录就是我们经常用到的classpath。
应用中类加载器的等级层次。
ExtClassLoader的父类也不是Bootstrap ClassLoader ,ExtClassLoader并没有父类,我们在应用中能提取到的顶层父类是ExtClassLoader。
JVM加载Class文件到内存中的两种方式
隐式加载:
所谓隐式加载就是不通过在代码里调用ClassLoader来加载需要的类,而是通过JVM来自动加载需要的类到内存的方式。例如,当我们在类中继承或者引用某各类时,JVM在解析当前这个类时发现引用的类不在内存中,那么就会自动将这些类加载到内存中。
显式加载:
相反的显示加载就是我们在代码中通过调用ClassLoader类来加载一个类的方式,例如,调用
this.getClass.getClassLoader().loadClass()或者Class.forName(),或者我们自己实现的ClassLoader的findClass()方法等。
其实这两种方式是混合使用的,例如,我们通过自定义的ClassLoader显示加载了一个类时,这个类中又引用了其他类,那么这些类就是隐式加载的。
如何加载class文件
ClassLoader加载一个class文件到JVM时需要经过的步骤
第一个阶段:找到.class文件并把这个文件包含的字节码加载到内存中。
第二个阶段:又可以分为三个步骤,分别是字节码验证,Class类数据结构分析及相应的内存分配和最后的符号表的链接。
第三个阶段:类中静态属性和初始化赋值,以及静态块的执行等。
常见加载类错误分析
在执行Java程序时经常会碰到ClassNotFoundException和NoClassDefFoundError两个异常,它们都和类加载有关。