类加载的过程:
1. 加载:通过一个类的全限定名来获取定义此类的二进制字节流;将这个字节流所代表的静态存储结构转化成方法区的运行时数据结构;生成一个代表这个类的java.lang.Class对象,作为方法区的这个类的各种数据的访问入口。
2. 连接(加载阶段与连接阶段的部分内容是交叉进行的)
2.1 验证:为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求而且不会危害虚拟机自身的安全
2.2 准备:为类变量分配内存并设置初始值的阶段(这里说的初始值通常是0值,类常量除外)
2.3 解析:虚拟机将常量池中的符号引用替换为直接引用的过程。符号引用以一组符号来描述所引用的目标,符号引用可以是任何形式的字面量。直接引用可以是直接指向目标的指针、相对偏移量或者一个能直接定位到目标的句柄。
3. 初始化:真正开始执行类中的Java程序代码(或者说是字节码)。从另一个角度来说,初始化过程就是执行<clinit>()方法的过程。<clinit>()方法是由编译器自动收集类中的所有静态语句块和静态变量赋值语句合并而生成的。(静态语句块中只能对定义在它之后的静态变量进行赋值,而不能进行访问)
对于初始化阶段,虚拟机规范严格规定了有且只有5种情况必须立即对类进行初始化(也可以说是对类进行主动引用时必须进行初始化)
3.1 使用new关键字实例化对象的时候、读取或者设置一个类的静态字段、调用一个类的静态方法(静态常量除外)。
3.2 利用java.lang.reflect包中的方法对类进行反射调用的时候,如果类没有进行初始化,则必须先触发其初始化。
3.3 当初始化一个类时,如果其父类还没有进行初始化,则需要先触发其父类的初始化。
3.4 当虚拟机启动时,包含main方法的那个主类要先进行初始化。
类加载器:
用来实现“通过一个类的全限定名来获取描述此类的二进制字节流”。
对于一个类来说,这个类的类加载器与这个类本身一起确立其在虚拟机中的唯一性。(比较两个类是否相等,必须是在这两个类由同一个类加载器加载的前提下)
双亲委派模型:从虚拟机的角度来说,只存在两种不同的加载器。一种是启动类加载器(BootstrapClassloader),这个类加载器由C++语言实现,是虚拟机自身的一部分;另一种就是其他所有的类加载器,由Java语言实现,独立于虚拟机外部,并且继承自java.lang.Classloader这个抽象类。(也可以将加载器分为启动类加载器、扩展类加载器和应用程序类加载器)
工作过程:一个类加载器收到了一个类加载请求,它不会先去尝试加载,而是将请求转发给它的父类加载器,每一个层次的类加载器都是如此,因此所有的类加载请求都会被转发给位于顶层的启动类加载器中。只有当父类反馈自己无法完成这个请求时(它的搜索范围内没有找到所需的类)时,子加载器才会尝试自己去加载这个类。(实际中,先检查类是否被加载,若没有,则调用父加载器的loadClass()方法,若父加载器为空则将启动类加载器作为父加载器。如果父加载器加载失败抛出ClassNotFoundException异常后,在调用自己的findClass()方法进行加载。)