类加载机制概述:虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,解析和初始化,最终形成可以被虚拟机直接使用的java类型,这就是java虚拟机的机制
1. Java程序执行时遵循以下顺序
1.1. 加载:将类的.class文件中的二进制数据装载到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Object对象,用来封装类在方法区的数据结构
1.1.1. 加载.class方式:
1.1.1.1. 从本地系统中加载
1.1.1.2. 通过网络下载.class
1.1.1.3. 从zip、jar等文件中加载.class
1.1.1.4. 从数据库中提取.class
1.1.1.5. 将Java源文件动态编译成.class
1.2. 连接:
1.2.1. 验证:确保被加载的类的正确性(防止恶意用户手工构造.class文件)
1.2.1.1. 类文件的结构检查:确保类文件遵从Java类文件的固定格式
1.2.1.2. 语义检查:确保类本身符合Java语言的语法规则(例如:final类型的类没有子类)
1.2.1.3. 字节码验证:确保字节码流(包括静态方法和实例方法)能被Java虚拟机安全执行
1.2.1.4. 二进制兼容性验证:确保相互应用的类之间协调一致(例如:NosuchMethodError)
1.2.2. 准备:为类的静态变量分配内存,并将其初始化为默认值
1.2.3. 解析:把类中的符号引用转换为直接引用:
public class Car{ public void run(){} } public class Worker{ public void work(){ Car car = new Car(); car.run(); //这段代码在Worker类的二进制数据中表示为符号引用 } }
在Worker类的二进制数据中,包含了一个对Car类的run()方法的符号引用,它由run()方法的全名和相关描述组成。
在解析阶段,Java虚拟机会把这个符号引用替换为一个指针,该指针指向Car类的run()方法在方法区内的内存位置,这个指针就是直接引用
1.3. 初始化:为类的静态变量赋予正确的初始值
1.3.1. 静态变量初始化方式:
1.3.1.1. 在静态变量声明处初始化
1.3.1.2. 在静态代码块中初始化
Notice : 准备的初始化为默认值与初始化赋予正确的初始值不是一个意思
public class Student { private static int a = 1; }
准备阶段,a的默认值是0,初始化的阶段a才等于3
上面这个过程等同于以下代码:
public class Student { private static int a; static{ a = 3; } }
2. 只有静态变量在准备阶段才被初始化,那么类在什么时候初始化呢?
public class Handsome{ public static int a =1; public static void sayHi(){} } public class Child extends Handsome{
private static int b = 2; public static void main(String [] args){
Child.b = 3;
} }
Java程序对类的使用方式分为两种:
1.1. 主动使用(6种)
1.1.1:创建类的实例 --> new Handsome()
1.1.2:访问某个类或接口的静态变量,或者对该静态变量赋值 --> Handsome.a = 2
1.1.3:调用类的静态方法 --> Handsome.sayHi()
1.1.4:反射 --> Class.forName("Handsome")
1.1.5:初始化一个类的子类 --> Child.b = 3
1.1.6:Java虚拟机启动时被标明为启动类的类 --> 含main方法启动的类
1.2. 被动使用: 除开上面6种的其他方式,都不会导致类被初始化
3. 每个类或接口被Java程序“首次主动使用”时才初始化
下一篇:7.2类加载器