类的生命周期:加载,验证,准备,解析,初始化,使用,卸载
必须对类进行初始化的五种条件(有且仅有):
1、遇到new、getstatic、putstatic、invokestatic这条字节码指令
2、使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化
3、当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化
4、当虚拟机启东市,用户需要制定一个要执行的主类(包含main方法的类),虚拟机会先初始化这个主类
5,、当时用动态语言支持是,如果一个java.lang.in oke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行初始化,则需要先触发其初始化
三种被动引用不会初始化:
1、通过子类引用父类的静态字段,不会导致子类初始化
2、通过数组定义应用类,不回触发此类初始化
3、常量在编译阶段会存入调用类的常量池,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化
类加载过程:
加载
1、通过一个类的全限定名来获取定义此类的二进制字节流
2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
3、在内存中生成一个代表这个类的java.lang.Class对象
如果是数组类,数组类本身是虚拟机直接创建,数组类的元素类型才是考类加载器创建
验证:为了确保
Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全 验证阶段如下:
1、文件格式验证
2、元数据验证,目的是对累的元数据信息进行语义校验
3、字节码校验,目的是通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的(方法体校验)
4、符号引用验证
准备:
正是味蕾变量分配内存并设置类变量初始值的阶段,就这些变量所使用的内存都将在方法区中进行分配,但是这时候进行内存分配的仅包括static修饰的变量,不包括实例变量,实例变量是对象实例化时随着对象一起分配在java堆中
解析:
虚拟机将常量池内的符号引用替换为直接引用的过程
符号引用:
可以是任何形式的字面量,只要使用时能无歧义的定位到目标即可
直接引用:
可以使直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄
解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号
类或接口的解析:假设当前代码所处的类为D,如果要把一个从未解析过的符号引用N解析为一个类或接口C的直接引用,需要完成以下步骤:
1、如果C不是一个数组类型,虚拟机会把代表N的全限定名传递给D的类加载器去加载这个类C
2、如果C是一个数组,并且数组的元素类型为对象,也就是N的面舒服会是类似”[Ljava/lang/Integer”的形式那将会按照低一点的规则加载数组元素类型
3、经过1/2两步后没有异常,C在虚拟机中实际上已经成为一个有效的类或接口了,然后还要进行符号引用验证,确认D是否具备对C的访问权限,没有的话就抛出java.lang.IllegalAccessError异常
字段解析:首先会对字段所属的类或接口符号引用进行解析
接口方法解析:
初始化:
初始化阶段是执行类构造器()方法的过程,()方法是由编译器自动收集类中的所有类变量的赋值动作和静态块语句块static{}中的语句合并产生的,编译期收集的顺序是有语句在源文件中的出现顺序决定的,静态块语句只能访问到静态块语句前的变量,不能访问到后面的,但能给后面的赋值,()方法不需要显示调用父类构造器,因为虚拟机保证子类()方法执行之前其父类的该方法已经执行了,类加载器同时接口的方法不需要先执行父接口的该方法,除非·用到父接口中定义的变量,虚拟机会保证一个类的()方法在多线程环境中被正确的加锁、同步。
类和类加载器:
类加载器用于实现类的加载动作,并且和类一同确立类在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间
绝大多数Java程序都会用到3种系统提供的类加载器:
启动类加载器Bootstrap ClassLoader,负责将存放在\lib目录中的,活着呗-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,名字不符的不会被加载)类库加载到虚拟机内存中,改加载器无法被java程序直接引用
扩展类加载器Extension ClassLoader,负责加载\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库
应用程序类加载器 负责加载用户类路径(ClassPath)上所指定的类库,没有自定义类加载器一般都默认使用它
类加载器之间的父子关系一般不会以继承关系来实现,而是使用组合关系来复用父加载器的代码,每个加载器对于收到加载类的请求都会先请求委派给父类加载器加载,父类不行再自己加载,自己不行就给子加载器