Java中类的加载
Java中class的加载,这里的加载
值指的是一个宏观的过程,其中又分为几个小的过程:加载(Loading)、链接(Linking)、初始化(Initialization)
加载(Loading)
- 找到文件,class文件是一个字节流文件
- 将里面的静态存储结构转换为方法区(JDK7及之前叫做Perm Gen,JDK8及之后叫做Meta space)的运行时数据结构
- 在内存中生成一个代表当前加载类的java.lang.Class对象,作为访问入口,这个Class相当于一个模板,创建Class的Object时,就是从这个模板创建
链接(Linking)
链接又分为了三个小阶段:验证(Verification)、准备(Preparation)、解析(Resolution)
验证(Verification)
目的是确保Class文件的字节流中包含的信息符合当前的Java虚拟机要求,保证被加载类的正确性并不会危机自身虚拟机的安全。
包括四种验证方式:
- 文件格式验证
- 元数据验证
- 字节码验证
- 符号引用验证
准备(Preparation)
- 为类变量分配内存,并设置初始值,叫做零值,基本类型如int设置为0,boolean类型设置为false等等,引用类型则设置为null。这里的操作不会作用于常量(static final),因为常量在编译时会确定内存大小(因为是常量嘛,以后不会改变了),这里只是将真正的值赋给它。
比如
private static final String msg = "Hello world.";
这里是直接将
"Hello world."
给msg
,而不是先给一个null后面再赋值。
- 此时肯定不会为实例的变量分配内存和初始值,因为此时并没有到创建对象这个环节。
解析(Resolution)
- 将常量池里面的符号引用转换为直接引用
- 其他的放张图吧,不想敲了
初始化(Initialization)
- 初始化就是执行类的clinit方法,这个方法是在javac编译的时候自动收集类的信息自动生成的,一般收集的是类变量和静态代码块,然后按照顺序执行
如何理解按照顺序执行呢?比如有以下代码:
public class ClinitTest {
static int a = 1;
static {
a = 2;
b = 20;
}
static int b = 10;
public static void main(String[] args) {
System.out.println(a); // 2
System.out.println(b); // 10
// 打印结果分别是 2 和 10
}
}
- 在执行类的clinit方法时,如果当前类存在父类,那么会先执行父类的clinit方法9(如果有)
- 虚拟机会保证一个类的clinit只执行一次(加锁),即使有多个线程同时做初始化操作