详解Java中类的加载与其初始化
Java虚拟机在运行Java程序的时候,会先将程序中所有需要用到的类加载进内存。而类的加载并不仅仅指从磁盘读取class文件并转化为二进制码,还包括链接、初始化等操作。本文会对Java中类的加载、连接、初始化过程进行详细阐述,并通过两条示例进行说明。
类的加载
类的加载通常在Java程序运行过程中进行,加载的方式有以下几种:
- 从本地系统中直接加载已经存在的class文件。
- 通过网络下载class文件,例如在Java Applet中。
- 从zip或jar等归档文件中加载class文件。
- 通过专门的类加载器动态生成类字节码,例如JSP页面中的脚本。
Java的类加载器采用的是一种双亲委派的机制。即在类的加载过程中,会先将请求交给父加载器进行处理,如果父加载器无法处理,才由自己来处理。父加载器和子加载器之间是一种包含与继承的关系,子加载器所加载的类可以访问父加载器所加载的类,反之则不行。
类的连接
类的连接可以分为以下三步:
- 验证:确保被加载的类的正确性。
- 准备:为类的静态变量分配内存,并将其初始化为默认值。
- 解析:把类中的符号引用转换为直接引用。
其中验证和解析两步暂不展开说明,准备阶段是为类的静态变量分配内存,并将其初始化为默认值,如int类型的默认值为0,对象类型的默认值为null。注意,除了被声明为final的static变量外,这里所指的“初始化”并不是指类中定义的初始化方法(例如构造方法或静态块)。
类的初始化
只有当对类进行第一次主动使用时,才会触发类的初始化。主动使用包括以下几种情况:
- 创建类的实例对象。
- 调用类的静态方法或静态变量。
- 使用反射方式来创建某个类的对象。
- 使用反射方式来调用某个类的静态方法或静态变量。
- 初始化某个类的子类。
- Java虚拟机启动时被标明的启动类。
类初始化的过程包括以下步骤:
- 首先执行所有声明为final的static常量的赋值语句(如果这些常量还未被赋值)。
- 执行static块。
- 对于非final的static变量,执行赋值语句(如果这些变量还未被赋值)。
在类初始化的过程中,如果某个已被加载的类在其父加载器或其它类加载器中已经被初始化,则不会再次触发初始化。如果一个类在初始化过程中产生了异常,则该类会初始化失败。
示例
下面通过两条示例说明类的加载、连接与初始化过程。
示例一
public class Example {
static {
System.out.println("Example's static block");
}
}
在运行主程序时,虚拟机首先会尝试加载Example类,因为Example类还未被加载过,所以进行了如下过程:
- 加载Example类。
- 准备Example类的静态变量,此时Example类尚未初始化,且没有父类,所以在准备阶段,jvm会为Example类的静态变量分配内存,并将其初始化为默认值。
- 开始初始化Example类,先执行所有声明为final的static常量的赋值语句(如果这些常量还未被赋值),但Example类中并不存在这样的常量。
- 执行静态块,输出“Example's static block”。
示例二
public class Example2 {
public static void main(String[] args) {
System.out.println("Example2.main");
}
}
在运行主程序时,虚拟机首先会尝试加载Example2类,因为Example2类还未被加载过,所以进行了如下过程:
- 加载Example2类。
- 准备Example2类的静态变量,此时Example2类尚未初始化,且没有父类,所以在准备阶段,jvm会为Example2类的静态变量分配内存,并将其初始化为默认值。
- 开始初始化Example2类,先执行所有声明为final的static常量的赋值语句(如果这些常量还未被赋值),但Example2类中并不存在这样的常量。
- 执行main方法所在的类Example2的静态方法print,输出“Example2.main”。
总结
类的加载、连接、初始化是Java虚拟机运行Java程序的重要流程。类的加载是指在程序运行的过程中,将需要用到的类加载进内存,Java的类加载器采用的是一种双亲委派的机制。类的连接包括验证、准备和解析三个操作。类的初始化只有在主动使用时才会触发,其中被声明为final的static变量除外。在类初始化的过程中,如果某个类已被加载并初始化,则不会再次触发初始化。这里通过两个简单的示例进行了说明。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解Java中类的加载与其初始化 - Python技术站