浅谈Java之类加载
一、前言
现实生活中,想要了解一个人就不能只了解ta的外在而是要深入内心,学习Java亦是如此,除了了解其基本语法格式之外,更要了解其底层的运行机制。因此在本篇文章中我将对Java的类加载机制进行简单描述。
二、一句话概括Java代码的执行流程
Java作为混合型语言,Java文件会通过javacJava编译器)编译为JVM能看懂的字节码文件(.class文件),然后JVM通过解释器和即时编译器的相互配合,合理地将其转换为机器码并执行。
三、浅谈类加载
1、什么是类加载
类加载就是JVM加载并处理字节码文件的过程。
2、类加载的基本流程
- 类的加载
- 类的链接
- 类的初始化
(1)类的加载
通过类加载器将字节码文件加载到内存中,类的加载产生的是Class对象,并将静态数据转换成方法区中的运行时数据。
注:需要注意的是,应将此处的类的加载与本篇文章所讲的类加载作出区分。
(2)类的链接
类的链接分为三个阶段:
- 验证阶段:进行字节码文件的数据验证,包括文件格式验证、元数据验证、字节码验证、符号引用验证,以保证数据符合JVM规范。
- 准备阶段:正式为类的静态变量分配内存并根据类型赋默认值。
- 解析阶段:将虚拟机常量池中发符号引用替换为直接引用。
(3)类的初始化
借助类构造器clinit()方法初始化类,执行静态成员变量的赋值语句以及静态代码块中的语句。
注意类的初始化阶段需要理清楚其子类与父类之间初始化的执行顺序,在执行子类的clinit()方法前父类的clinit()方法一定是执行完的,我们可以通过断点调试来检验。
class Father {
static {
System.out.println("执行父类中的静态代码块...");
}
public Father() {
System.out.println("父类的构造方法执行...");
}
private int age = 10;
private static String name = "father";
private static int num = 0;
}
class Child extends Father {
static {
System.out.println("执行子类中的静态代码块...");
}
public Child() {
System.out.println("子类的构造方法执行...");
}
private int age = 5;
private static String name = "child";
private static int num = 1;
public static void main(String[] args) {
new Child();
}
}
程序开始运行时会我们发现两个类的静态变量以及静态代码块被执行:
从静态代码块的执行顺序中我们不难发现,Father父类先被初始化了。同时我们也能看出非静态的成员变量在类的初始化阶段结束时是并未被赋值的。