类加载:
1,加载:将class字节码文件加载到内存中,并在方法区创建一块区域,存放了类的静态变量,方法,字段,等所有的类运行时数据结构(除了非静态成员变量(对象的特有属性)不会被加载,其它的都会被加载。),然后会在堆中生成一个对应的Class对象。
2,链接
1)验证 :验证阶段用于检验被加载的类是否有正确的内部结构,其主要包括 文件格式验证,元数据验证,字节码验证,符号引用验证。
2)准备:准备阶段负责为类的静态变量分配内存,并设置默认初始值。
3)解析:将class常量池中的符号引用替换为直接引用(内存地址)的过程
3,初始化:执行类构造器<clinit>()方法执行,此方法由编译器自动收集静态变量和静态代码块 合并产生(执行顺序 由语句在源码文件中出现的顺序决定)。jvm会保证一个类的clinit方法在多线程下是线程安全的。
例1 :先执行 a = 200,再执行a = 100;
static int a = 200;
static{
a = 100;
}
例2 :先执行 a = 100,再执行a = 200;
static{
a = 100;
}
static int a = 200;
扩展下: Class.forName("xx") :得到的class是已经初始化完成的。
getClassLoader().loadClass("xx"):得到的class是还没有链接的,只执行了第一步加载过程的。
会发生类初始化(不是实例化)的时机:
1,new 一个类
2,访问类的静态变量(final类型的静态变量除外) 或静态方法
对于一个final类型的静态变量。
如果该变量的值是一个常量,编译器会在编译时(类加载第二步的解析中)直接把这个变量出现的地方替换成它的值,因此即使程序使用该静态变量,也不会导致该类的初始化,因为直接使用这个值对应的地址了。
反之,如果final类型的静态Field的值不能在编译时确定下来,则必须等到运行时才可以确定该变量的值,如果通过该类来访问它的静态变量,则会导致该类被初始化。
3,反射 Class.forName("xx")
4,调用一个类时,若父类没被加载,则会先加载其父类
5,虚拟机启动时,先初始化main方法所在的类
类加载机制:
类加载器的任务 是根据类的全限定名来读取此类的二进制字节流到 JVM 中,然后转换成一个与目标类对象的java.lang.Class 对象的实例,在java 虚拟机提供三种类加载器,引导类加载器,扩展类加载器,系统类加载器。