首先来挂一张类加载的生命周期图
图来源:面试官:为什么java中静态方法不能调用非静态方法和变量?
具体流程解释:
- 1、加载:”加载“是”类加机制”的第一个过程,在加载阶段,虚拟机主要完成三件事:
- 通过一个类的全限定名来获取其定义的二进制字节流
- 将这个字节流所代表的的静态存储结构转化为方法区的运行时数据结构
- 在堆中生成一个代表这个类的Class对象,作为方法区中这些数据的访问入口。
注意此时会扫描到我们的代码中是否有静态变量或者是静态方法等等这些静态数据结构,还未分配内存。
-
2、验证:验证的主要作用就是确保被加载的类的正确性。
-
3、准备:准备阶段主要为类变量分配内存并设置初始值。这些内存都在方法区分配。注意此时就会为我们的类变量也就是静态变量分配内存,但是普通成员变量还没。
-
4、解析:解析阶段主要是虚拟机将常量池中的符号引用转化为直接引用的过程。
-
5、初始化:这是类加载机制的最后一步,在这个阶段,java程序代码才开始真正执行。我们知道,在准备阶段已经为类变量赋过一次值。在初始化阶端,程序员可以根据自己的需求来赋值了。初始化时候才会为我们的普通成员变量赋值。
接下来我们开始趣谈
- 1、加载:特斯拉这个类要在上海这个JVM上执行,那么首先是划一块地给他对吧,需要做三件事:
- 上海市需要通过一个类的全限定名(美国特斯拉公司…)来获取其定义的二进制字节流(特斯拉的建地规划图纸)。
- 将这个字节流所代表的的静态存储结构(图纸上的结构)转化为方法区的运行时数据结构(上海某区域的实际土地结构)
- 上海市专门成立一个项目组来处理特斯拉建厂的事项。这个项目组记载来所有特斯拉的情况(这个项目组代表特斯拉这个类的Class对象),项目组是一直存在的(随特斯拉消失而消失),并且是其他人访问特斯拉数据的入口,而不是直接去特斯拉实际工厂。
- 2、验证:验证的主要作用就是确保这个特斯拉真的是特斯拉,而不是假冒骗钱拿地的开发商(防止代码注入引发的安全漏洞)。
- 3、准备:开始为特斯拉工厂划分土地面积,考察将图纸上的A区域、B区域…落到上海实际的哪块土地上(静态变量分配内存),至于特斯拉的门面店(实例对象)就先不着急,这些门面店将根据上海市区的人流量的大小选址产生,因此就跟不用说门面店里面摆放的那些样品车了(普通成员变量)。
- 4、解析:符号引用转化为实际引用,这个有点复杂,这样解释哈:符号引用就是特斯拉建厂图纸上的符号,但是搬砖的人不认识,就问设计师,设计师说,这些细节你去问执行设计师,执行设计师说这是XX外包公司提供的,你问XX公司,最后你去XX公司问出来了,这个符号就是特码的一个厕所马桶(实际引用),于是你费了九牛二虎之力把所有的符号引用转化成实际引用了。
- 这里说明两点:
- 如果有了直接引用,那么直接引用的目标一定被加载到了内存中(说明你肯定是问出来这个符号到底是什么意思了)。
- 为什么要符号引用直接用直接引用不香么?在编译的时候一个每个java类都会被编译成一个class文件,但在编译的时候虚拟机并不知道所引用类的地址,所以就用符号引用来代替,而在这个解析阶段就是为了把这个符号引用转化成为真正的地址的阶段(编译是规划的那帮人,运行时是真的执行人,他们是两帮人,编译的人 不太能 考虑运行的实际情况,这也是为什么会有编译器错误和运行时错误)。
- 这里说明两点:
- 5、初始化:这是特斯拉投产前的最后一步了,在这个阶段,java程序代码才开始真正执行。我们知道,在准备阶段已经为类变量赋过一次值(为规划面积分配实际土地)。在初始化阶端,程序员可以根据自己的需求来赋值了(意味着两点:
- 1、以前赋值了,现在我改变值。
- 2、以前没有赋值,现在我来赋值)。
注意如果以前没有赋值,现在还不赋值,编译期都过不了。
同时,这个时候门店也开始初始化了,初始化时候才会为我们的普通成员(特斯拉样车)变量赋值。
欢迎拍砖,我来改正!
另外广告一下我的github项目:AlgorithmPractice。
该项目旨在建立知识结构体系,方便查找,欢迎更多志同道合的朋友加入AlgorithmPractice,(欢迎提issue和pull request)