1:类的加载时机
类从被加载到虚拟机内存中开始,直到卸载出内存为止,它的整个生命周期包括了:加载、验证、准备、解析、初始化、使用和卸载这7个阶段。其中,验证、准备和解析这三个部分统称为连接(linking)。
2:“加载”(Loading)阶段是“类加载”(Class Loading)过程的第一个阶段,在此阶段,虚拟机需要完成以下三件事情:
1、 通过一个类的全限定名来获取定义此类的二进制字节流。
2、 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3、 在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口。
3:准备
准备阶段是为类的静态变量分配内存并将其初始化为默认值,这些内存都将在方法区中进行分配。准备阶段不分配类中的实例变量的内存,实例变量将会在对象实例化时随着对象一起分配在Java堆中。
public static int value=123;//在准备阶段value初始值为0 。在初始化阶段才会变为123 。
初始化:即初始化阶段是执行类构造器()方法的过程。
()方法是由编译器自动收集类中的所有变量的赋值动作和静态语句块(static{}块)中的语句合并产生的,编译器收集的顺序是语句在源文件中出现的顺序决定的,静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量在之前可以访问但不能赋值;
()方法与类构造函数(或者实例构造器()方法)不同,它不需要显示调用父类构造器,虚拟机会保证在子类的()方法执行之前父类的已经执行完毕;
由于父类的()方法先执行,意味着父类中定义的静态语句块要优先于子类的变量赋值操作;
()方法对于类或者接口不是必须的,如果一个类没有静态语句块和变量的赋值操作,那编译器就可以不为这个类生成()方法;
虚拟机会保证一个类的()方法在多线程环境中被正确的加锁、同步。
4:何时开始类的初始化
什么情况下需要开始类加载过程的第一个阶段:"加载"。虚拟机规范中并没强行约束,这点可以交给虚拟机的的具体实现自由把握,但是对于初始化阶段虚拟机规范是严格规定了如下几种情况,如果类未初始化会对类进行初始化。
创建类的实例
访问类的静态变量 (除常量【 被final修辞的静态变量】 原因:常量一种特殊的变量,因为编译器把他们当作值(value)而不是域(field)来对待。如果你的代码中用到了常变量(constant variable),编译器并不会生成字节码来从对象中载入域的值,而是直接把这个值插入到字节码中。这是一种很有用的优化,但是如果你需要改变final域的值那么每一块用到那个域的代码都需要重新编译。
访问类的静态方法
反射 如(Class.forName("my.xyz.Test"))
当初始化一个类时,发现其父类还未初始化,则先出发父类的初始化
虚拟机启动时,定义了main()方法的那个类先初始化
以上情况称为称对一个类进行“主动引用”,除此种情况之外,均不会触发类的初始化,称为“被动引用”
接口的加载过程与类的加载过程稍有不同。接口中不能使用static{}块。当一个接口在初始化时,并不要求其父接口全部都完成了初始化,只有真正在使用到父接口时(例如引用接口中定义的常量)才会初始化。
被动引用例子
子类调用父类的静态变量,子类不会被初始化。只有父类被初始化。 。 对于静态字段,只有直接定义这个字段的类才会被初始化.
通过数组定义来引用类,不会触发类的初始化
访问类的常量,不会初始化类
例子:
public classStaticAndAssignment {
private staticStaticAndAssignmentstaticAndAssignment=newStaticAndAssignment();
public static intcount1;
public static intcount2=0;
privateStaticAndAssignment() {
count1++;
count2++;
}
public staticStaticAndAssignment getInstance() {
returnstaticAndAssignment;
}
public static voidmain(String[] args) {
StaticAndAssignment singleTon = StaticAndAssignment.getInstance();
System.out.println("count1="+ singleTon.count1);
System.out.println("count2="+ singleTon.count2);
}
}
得理解 准备阶段和初始化阶段的差别;
分析:
1:SingleTon singleTon = SingleTon.getInstance();调用了类的SingleTon调用了类的静态方法,触发类的初始化
2:类加载的时候在准备过程中为类的静态变量分配内存并初始化默认值 singleton=null count1=0,count2=0
3:类初始化化,为类的静态变量赋值和执行静态代码快。singleton赋值为new SingleTon()调用类的构造方法
4:调用类的构造方法后count=1;count2=1
5:继续为count1与count2赋值,此时count1没有赋值操作,所有count1为1,但是count2执行赋值操作就变为0