类的生命周期
如下图所示,类的整个生命周期包括了:加载、验证、准备、解析、初始化、使用和卸载这7个阶段。其中,验证、准备和解析这三个部分统称为连接。
加载
加载阶段是将class加载到内存的过程,主要完成以下三件事情:
- 根据类的全限定名查找定义此类的二进制字节流;
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;
- 在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口。
加载阶段即可以使用系统提供的类加载器在完成,也可以由用户自定义的类加载器来完成。加载阶段与连接阶段的部分内容(如一部分字节码文件格式验证动作)是交叉进行的,加载阶段尚未完成,连接阶段可能已经开始。
连接
验证
验证是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
准备
准备阶段是为类的静态变量分配内存并将其初始化为默认值,这些内存都将在方法区中进行分配。如 public static int value=123;//在准备阶段value初始值为0 。在初始化阶段才会变为123 。
解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
符号引用(Symbolic Reference):符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。符号引用与虚拟机实现的内存布局无关,引用的目标并不一定已经加载到内存中。
直接引用(Direct Reference):直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用是与虚拟机实现的内存布局相关的,如果有了直接引用,那么引用的目标必定已经在内存中存在。
初始化
类的初始化时机
- 创建类的实例;
- 访问类的静态变量(被final修辞的静态变量除外);
- 访问类的静态方法;
- 反射如(Class.forName(“my.xyz.Test”));
- 当初始化一个类时,发现其父类还未初始化,则先触发父类的初始化;
- 虚拟机启动时,定义了main()方法的那个类先初始化;
类的初始化顺序
- 先父类,再子类;
- 同一类中按照声明的先后顺序进行;
class SingleTon {
private static SingleTon singleTon = new SingleTon();
public static int count1;
public static int count2 = 0;
private SingleTon() {
count1++;
count2++;
}
public static SingleTon getInstance() {
return singleTon;
}
}
public class Test {
public static void main(String[] args) {
SingleTon singleTon = SingleTon.getInstance();
System.out.println("count1=" + singleTon.count1);
System.out.println("count2=" + singleTon.count2);
}
}
参考: