储备知识
a.类加载的本质
将描述类的数据从class文件加载到内存&对数据进行校验、转换解析和初始化,最终形成可被虚拟机直接使用的java使用类型(Class文件是一串二进制字节流)
b.类加载过程
a.分为5个步骤 加载--->验证---->准备--->解析--->初始化
具体
生成Class文件--->开始--->加载--->连接阶段(验证--->准备--->解析)--->初始化--->结束--->后置条件(开发者使用--->类卸载)
步骤1:加载
作用:将外部的Class文件加载到java虚拟机&存储到方法区内,即二进制字节流
具体流程:1.通过类的权限定名,来获取定义此类的二进制字节流
2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
3.在内存中生成一个代表这个类的java.lang.Class对象,作为方法区该类的各种数据的访问入口
注:数组类通过java虚拟机直接创建,不通过类加载器创建
步骤2:验证
作用:确保加载进来的Class文件包含的信息符合Java虚拟机的要求
具体流程:
1.文件格式化验证
2.元数据验证
3.字节码验证
4.符号引用验证
步骤3:准备
作用:为类变量分配内存&设置类变量的初始值
具体流程:
1.为类的static变量在方法区中分配内存
2.将上述变量的初始值设为0 (非开发者定义的值)
注:实例变量不在该阶段分配内存
若类变量为常量(被final修饰),则直接赋开发者定义的值
步骤4:解析
作用:将常量池内的符号引用直接转为直接引用
具体流程:解析对象包括
a.类/接口
b.方法 (b1.类方法 ;b2.接口方法(私有);b3.方法类型; b4.方法句柄)
c.字段
注:实例变量不在该阶段分配内存;
因为类方法和私有方法符合编译器可知、运行期不可变的要求,既不会被集成或重写,所以适合在类加载过程进行解析;
若变量位常量(final修饰),则直接赋开发者定义的值
步骤5:初始化
作用:初始化类变量&静态语句块
具体流程:
1.生成类构造器<clinit>()方法(a.即合并所有类变量(static变量)和静态语句块(static)
b.顺序=定义的顺序、实例化变量由类的构造函数<init>()赋值);
2.执行<clinit>()方法
注:1.类构造器<Clinit>()区别于类构造函数(<init>())
(a.不需要显示调用父类构造器
b.子类<clinit>())执行前,父类的<init>()一定会被执行,因此虚拟机第一个执行<clinit>()是java.lang.Object
2.静态语句块只可赋值,不可被访问
3.接口与类不同,执行子接口的<clinit>()并不需执行父接口的<clinit>()
网友评论