类的加载机制
类的生命周期
类在JVM中的生命周期分七个阶段:加载、验证、准备、解析、初始化、使用和卸载。其中验证、准备、解析有称为类的连接。加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的,而解析可以在初始化之前也可在初始化之后,为了支持java语言的动态绑定(或者叫运行时绑定)。
加载
通过类的全名获取二进制字节流,字节流存入方法区,堆中生成一个Class对象,作为方法区数据结构访问的入口
验证
文件格式验证:是否满足Class文件规范
元数据验证
字节码验证
符号引用验证
准备
类的静态变量会在类准备阶段分配内存
赋予初始值(0,null,false等),而java中显式赋值的操作是在类初始化的时候执行。
类的初始化
类只有在主动使用时,虚拟机才会对类进行初始化,被动使用不会初始化。
主动引用的情况:
- New实例化
- 访问静态变量、静态方法
- 反射访问
初始化类时,如有父类,父类也会被初始化
Main方法所在类会先被初始化.
调用父类静态变量,子类被称为被动引用,不会初始化。
被动引用的情况:
- 引用静态变量,定义该常量的类不会初始化
- 数组定义类
实例1:
public class Parent{ public static int parent = 0; static{ System.out.println("Parent init..."); } } public class Child extends Parent{ public static int child=Parent.parent; static{ System.out.println("Child init..."); } public Child(){ System.out.println("Child init by constructor..."); } } public class ClassLoaderTest{ public static void main(String[]args){ System.out.println(Child.parent); } }
输出结果:
Parent init... 0
分析
1:子类调用父类的静态变量,父类会被初始化,子类不会被初始化,也就是说对于静态变量,只有直接定义该变量的类才会被初始化。所以Child.parent时,Child不会初始化,Parent初始化。
2:类的static方法在类初始化的时候会被执行(注意:是初始化时执行,而并非类加载时执行)。所以Child类的static和构造方法都不会被执行。而parent的static方法会被执行。
实例2:
public class Child{ public static Child child = Child.getInstance(); public static int num = 2; public static Child getInstance(){ num++; System.out.println("instance:num=" + num); returnchild; } public Child(){ System.out.println("Child init..."); num++; System.out.println("Constructor:num="+num); } } public class ClassLoaderTest{ public static void main(String[]args){ System.out.println("main:num="+Child.num); } }
输出结果:
instance:num=1 main:num=2
这个就有点意思了,为什么instance:num不为2或3,而为1了?。
分析
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,可自行测试。
类加载器
类加载的双亲委派机制是当类加载器收到一个类加载请求是,首先不会自己尝试加载,而是交由父加载器加载,父加载器同样会委派上级加载器加载,最终会交由启动类加载器加载,而上级加载器在指定目录下没有找到指定类时再交给下级加载器加载(如果最下级加载也没有找到类会抛出ClassNotFound异常),这种机制的好处就是一个类不管由哪个加载器加载,最终得到的是相同的对象。
双亲委派