现在的位置: 首页 > 综合 > 正文

java 类的生命周期

2013年01月28日 ⁄ 综合 ⁄ 共 2319字 ⁄ 字号 评论关闭

java学习-类的生命周期(一)

2012-07-09

Java 程序需要使用某个类的时候,需要经过以下几个过程:

1、 加载过程:将编译的.class文件加载到内存中,加载途径有多重:本地、网络、jar包等等,类的加载过程是由类加载器完成的,类加载器分为两种:
(1)Java虚拟机自带的加载器
(2)用户自定义的,继承了java.lang.ClassLoader
类加载器并不是等到需要使用类首次使用时候才去加载,java虚拟机规范允许类加载器在预料某个类将要被使用时候就去加载它,如果在加载过程中遇到错误或者类文件不存在,不会立马报错,一直等到使用该类的时候才会报错的,如果一直没有使用,那么一直将不会报错的。
2、 连接阶段:
(1)验证:语法、二进制兼容、字节码验证等等
(2)准备:为静态变量分配内存,并设置默认的初始值
(3)解析:把类中的符号引用转换为直接引用
3、 初始化
(1)在静态变量声明处进行初始化
(2)在静态代码块内进行初始化
Java虚拟机初始化一个类包含以下步骤:

(1)假如这个类还没有被加载和连接,那就先进行加载和连接操作。
(2)假如类存在直接的父类,并且这个父类还没有被初始化,那就初始化父类,这个和以前学习多态时候构造器为什么父类的构造器总是在子类构造器执行之前就已经执行过了,所以应该学习下java虚拟机初始化一个类的过程,按照以前的学习多态时候的说法,总觉得理解起来很牵强,但是现在按照这种说法,就是虚拟机的规定了,也就是一种规范了吧。
(3)假如类中存在初始化语句,那就按照顺序依次执行这些初始化语句。
(4)直接父类如果还有直接父类的时候那么循环执行2和3这个步骤,但是Object类永远是第一个被初始化的。

类的初始化时机:
java虚拟机首次主动使用一个类的时候,下边六种情况被视为对类或者接口的主动调用,也就是说会初始化类的:

(1)用new语句创建,或者通过反射、克隆以及反序列化来创建类的实例的时候。
(2)调用类的静态方法。
(3)调用类或者接口的静态变量,或者对静态变量进行赋值的时候。
(4)调用Java Api的某些反射方法,例如Class.forName方法,该方法会返回一个类的实例。
(5)初始化一个类的子类的时候,会初始化子类继承的父类。
(6)java虚拟机启动一个被标明为启动类的时候,就是运行java命令的时候,例如 编译完Student类(javac Student.java),要使用java Student才能运行这个类,这个Sdudent类就被标明为启动类。
看下边的例子,没有写继承和java命令的那种类型,大家可以自己做一下例子:

除了上边六种情形,其他使用java类的方式都被看为是被动调用,都不会导致类的初始化,看下边的一些情况:
(1) 对于final类型的static变量,如果在编译时候就能能计算出变量的值,这种变量被叫做编译时常量,对编译时常量的使用看做是被动使用,不会导致类的初始化,例如:
Public static final int a = 5+6; 这个a变量就是编译时常量,变量a在被使用的时候直接将计算出的结果11嵌入到使用的地方,这种调用方式也叫做内嵌调用,所以当访问这个变量的时候,无需初始化定义a所在的那个类。(记得面试时候有人以前问过我为什么好多人喜欢把变量定义成final的,应该是考虑了性能的关系)
(2) 如果编译时候不能计算出a的值,那么将会进行主动使用,就会初始化变量a所在的类。
(3) 初始化机制要求类在使用时候,对类的所有父类都必须进行初始化,但是这个并不适用于接口
a)初始化一个类的时候,并不会初始化这个类所实现的接口。
b)初始化一个接口的时候,并不会初始化父接口。
(4) 当程序访问的静态变量或者静态方法在当前类或者当前接口中定义的时候,才可以看做是对类或者接口的主动使用,例如

(5)类加载器classLoad的loadClass()方法加载一个类,并不是对类的主动调用,不会导致类的初始化
例如下边的例子: