类的生命周期

引言:

java虚拟机为java程序提供运行时环境,其中一项重要的任务就是管理类和对象的生命周期。类的生命周期从类被加载、连接和初始化开始,到类被卸载结束。当类出于生命周期中时,它的二进制数据位于方法区内,在堆区内还会有一个相应的描述这个类的Class对象。只有当类处于生命周期中时,java程序才能使用它,比如调用类的静态属性和方法,或者创建类的实例。


java虚拟机及程序的生命周期


当通过java命令运行一个java程序时,就启动了一个java虚拟机进程。java虚拟机进程从启动到终止的过程,成为java虚拟机的生命周期。在一下情况下,java虚拟机将结束生命周期:

1、程序正常执行结束;

2、程序在执行中因为出现异常或错误而异常终止;

3、执行了System.exit()方法;

4、由于操作系统出现错误而导致java虚拟机进程终止。

当java虚拟机处于生命周期中时,它的任务就是运行java程序。java程序从开始运行到终止的过程为程序的生命周期,它和java虚拟机的生命周期是一致的。


类的加载、连接和初始化

当java程序需要使用某个类时,java虚拟机会确保这个类已经被加载、连接和初始化。其中连接的过程又包括验证、准备和解析这三个步骤。

加载:查找并加载类的二进制数据

           类的加载是指把类的.class文件中的二进制数据读入到内存中,把它存放在运行时数据区的方法区内。然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于运行时数据区的堆区的Class对象。Class对象封装了类在方法区内的数据结构,并且向java程序提供了访问类在方法区内的数据结构的接口。


Class对象是java程序与类在方法区的数据结构的接口

类的加载是由类加载器完成的。类加载器并不需要等到某个类被“首次主动使用”时再加载它,java虚拟机规范允许类加载器在预料某个类将要被使用时就预先加载它,如果加载类的过程中遇到.class文件缺失或存在错误,类加载器必须等到程序首次主动使用该类时才报告错误,如果这个类一直没有被使用,那么类加载器将不会报告错误。


类被加载后,进入连接阶段。连接就是把已经读入到内存的类的二进制数据合并到虚拟机的运行时环境中去。连接的第一步就是类的验证,保证被加载的类有正确的内部结构,并且与其它类协调一致,如果java虚拟机检查到错误,会抛出相应的Error对象。

也许你会问:由java编译器生成的java类的二进制数据肯定是正确的,为什么还要进行类的验证呢?这是因为java虚拟机并不知道某个特定的.class文件到底是如何被创建的,可能是正常的java编译器生成的,也有可能是黑客特质的(黑客试图破坏虚拟机的环境),类的验证能提供程序的健壮性,确保程序被安全的执行。

连接:包括验证、准备、解析类的二进制数据

        1、验证:确保被加载类的正确性

              类的验证主要包括:

                 a、类文件的结构检查,确保类文件遵从java类文件的固定格式

                 b、语义检查,确保类本身符合java语言的语法规定,比如验证final类是不是有子类,final 类型的方法有没有被覆盖等等。

                  c、二进制兼容的验证,确保互相引用的类之间的协调一致。比如A类的getOne()方法中会调用B类的run()方法。java虚拟机在验证A类的时候会检查在方法区内是否存在B类的run方法,如果不存在(A类和B类的版本不兼容,就会出现这种问题),就会抛出NoSuchMethodError错误。

         2、准备:为类的静态变量分配内存,并将其初始化为默认值

               在准备阶段,java虚拟机为类的静态变量分配内存,并设置默认的初始值。例如对于下面的Sample类,在准备阶段,将为int类型的静态变量a分配4个字节的内存空间,并且赋予默认值0,为long类型的静态变量b分配8个字节的内存空间,并且赋予默认值0。

public class Sample {

	private static int a = 1;
	public static long b;
	
	static {
		b = 2;
	}
}


         3、解析:把类中的符号引用转换为直接引用

              在解析阶段,java虚拟机会把类的二进制数据中的符号引用替换为直接引用。例如Sample类的gotoWork()方法中会引用SampleTest类的run()方法。

public class Sample {

	private static int a = 1;
	public static long b;
	
	static {
		b = 2;
	}
	public void gotoWork(){
		SampleTest test = new SampleTest();
		test.run();//这段代码在Sample类的二进制数据中表示为符号引用。
	}
}

在Sample类的二进制数据中,包含了一个对SampleTest类的run()方法的符号引用,它由run()方法的全名和相关描述符组成。在解析阶段,java虚拟机会把这个符号引用替换成一个指针,该指针指向SampleTest类的run()方法在方法区内的内存位置,这个指针就是直接引用。

初始化:给类的静态变量赋予正确的初始值

 初始化阶段,java虚拟机执行类的初始化语句,为类的静态变量赋予初始值。在程序中,静态变量的初始化有两种途径:

1、在静态变量的声明处进行初始化;

2、在静态代码块处进行初始化,例如以下代码,静态变量a和b都被显示的初始化,而静态变量c没有被显示初始化,一直保持默认值0。

	private static int a = 1;
	public static long b;
	public static long c;
	
	static {
		b = 2;
	}
}

静态变量的声明语句,以及静态代码块都被看作类的初始化语句,java虚拟机会按照初始化语句在类文件中的先后顺序来一次执行它们。例如当以下Sample类被初始化后,静态变量a的值为4。

public class Sample {

	private static int a = 1;
	public static long b;
	public static long c;

	static {
		b = 2;
		a = 3;
	}
	static {
		a = 4;
	}

	public static void main(String[] args) {
		System.out.println("a = " + a);
	}

}

java虚拟机初始化一个类包含以下步骤:

1、假如这个类还没有被加载和连接,那就先进行加载和连接。

2、假如类存在直接的父类,并且父类还未被初始化,那就先初始化父类

3、加入类存在初始化语句,那就依次执行初始化语句。

当初始化一个父类时,也需要重复以上步骤,这会确保当程序主动使用一个类时,这个类及所有父类(包括直接父类和间接父类)都已经被初始化。程序中第一个被初始化的类是Object类。例如以下代码中,Sub类是Base类的子类,因此当初始化Sub类的时候,会先初始化Base类。

class Base{
	static int a = 1;
	static {
		System.out.println("init Base");
	}
}
class Sub extends Base{
	static int b = 1;
	static {
		System.out.println("init Sub");
	}
}

public class InitTester {

	static {
		System.out.println("init InitTester");
	}
	public static void main(String[] args) {
		System.out.println("b="+Sub.b);//执行这行代码时,先一次初始化Base类和Sub类
	}
}

当运行InitTester类的main方法时,当访问Sub.b的时候,先依次初始化Base类和Sub类。以上程序执行结果为:

init InitTester
init Base
init Sub
b=1


如果把以上main方法做如下修改:

public static void main(String[] args) {
		Base base;//不会初始化Base类
		System.out.println("After defining base");
		base = new Base();//执行这行代码时,初始化Base类
		System.out.println("After createing an object of Base");
		System.out.println("a = "+base.a);
		System.out.println("b = "+Sub.b);//执行这行代码时,仅仅初始化Sub类,因为Base类已经初始化过了
	}

所以以上程序的打印结果为:

init InitTester
After defining base
init Base
After createing an object of Base
a = 1
init Sub
b = 1




发布了33 篇原创文章 · 获赞 1 · 访问量 4万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章