java类的生命周期
-
类的生命周期的完整过程
加载->连接(验证->准备->解析)->初始化 ->使用->卸载 -
加载:将类的.class 文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Class 对象,用来封装类在方法区内的数据结构。
类的加载方式比较灵活,一般常用的加载方式有两种:1. 加载class文件,从class文件中读取文件内容;2. 从jar文件中读取。
-
连接:类加载后的验证和初始化前的准备
3.1 验证:确保加载的类能够被JVM运行。
当一个类被加载后,验证这个类是否合法。比如:类是否符合字节码格式、变量与方法是否重复、数据类型是否有效、继承与实现是否符合标准等。
3.2 准备:为类的静态变量分配内存并设置默认值。
jvm默认值:
- 基本类型:int、short、long、char、byte、boolean、float、double默认值为0
- 引用类型:默认值null
- 常量默认值为在程序中设定的值。
3.3 解析:常量池中的符号引用转换为直接引用。
例如:要找内存中寻找类中的一个add()方法,在解析阶段以前是找不到的,但是在解析阶段会将add()方法的名字指向方法区中的一块内存地址,通过内存地址就可以找到add()方法具体分配在内存的哪个区域。add()就是符号引用,内存地址就是直接引用。
在解析阶段,JVM会将所有类或者接口名、字段名、方法名转换为具体的内存地址。
连接阶段完成后,会根据类的使用情况(直接引用or被动引用)选择是否对类进行初始化。
4.初始化:初始化类的静态变量为正确的初始值。
如果类被直接引用就会触发类的初始化。
4.1 直接引用:
- 通过new关键字实例化的对象,读取或者设置类的静态变量,调用类的静态方法。
- 通过反射执行以上三种行为
- 初始化子类的时候,触发父类的初始化
- 作为程序入口直接运行(也就是直接调用main方法)
除了以上四种情况,其他使用类的方式叫被动引用,被动引用不会触发类的初始化。
4.2 类初始化的方式
-
在变量的声明处赋值
-
在静态代码块赋值。
静态变量的声明和静态代码块的初始化都可以看做静态变量的初始化,类的静态变量的初始化是有顺序的。顺序为类文件从上到下进行初始化。