一、概要:
类的生命周期从类的 加载、连接、初始化 开始,到类的 卸载结束;
二、几个阶段:
加载:查找并加载类的二进制数据。(把类的.class文件的二进制数据读入内存,存放在运行时数据区的方法区;类加载的最终结果是产生 堆区中描述对应类的Class对象);
连接:包括验证、准备和解析三个子阶段;
验证:确保补加载类的正确性;
准备:为类的静态变量分配内存,并设定默认值;
解析:把类中的符号引用转换为直接引用;
初始化:给类中的静态变量赋予正确的初始值;
类的加载时机:并不需要等到某个类被主动使用时才加载,虚拟机会预测某个类将要被使用就加载它;
类的初始化时机:
a. 创建类(new、反射、克隆、反序列化)
b. 使用静态方法、非静态变量
c. Class.forName("ATest"); 获取描述类的Class对象;
另:初始化子类,父类被先初始化。虚拟机的启动类,使用命令 java Sample(也算是创建类);
注:
使用能在编译期能得知的final static修饰的常量,不会导致类的初始化;
public static final int a = 2*3;//编译时常量,不会导致类初始化;
public static final int a b = (int)(Math.random()*10)/10+1; // 不是编译时常量,会初始化;
只有当程序访问的静态变量或静态方法的确在当前类或接口中定义时,才可以看作是对类或接口的主动使用;(如用子类引用调用父类静态方法或变量,只会初始化父类)
调用ClassLoader.loadClass()方法加载一个类时,只会加载,但不会初始化;
子类父类初始化过程:
先对这个类进行加载和连接-> 如果有直接父类,先加载连接初始化这个父类->重复以上步骤直到所有父类初始化,初始化当前类;
(先加载连接当前类,再加载连接初始化父类,再初始化当前类)
class Base { static { System.out.println("init Base"); } } class Sub extends Base { static { System.out.println("init Sub"); } } public class ATest extends Sub { static { System.out.println("init ATest"); } public static void main(String args[]) { System.out.println("main methos"); } } 结果: init Base init Sub init ATest main methos