JAVA对象的创建过程

背景

最近被问java对象的创建过程,当时一听感觉会又感觉不会?是类的加载过程还是JVM中new一个对象的过程?
然后就查资料整理了下,也给自己理理思路,顺便给大家分享下。
在网上搜了下实际就是类的初始化和实例化的问题,但是很多都是侧重说的是类执行的顺序,这里不写类的执行顺序只说两者的区别,如果后续有时间的话专门写一篇类执行顺序的文章。

基础知识

java类的生命周期

指一个class文件从加载到卸载的全过程,类的完整生命周期包括7个部分:加载——验证——准备——解析——初始化——使用——卸载,如下图所示

其中,验证——准备——解析 称为连接阶段,除了解析外,其他阶段是顺序发生的,而解析可以与这些阶段交叉进行,因为Java支持动态绑定(晚期绑定),需要运行时才能确定具体类型;在使用阶段实例化对象

JAVA对象的创建过程_第1张图片
可以明显看出初始化和实例化是两个不同的阶段。

类的初始化

是完成程序执行前的准备工作。在这个阶段,静态的(变量,方法,代码块)会被执行。同时在会开辟一块存储空间用来存放静态的数据。初始化只在类加载的时候执行一次

主要职责:
类的构造器调用(),初始化相关静态代码块以及静态变量的赋值。
对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的。在实例化一个对象时,JVM首先会检查相关类型是否已经加载并初始化,如果没有,则JVM立即进行加载并调用类构造器完成类的初始化。
JAVA对象的创建过程_第2张图片

类的实例化(实例化对象)

是指创建一个对象的过程。**这个过程中会在堆中开辟内存,将一些非静态的方法,变量存放在里面。**在程序执行的过程中,可以创建多个对象,既多次实例化。每次实例化都会开辟一块新的内存。(就是调用构造函数)

主要职责: 实例的构造器调用(init)、分配内存、属性值得定制化赋值机制。
类的实例化本身意义就是对象的概念,其实就是实例化对应的对象的过程。
实例对象内存的分配、实例对象参数的默认初始化+实例对象参数的实例化(就是按开发要求的实现调用,例如调用构造器等)。
此时一般处于在装载阶段的初始化完成之后,使用之前的阶段,接下来就要进行类的实例化操作。
JAVA对象的创建过程_第3张图片
JAVA对象的创建过程_第4张图片

类的加载过程

加载:通过类名获取类的二进制字节流是通过类加载器来完成的。其加载过程使用“双亲委派模型”

验证:当一个类被加载之后,必须要验证一下这个类是否合法,比如这个类是不是符合字节码的格式、变量与方法是不是有重复、数据类型是不是有效、继承与实现是否合乎标准等等。总之,这个阶段的目的就是保证加载的类是能够被jvm所运行。

准备:为类变量(静态变量)在方法区分配内存,并设置零值。注意:这里是类变量,不是实例变量,实例变量是对象分配到堆内存时根据运行时动态生成的。

解析:把常量池中的符号引用解析为直接引用:根据符号引用所作的描述,在内存中找到符合描述的目标并把目标指针指针返回。

初始化:类的初始化过程是这样的:按照顺序自上而下运行类中的变量赋值语句和静态语句,如果有父类,则首先按照顺序运行父类中的变量赋值语句和静态语句在类的初始化阶段,只会初始化与类相关的静态赋值语句和静态语句,也就是有static关键字修饰的信息,而没有static修饰的赋值语句和执行语句在实例化对象的时候才会运行。执行()方法(clinit是class initialize的简写)

实例化:在堆区分配内存空间,执行实例对象初始化,设置引用变量a指向刚分配的内存地址

运行时区内存分配

JAVA对象的创建过程_第5张图片

主要区别

JAVA对象的创建过程_第6张图片

扩展

初始化的几种情况

虚拟机规范严格规定了有且只有5种情况必须立即对类进行初始化:

第一种:遇到new、getstatic、putstatic、invokestatic这四条字节码指令时,如果类还没有进行过初始化,则需要先触发其初始化。生成这四条指令最常见的Java代码场景是:使用new关键字实例化对象时、读取或设置一个类的静态字段(static)时(被static修饰又被final修饰的,已在编译期把结果放入常量池的静态字段除外)、以及调用一个类的静态方法时。
第二种:使用Java.lang.refect包的方法对类进行反射调用时,如果类还没有进行过初始化,则需要先触发其初始化。
第三种:当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
第四种:当虚拟机启动时,用户需要指定一个要执行的主类,虚拟机会先执行该主类。
第五种:当使用JDK1.5支持时,如果一个java.langl.incoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。

注意,有且只有五种情况必须对类进行初始化,这五种情况被称为“主动引用”,除了这五种情况,所有其他的类引用方式都不会触发类初始化,被称为“被动引用”。

常见的不会触发初始化的引用方式(被动引用)

  • 通过子类引用父类的静态变量 只会初始化父类 不会初始化子类
  • 创建类的数组
  • 引用常量池内的变量

类的实例化详细过程

JAVA对象的创建过程_第7张图片

java虚拟机就会为其分配内存来存放自己及其从父类继承过来的实例变量

为这些实例变量分配内存的同时,这些实例变量先会被赋予默认值(零值)【这个零值与加载阶段中的准备很相似,就是先赋值语义级别的默认值,而并非参数真正的初始化】

在内存分配完成之后调用方法,Java虚拟机执行构造代码块、构造方法等,方法参数执行等。才会对新创建的对象赋予我们程序给定的值

创建一个对象包含下面两个过程:

  • 类构造器完成类初始化(赋予静态变量默认值)
  • 类实例化(分配内存、赋予默认值、执行定制化赋值操作)

参考
更详细但是不宜看懂
https://baijiahao.baidu.com/s?id=1709082359796947225&wfr=spider&for=pc
有具体的创建流程但是部分不太准确
https://blog.csdn.net/m0_47023194/article/details/115599414
其他
https://www.jianshu.com/p/9d684a0a9ccc

你可能感兴趣的:(java,java)