类加载系统,主要有以下部分
- 加载
- 链接
- 验证
- 准备
- 解析
- 初始化
它们每部分都做些什么事情呢?
加载
(2条消息) JVM 双亲委派模型_兜兜转转m的博客-CSDN博客
链接
链接-验证
一般验证部分,IDEA已经帮我们规范了。
验证是连接阶段的第一步,这一阶段的目的是为了确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并不会危害虚拟机的自身安全。
验证过程的主要验证信息:验证过程中,主要对三种类型的数据进行验证,分别是“元数据验证,字节码验证和符号引用验证”。具体内容请看下边的讲解。
- 验证这个类是否有父类(除了 java.lang.Object 之外,所有类都应当有父类);
- 验证这个类是否继承了不允许被继承的类(被 final 修饰的类);
- 如果这个类不是抽象类,验证该类是否实现了其父类或接口之中所要求实现的所有方法;
- 验证类中的字段、方法是否与父类产生矛盾(例如覆盖了父类的 final 字段,或者出现不符合规则的方法重载,例如方法参数都一致,但返回值类型却不同等等)
字节码验证:字节码验证主要目的是通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。这个阶段将对类的方法体进行校验分析,保证被校验类的方法在运行时不会产生危害虚拟机安全的事件,例如:
- 保证任意时刻操作数栈的数据类型与指令代码序列都能配合工作。例如不会出现类似这样的情况:在操作数栈放置了一个int类型的数据,使用时却按long类型来加载入本地变量表中;
- 保证跳转指令不会跳转到方法体以外的字节码指令上;
- 保证方法体中的类型转换是有效的,例如可以把一个子类对象赋值给父类数据类型,但是把父类对象赋值给子类数据类型,甚至把对象赋值给与它毫无继承关系、完全不相干的一个数据类型,则是危险不合法的
符号引用验证:符号引用验证可以看作是类对自身以外(常量池中的各种符号引用)的信息进行匹配性校验,通常需要校验以下内容:
- 符号引用中通过字符串描述的全限定名是否能够找到对应的类;
- 在指定类中是否存在符合方法的字段描述符以及简单名称所描述的方法和字段;
- 符号引用中的类、字段、方法的访问性(private、default、protected、public)是否可被当前类访问。
链接-准备(prepare)
为类变量进行赋值和初始化。
准备阶段是正式为类变量分配内存(静态变量哦)并设置类变量默认值(通常情况下是数据类型的零值)的阶段,这些变量所使用的内存都将在方法区中进行分配。这时候进行内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化的时候随着对象一起分配在Java堆中。
链接-解析(resolve)
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
符号引用(Symbolic References):符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。
直接引用(Direct References):直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。如果有了直接引用,那么引用的目标一定是已经存在于内存中。
解析过程具体的解析内容:解析过程中,主要对如下4种类型的数据进行验证:
- 类或接口的解析;
- 字段解析;
- 类方法解析;
- 接口方法解析。
初始化
定义:进行准备阶段时,变量已经赋过一次系统要求的初始零值,而在初始化阶段,则会根据程序员通过程序编码制定的主观计划去初始化类变量和其他资源。
总结
Java中的静态变量和静态代码块是在类加载的时候就执行的,实例化对象时,先声明并实例化变量再执行构造函数。如果子类继承父类,则先执行父类的静态变量和静态代码块,再执行子类的静态变量和静态代码块。同样,接着在执行父类和子类非静态代码块和构造函数。
注意:(静态)变量和(静态)代码块的也是有执行顺序的,与代码书写的顺序一致。在(静态)代码块中可以使用(静态)变量,但是被使用的(静态)变量必须在(静态)代码块前面声明。
最后给出执行步骤:
- 父类静态变量和静态代码块(先声明的先执行)---按照编码顺序;
- 子类静态变量和静态代码块(先声明的先执行)---按照编码顺序;
- 父类的变量和代码块(先声明的先执行)---按照编码顺序;
- 父类的构造函数;
- 子类的变量和代码块(先声明的先执行)---按照编码顺序;
- 子类的构造函数