Class文件
- java程序运行期间加载,动态扩展,动态加载,动态连接
- 面相接口的应用程序,可以等到运行时再指定其实际的实现类
- 通过Java预定义和自定义的类加载器,在运行时从网络或其他地方加载一个二进制流作为程序代码的一部分
- 类的生命周期:加载(Loading)、连接(Linking)[验证(verification)、准备(Preparation)、解析(Resolution)]、初始化(Initialization)、使用(Using)、卸载(Unloading)
- 解析可能发生在初始化之后,为了支持java语言的动态绑定
- 五种必须对类立即初始化的场景
- 使用new关键字实例对象时,读取或设置一个类的静态字段时(除了被final修饰,已在编译器把结果放入常量池的静态字段除外),调用一个类的静态方法时
- 使用java.lang.reflect包的方法对类进行反射调用时,如果类没有初始化,要先触发其初始化
- 初始化一个类时,发现父类还没初始化,则先要触发父类初始化
- 虚拟机启动时,用户需要指定一个要执行的主类(psvm),虚拟机会先初始化这个主类
- java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个句柄对应类未初始化,则需要触发
==========================================================
4. 加载(是类加载(class loading)的一个阶段)
- 加载字节流到方法区,然后在内存中实例化一个java.lang.Class类的对象
- 数组类的加载,根据数据组的组件类型(去掉维度的类型)是否为引用类型来加载,数组类的可见性和它的组件类型可见性一致,如果不是引用类型,数组类的可见性将默认为public
- 验证
- 文件格式验证:class文件是否以魔数开始;主次版本号是否在当前虚拟机可处理的范围;常量池指向是否正确;etc.
- 元数据验证:对元数据进行语义校验,保证不存在不符合java语言规范的元数据信息
- 字节码验证:
- 符号引用验证:是否能找到被引用的字段、方法等
- 准备
- 为类变量在方法区分配内存,并设置初始值(基本数据的零值,引用类型为null,bool为false,数字类型为0,byte为0,char为’\u0000’)
- private static int value= 123;在准备阶段过去后初始值为0,在类初始化后才会执行putstatic指令变成123
- private static final int value=123;编译时将此值标记为constantValue属性,准备阶段会根据此属性将value设置为123
- 解析
- 将常量池的符号引用替换为直接引用的过程
- 1)类或接口的解析:加载引用的类时,由于元数据验证、字节码验证需要,可能触发其他类加载,一旦有失败,就会解析失败,抛出异常
- 2)字段解析:先加载字段所属类,如果此类实现了接口,会加载所有接口类及父接口类,去匹配字段
- 3)类方法解析:
- 4)接口方法解析:在接口方法表里发现class_index是类的话会报错。在当前接口中查找匹配方法,找不到就加载父接口,在父接口里找,直到java.lang.Object
- 初始化
- 初始化阶段其实是执行类构造器()方法的过程
- 如果一个类里面没有静态语句块,也没有对变量的赋值操作,编译器可以不生成()方法
- 执行接口的()不需要为父类也初始化
- 虚拟机会保证初始化在多线程中正确佳作同步,如果多个线程同时去初始化一个类,只会有一个线程去执行(),其他线程会被阻塞
=================================================================
- 类加载器 https://juejin.im/post/5950947c5188250d9745f302
- 只用来实现类的加载动作,类和类加载器在java虚拟机中唯一
- 对同一个Class文件,被同一虚拟机加载,只要加载它们的类加载器不同,那么这两个类就肯定不相等
- 双亲委派模型
- 类加载器大致分为两类,启动类加载器,一般类加载器
- 对程序员来讲分三类,启动类加载器,扩展类加载器,应用程序类加载器
- 双亲委派模型的要求是,如果有类加载请求,都会由子加载器要求父加载器来加载,直到启动类加载器,如果父加载器无法加载,才有子加载器来加载类。
- 避免基础类由不同类加载导致的解析冲突