一 (1)和c/c+(c在编译阶段进行链接)不同的是 java语言中类型的加载,连接,初始化都是在程序运行期完成的。
二 类加载的时机
(1)类从被加载到虚拟机内存 到卸载出内存为止。
类的生命周期经过下面7个阶段:
(这里基本按箭头方向顺序开始(不是按部就班的进行或完成,这些阶段会在一个阶段执行过程调用激活另一阶段),解析阶段除外(为了支持java动态绑定(或者叫晚期绑定),某些情况下解析可以在初始化阶 段之后进行)
(2)5种必须对类进行初始化(加载验证准备要在此之前完成)//这5种是对一个类的主动引用,其他称为被动引用
@1 遇到new(new一个对象), getstatic(读一个静态字段), putstatic(写一个静态字段) ,invokestatic (调用 static方法) 4个字节码指令
@2 进行反射调用(使用java.lang.reflect包下的方法)
@3 初始化子类时,先要初始化父类
@4 执行main方法的类必须先初始化
@5 使用JDK1.7 的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例解析结果是 REF_getStatic REF_putStatic REF_invokeStatic的方法句柄 ,初始化这些方法所在的类。
(3)被动引用(不会初始化)的三个例子
接口的加载过程和类的加载有一些不同:接口并不要求其父接口全部都完成初始化(而子类初始化时,其父类必须全部初始化)
三 类加载过程
(1)在加载(loading)阶段完成三件事-----获取类的二进制字节流
----------这个字节流的存储结构 转为 方法区 运行时的数据结构
----------在内存中 生成代表这个类的对象, 作为方法区这个类的各种数据的访问路口
数组类的情况有所不同(数组类由java虚拟机直接创建,不通过类加载器创建),但数组类的元素类型最终还是要类加载器创建
数组类创建过程需要遵循以下的规则:------数组类的组件类型是 引用类型, 正常递归加载
-------- 不是引用类型 JVM把数组标记与引导类加载器关联
--------- 可见性一致,不是引用类型,默认public
(2)验证 (大致会完成4个阶段的检验)(非常重要但不一定必要的阶段)
----------文件格式验证 (CAFEBABE 版本号 )
----------元数据验证 (是否有父类,父类是否final,类是否抽象,是否符合重载规则)
----------字节码验证(方法)(最复杂的一个阶段){确定程序语义是合法的,方法不会出错}
----------符号引用验证(确保解析动作能正常执行)
(3)准备(变量通常为0 false null)
public static int val=123; //准备阶段过后的初始值为0 而不是123,赋值是在初始化阶段
public static final int val=123 //加final 准备阶段过后是123
(4)解析(虚拟机将常量池内的符号引用 替换为直接引用的过程)
解析动作主要针对 类或接口 字段 类方法 接口方法 方法类型 方法句柄和调用点限定符7类符号引用进行
(5)初始化(是类加载过程的最后一步)
static int i=0; static{ } static{ }能访问 i 定义在static{ }后面的只能赋值不能访问
四 类加载器
双亲委派模型
(某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;
只有父类加载器无法完成此加载任务时,才自己去加载。)
public class Demo04 {
/**
*
* Bootstrap ClassLoader 启动类加载器
* Extension ClassLoader 扩展类加载器 //ext的目录下
* ApplicationClassLoader 应用程序类加载器//一般程序默认的加载器
*
* User ClassLoader 用户自定义的类加载器
*/
public static void main(String[] args) {
Object obj=new Object();
Person p=new Person();
System.out.println(obj.getClass().getClassLoader());//null
System.out.println(p.getClass().getClassLoader().getParent());//sun.misc.Launcher$ExtClassLoader@a15670a
}
}