跟孙哥学java
类的生命周期 描述一个类加载,使用,卸载的整个过程
生命周期的概述
连接又可以拆分成
加载阶段
- 加载阶段 第一步是类加载器 根据类的全限定名 通过不同的渠道以二进制流的方式获取字节码信息。程序员可以编码选择不同渠道
比如Spring常用到动态代理技术
早期Applet技术 通过网络传输的类
- 类加载器在加载完类之后,Java虚拟机讲字节码信息保存到方法区中
生成一个InstancKlass对象,保存类的所有信息,里面还包含实现特定功能比如多态的信息。
- Java虚拟机在堆中生成一封与方法区中数据类似的Java.lang.Class对象
对于开发者来说,只需要访问堆中的Clss对象而不需要访问方法区中所有信息。
这样ava虚拟机就能很好地控制开发者访问数据的范围。提升数据的安全性
连接阶段
- 验证
验证内容是否满足Java虚拟机规范 主要包含如下四部分,具体见《Java虚拟机规范》
- 文件格式校验 如 0xCAFEBABE 开头,主次版本号是否满足Java虚拟机版本要求
class文件的主副版本号都不能超过java虚拟机的主副版本号
- 元信息验证,例如类必须有父类(super不能为空)
- 验证程序执行指令的语义,比如方法内的指令执行跳转到其他方法
- 符号引用验证,例如是否访问了其他类的private的方法
- 准备
给静态变量赋初值,防止该变量存在残留值。
final修饰的基本数据类型的静态变量,在准备阶段就会进行赋值
- 解析
将常量池中的符号引用替换成内存的直接引用 直接引用不在使用编号,而是使用地址直接访问内存
初始化阶段
对于非final修饰的静态变量,会由虚拟机给变量赋予初值
在初始化阶段: 会执行静态代码块中的代码,并为静态变量赋值 初始化阶段会执行字节码文件中clinit部分的字节码指令 putstatic---> 给静态变量赋值
执行过程:
将1 放入操作数栈中
putstatic,将操作数栈中的1赋值给 常量池中编号为2的变量
将2放入操作数栈中
给常量池 编号为2的变量赋值为2
以下方式会导致类的初始化:
1.访问一个类的静态变量或者静态方法,注意变量是finl修饰的并且等号右边是常量不会触发初始化。
2.调用Class.forName(String className)。
3.new一个该类的对象时。
4.执行Main方法的当前类的静态变量。
clinit指令在特定情况下不会出现,比如:如下几种情况是不会进行初始化指令执行的。 1.无静态代码块且无静态变量赋值语句。 2.有静态变量的声明,但是没有赋值语句。 3.静态变量的定义使用final关键字,这类变量会在准备阶段直接进行初始化。
注意: 直接访问父类的静态变量,不会触发子类的初始化。 子类的初始化clinit调用之前,会先调用父类的clinit初始化方法。
面试题
程序的输出结果是什么?
输出: D A C B C B