1、定义:虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是类加载机制;
2、类加载时机
(1)整个生命周期:加载--连接(验证--准备--解析)--初始化--使用--卸载
什么时候需要加载: new ,getstatic,setstaic,反射,初始化类的时候发现父类没加载,启动main时,虚拟机会先加载这个主类;
初始化类的时候发现父类没加载:
class SuperClass{ public SuperClass(){ System.out.println("superclass init"); } static { System.out.println("superclass"); } public static int value = 23; } class SubClass extends SuperClass{ public SubClass(){ System.out.println("init subclass"); } static { System.out.println("sublclass"); } //public static int a = 1111; } public class NotInitialization { public static void main(String[] args) { System.out.println(SubClass.value); } }
最后的输出结果是 superclass 23;
superclass是subclass 的父类,所以在找value的时候需要加载superclass,value是静态变量,所以在类加载的时候就会加载;
如果在subclass 中也定义a;
class SubClass extends SuperClass{ public SubClass(){ System.out.println("init subclass"); } static { System.out.println("sublclass"); } public static int a = 1111; } public class NotInitialization { public static void main(String[] args) { System.out.println(SubClass.a); } }
输出变成:
superclass
sublclass
1111;
对于静态字段,对于直接定义这个字段的类才会被初始化,所以SubClass 也会发生初始化;
如果sublclass 中的a 用final修饰;则输出是:
1111
不会加载父类对象;static final 修饰的常量,在编译阶段通过常量传播优化,已经将此常量的值1111存储到NotInitialization 类的常量池中;以后调用这个变量则会直接在常量池中调用,和subclass 和superclas 没有直接关系,所以不需要加载;
3、类加载过程
(1)加载:
加载阶段,虚拟机需要完成以下三件事情;
1)定义一个全局限定名来对应此类的二进制字节流,为了获取这个类;
2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;(jdk 7前是perm永久代,jdk8变成了元数据)
3)在内存中生成一个代表这个类的java.lang.class 对象,作为方法区这个类的各种数据访问入口;
(2)验证:
检查各种语法是否和 《java 虚拟机规范相同》:文件格式验证,元数据验证,字节码验证,符号引用验证;
(3)准备:
准备阶段是正式为类变量分配内存并设置类变量的初始化阶段;(仅包含类变量(static修饰的变量,不包含实例变量,实例变量是在对象实例化的时候开始分配在堆中),初始值通常情况下为该类型值得零值),
public static int a = 123; 在准备阶段初始值为0;在初始化阶段才会赋值为123;如果有constantValue类型那在准备阶段就会把值附上去;
(4)解析:
解析阶段是虚拟机将常量池中的符号引用替换为直接引用的过程,类或者接口的解析,字段解析,类方法解析,接口方法解析,
(5)初始化:
对类变量进行赋值;