文章目录
连接
验证
这个阶段主要目的是保证Class流的格式是正确的。主要验证的内容包括:1
1、文件格式的验证
是否以0xCAFEBABE开头
版本号是否合理
2、元数据的验证
是否有父类
是否继承了final类
非抽象类
实现了所有抽象方法
3、字节码验证
运行检查
栈数据类型和操作码数据参数吻合
跳转指令指定到合理的位置
4、符号引用验证
常量池
中描述类是否存在
访问的方法或字段是否存在且有足够的权限
准备
这个阶段主要是为
对象
和变量
分配内存,并为类设置初始值
(方法区中)
对于static类型变量在这个阶段会为其赋值为默认值,
比如public static int v=5,在这个阶段会为其赋值为v=0,
而对于static final类型的变量,在准备阶段就会被赋值为正确的值
解析
在这个阶段会 将
符号引用
转换成直接引用
。
原来的符号引用仅仅是一个字符串,而引用的对象不一定被加载,直接引用只的是将引用对象的指针或者地址偏移量指向真正的对象,将字符串所指向的对象加载到内存中。
初始化
定义
在这个阶段主要执行类的构造方法。并且为静态变量赋值为初始值,执行静态块代码。
顾名思义 就是 初始化刚生成的类的中的变量
此阶段,java层 程序猿的代码 开始介入 之前的流程 可以理解为全在虚拟机,程序猿无感知
类的初始化步骤
- 假如这个类还没有被加载和连接,那就先进行加载和连接
- 假如类存在直接的父类,并且这个父类还没有被初始化,那就先初始化它的父类
- 假如类中存在初始化语句时,那就依次执行这些初始化语句。
类的初始化时机
所有的Java类只有在对类的首次主动使用时才会被初始化。
子类继承父类时的初始化顺序
-
首先初始化父类的static变量和static块,按出现顺序;
-
初始化子类的static变量和static块,按出现顺序;
-
初始化父类的普通变量和构造块,按出现顺序,然后调用父类的构造函数;
-
初始化子类的普通变量和构造块,按出现顺序,然后调用子类的构造函数。
_注意:
(1 )在(1.2)两步中类加载(只加载一次,除非卸载)的时候执行,并且只执行一次。初始化之前已经分配完内存了。
(2)接口不能使用static{}语句块,但编译器仍然会为接口生成“()”类构造器,用于初始化接口中所定义的成员变量。接口和类真正区别是:当一个类在初始化的时候,要求其父类全部都已经初始化过了,但是一个接口在初始化时,并不要求其父接口全部都完成了初始化,只有在真正使用到父接口的时候(如引用接口中定义的常量)才会初始化._
类中实例化自己时的初始化顺序
初始化总结
在类的初始化阶段,虚拟机负责对类进行初始化,主要就是对静态Field进行初始化,在Java类中对静态Fieldr指定初始值有两种方式:
- 声明静态Field时指定初始值;
- 使用静态初始化块为静态Field指定初始值。
使用
正常使用类 ,比如 new String();
主动使用的情况有七种
- 创建类的实例
- 访问某个类或接口的静态变量,或者对该静态变量赋值
- 调用类的静态方法
- 反射(Class.fotName)
- 初始化一个类的子类
- Java虚拟机启动时被标明为启动类的类(面方法所在的类)
- Jdk1.7 开始提供的动态语言支持
注意:
1、当Java虚拟机初始化一个类时,要求他的所有父类都已经被初始化,但是这条规则并不适合接口。在初始化一个类或接口时,并不会先初始化它所实现的接口。
2、只有当程序访问的静态变量或静态方法确实在当前类或当前接口中定义时,才可以认为是对类或接口的主动使用。如果静态方法或变量在parent中定义,从子类进行调用,则不会初始化子类。
类的被动使用(不会发生类的初始化)
-
当访问一个静态变量时,只有真正实现这个静态变量的类才会被初始化(通过子类引用父类的静态变量,不会导致子类初始化)
-
通过数组定义类应用,不会触发此类的初始化 A[] a = new A[10];
-
引用常量(final类型)不会触发此类的初始化(常量在编译阶段就存入调用类的常量池中了)
-
其实只要不是主动引用的都是被动引用 O(∩_∩)O哈哈~
卸载
把加载到java虚拟机中的class类删除掉
下面3种情况会导致类的卸载
-
类加载器没有被引用
-
类对象没有被引用
-
没有该类的实例对象存在
参考
推荐博客
刘望舒 csdn上博客中有些图看不了了,但是有些文章 简书上也有,可以多搜几个平台的网址,相关图片就可以看到了