1、类加载时机
当程序使用某个类时,如果该类还未被加载到内存中时加载该类(一般时第一次使用时)。或者时采用预加载机制来加载该类。
2、类加载时机
该图为类的生命周期。
加载、验证、准备、初始化、卸载这5个阶段的顺序是确定的,类加载过程必须按照这种顺序开始执行(是执行,不是完成。比如说可以先进行加载,加载还没结束,验证阶段就已经开始了。而不是加载全部完成后在做验证)。而解析阶段可以在初始化阶段后进行,这是为了支持java的运行时绑定(比如多态)。
什么时候进行加载阶段:java虚拟机规范虽然没有强制性约束在什么时候开始类加载过程,由jvm自行把握。
初始化时机:
1、 主动引用
- 创建类的实例
- 访问类的静态变量(除常量【被final修辞的静态变量】原因:常量一种特殊的变量,因为编译器把他们当作值(value)而不是域(field)来对待。如果你的代码中用到了常变量(constant variable),编译器并不会生成字节码来从对象中载入域的值,而是直接把这个值插入到字节码中。这是一种很有用的优化,但是如果你需要改变final域的值那么每一块用到那个域的代码都需要重新编译。
- 访问类的静态方法
- 反射如(Class.forName("my.xyz.Test"))
- 当初始化一个类时,发现其父类还未初始化,则先出发父类的初始化
- 虚拟机启动时,定义了main()方法的那个类先初始化
2、被动引用
- 子类调用父类的静态变量,子类不会被初始化。只有父类被初始化。。对于静态字段,只有直接定义这个字段的类才会被初始化.
- 通过数组定义来引用类,不会触发类的初始化
- 访问类的常量,不会初始化类
3、类加载过程
1.加载
“加载”(Loading)阶段是“类加载”(Class Loading)过程的第一个阶段,在此阶段,虚拟机需要完成以下三件事情:
1、通过一个类的全限定名来获取定义此类的二进制字节流。
2、 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3、 在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口。
加载阶段即可以使用系统提供的类加载器在完成,也可以由用户自定义的类加载器来完成。加载阶段与连接阶段的部分内容(如一部分字节码文件格式验证动作)是交叉进行的,加载阶段尚未完成,连接阶段可能已经开始。
2、验证
验证是连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
3、准备
准备阶段是为类的静态变量分配内存并将其初始化为默认值,这些内存都将在方法区中进行分配。准备阶段不分配类中的实例变量的内存,实例变量将会在对象实例化时随着对象一起分配在Java堆中。
public static int value=123;//在准备阶段value初始值为0 。在初始化阶段才会变为123 。
4、解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
5、初始化
根据程序初始化类变量(与准备阶段不同),执行静态代码块。初始化阶段对于接口时不需要的,因为接口没有变量和静态代码块
4 、经典例子
public class SingleTon {
private static SingleTon singleTon = new SingleTon();
public static int count1;
public static int count2 = 0;
public SingleTon() {
count1++;
count2++;
System.out.println(count1);
System.out.println(count2);
}
public static SingleTon getInstance() {
return singleTon;
}
public static void main(String[] args) {
SingleTon singleTon = SingleTon.getInstance();
System.out.println("count1=" + singleTon.count1); //1
System.out.println("count2=" + singleTon.count2); //0
}
}
过程解析:
1.
main方法中执行
SingleTon singleTon = SingleTon.getInstance();
,该语句访问类SingleTon
类的静态方法,SingleTon必须执行初始化。
2.准备阶段
为3个类变量赋予初始值(这个值系统给的,0、null这类的)。之后进行解析、初始化等阶段
private static SingleTon singleTon = new SingleTon(); //null
public static int count1; //0
public static int count2 = 0; //0
3、初始化阶段
调用构造器
private static SingleTon singleTon = new SingleTon();
程序跳到这里,执行构造器,输出两个1。这里singleTon
这个类变量初始化完成。
public SingleTon() {
count1++;
count2++;
System.out.println(count1); //1
System.out.println(count2); //1
}
初始化count1、count2,这里程序没有指定count1的值,所以它还是1,指定了count2的值,所以count2为0
public static int count1;
public static int count2 = 0;
网友评论