首先看两个实例:
实例1
答案:
实例2
答案:
类的生命周期
类在JVM中的生命周期分七个阶段:加载、验证、准备、解析、初始化、使用和卸载。其中验证、准备、解析有称为类的连接。加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的,而解析可以在初始化之前也可在初始化之后,为了支持java语言的动态绑定(或者叫运行时绑定)。
加载
· 通过类的全名获取二进制字节流
· 字节流存入方法区
· 堆中生成一个Class对象,作为方法区数据结构访问的入口
验证
· 文件格式验证:是否满足Class文件规范
· 元数据验证
· 字节码验证
· 符号引用验证
准备
· 类的静态变量会在类准备阶段分配内存
· 赋予初始值(0,null,false等),而java中显式赋值的操作是在类初始化的时候执行。
类的初始化
类只有在主动使用时,虚拟机才会对类进行初始化,被动使用不会初始化。
主动引用的情况:
· New实例化
· 访问静态变量、静态方法
· 反射访问
· 初始化类时,如有父类,父类也会被初始化
· Main方法所在类会先被初始化.
被动引用的情况:
· 调用父类静态变量,子类被称为被动引用,不会初始化。
· 引用静态变量,定义该常量的类不会初始化
· 数组定义类
现在,再回过头来分析开始提到的两个实例
实例1:
1:子类调用父类的静态变量,父类会被初始化,子类不会被初始化,也就是说对于静态变量,只有直接定义该变量的类才会被初始化。所以Child.parent时,Child不会初始化,Parent初始化。
2:类的static方法在类初始化的时候会被执行(注意:是初始化时执行,而并非类加载时执行)。所以Child类的static和构造方法都不会被执行。而parent的static方法会被执行。
实例2:
1:类的静态变量会在类准备阶段分配内存,并赋予初始值(0,null等),而java中显式赋值的操作是在类初始化的时候执行。
2:访问child.num时会触发Child的初始化,但是第一行代码child=Child.getinstance()执行时,类尚未完成初始化,所以此时num在内存中的值为0,执行++后为num=1,而当类完成初始化时,num的值又被重置为2,所以main:num=2;
3:如果将Childchild = Child.getInstance();int num = 2;换下位置,可以得到num值都为3,可自行测试