深入分析Java中对象的创建过程

在Java中,对象创建是一个至关重要的过程,它涉及类加载、内存分配、初始化、赋值等多个阶段。理解这些过程不仅有助于编写更高效的代码,还能帮助我们优化程序的性能。本文将深入分析Java对象创建的全过程,重点讨论类加载、对象初始化、静态与非静态初始化的顺序,以及JVM堆栈内存的分配机制,并结合代码进行解析。

一、类加载过程

Java虚拟机(JVM)在执行Java程序时,需要首先将类加载到内存中。类加载分为以下几个步骤:

  1. 加载(Loading):JVM通过类加载器(ClassLoader)加载字节码文件(.class文件)到内存中,并将其转换为 Class 对象。
  2. 链接(Linking)
    • 验证(Verification):确保类的字节码符合JVM规范。
    • 准备(Preparation):为类的静态变量分配内存,并初始化为默认值。
    • 解析(Resolution):将常量池中的符号引用替换为直接引用。
  3. 初始化(Initialization):执行类的静态代码块以及静态变量的初始化。

示例代码:

 

java

代码解读

复制代码

public class InitializeDemo2 { private static int k = 1; private static int n = 10; private static int i; private String s = new String("-----"); static { System.out.println("静态代码块:执行了"); System.out.println("k=" + k + ", n=" + n + ", i=" + i); } private int h = 2; { System.out.println("普通代码块:执行了"); System.out.println(s); } public static void m1() { System.out.println("静态方法:执行了"); System.out.println("k=" + (k++) + ", n=" + n + ", i=" + i); } public void m2() { System.out.println("普通方法:执行了"); System.out.println("k=" + (k++) + ", n=" + n + ", i=" + i); } public InitializeDemo2(String str) { System.out.println("构造器:执行了"); System.out.println("k=" + (k++) + ", n=" + n + ", i=" + i); } }

在上述代码中,InitializeDemo2 类的加载过程会按上述顺序进行。静态变量 k 在“准备”阶段被分配内存并初始化为默认值 0,在“初始化”阶段被赋值为 1

说到这里,就要暂停下。我们怎么验证类的加载过程呢(本文仅从程序员更容易看懂的角度)?我们知道,如果调用类内部的静态方法,会触发类加载过程。

通过在另一个类调用IInitializeDemo2 的静态方法:

这边整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

 需要全套面试笔记的【点击此处即可】免费获取

java

代码解读

复制代码

public static void main(String args[]) { InitializeDemo2.m1(); }

可以验证到输出是:

 

shell

代码解读

复制代码

静态代码块:执行了 k=1, n=10, i=0 静态方法:执行了 k=1, n=10, i=0

说明,静态变量在类加载阶段就已经完成了!

二、对象初始化

在对象创建过程中,核心的过程是初始化,其分为两个主要部分:静态初始化非静态初始化

  1. 静态初始化:只在类第一次加载时执行,用于初始化静态变量和执行静态代码块。
  2. 非静态初始化:每次创建对象时执行,用于初始化实例变量和执行非静态代码块。

初始化顺序:

  • 静态变量和静态代码块:按它们在类中出现的顺序执行,只在类加载时执行一次,在前面已经通过代码得到验证。
  • 实例变量和实例代码块:在每次创建对象时执行,执行顺序同样是按照它们在类中出现的顺序。
  • 构造函数:实例变量和实例代码块执行后,才会执行构造函数。

示例代码:

 

java

代码解读

复制代码

public class InitOrder { static { System.out.println("Static Block 1"); } static int x = print("Static Variable x"); static { System.out.println("Static Block 2"); } int y = print("Instance Variable y"); { System.out.println("Instance Block"); } public InitOrder() { System.out.println("Constructor"); } static int print(String message) { System.out.println(message); return 0; } public static void main(String[] args) { new InitOrder(); } }

输出结果:

 

scss

代码解读

复制代码

Static Block 1 Static Variable x Static Block 2 Instance Variable y Instance Block Constructor

从上面的例子中可以看出,静态块和静态变量按照声明顺序初始化(读者可以调整顺序验证),然后执行实例变量的初始化,接着执行实例块,最后调用构造函数。

三、JVM堆栈内存的分配

JVM在创建对象时,主要使用堆内存、栈内存进行分配:

  1. 堆内存:用于存储对象实例和数组。每当我们用 new 关键字创建一个对象时,都会在堆中分配内存,其实这是一个动态的过程。

  2. 栈内存:用于存储方法调用相关的信息,包括方法的参数、局部变量、操作数栈和返回地址。每次方法调用都会在栈中创建一个栈帧(Stack Frame)。

对象创建时的堆、栈内存分配过程:

  • 当 new 关键字用于创建对象时,JVM首先会在堆中为该对象分配内存空间,并初始化默认值(如 int 默认值为 0,对象引用默认值为 null)。
  • 然后,JVM会将对象的引用地址存储在栈中的局部变量表中。

示例代码:

 

java

代码解读

复制代码

public class MemoryAllocation { public static void main(String[] args) { Example example = new Example(); } }

在这段代码中,new Example() 会在堆中分配 Example 对象的内存,并在栈中存储指向这个对象的引用 example。当方法执行结束,栈帧被销毁,但堆中的对象只要有引用指向它,就不会被垃圾回收。

对于内存分配,这里本文不计划深入探讨了,计划未来再写篇文章。

四、赋值过程

在Java中,对象的赋值过程包括两个主要部分:默认初始化 和 显式初始化

  • 默认初始化:在对象内存分配后,JVM自动将对象的所有实例变量设置为其默认值。
  • 显式初始化:在默认初始化之后,Java按照代码中出现的顺序进行显式初始化。如果有构造函数,则在显式初始化完成后执行构造函数中的赋值操作。

示例代码:

 

java

代码解读

复制代码

public class ValueAssignment { int i = 5; String s = "Hello"; public ValueAssignment() { i = 10; s = "World"; } public static void main(String[] args) { ValueAssignment va = new ValueAssignment(); System.out.println("i: " + va.i + ", s: " + va.s); } }

输出结果:

 

yaml

代码解读

复制代码

i: 10, s: World

这里的过程如下:

  1. 默认初始化i 被设置为 0s 被设置为 null
  2. 显式初始化i 被赋值为 5s 被赋值为 "Hello"
  3. 构造函数初始化i 被重新赋值为 10s 被重新赋值为 "World"

结论

对象创建是Java程序中非常基础但复杂的过程,涉及类加载、静态与非静态初始化的顺序、内存分配和赋值等多个方面。理解这些细节对于编写高效、健壮的Java代码至关重要。通过本文的分析和示例代码,我们深入探讨了对象创建的每一个阶段以及它们在JVM中的具体实现方式,为深入掌握Java的内存模型和对象生命周期奠定了基础。

  • 7
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值