java 实例化对象jvm_Java对象在内存中实例化的过程

Java对象在内存中实例化的过程

在讲 Java 对象在内存中的实例化过程前,先来说下在类的实例化过程中,内存会使用到的三个区域:栈区、堆区、方法区。

堆区:

存储的全部都是对象,每个对象包含了一个与之对应的 class 类的信息。

jvm 只有一个堆区(steap),它会被所有线程共享,堆中不存放基本数据类型和对象引用,它只存放对象本身。

栈区:

每个线程都包含一个栈区,栈中只保存基本数据类型的值和对象以及基础数据的引用。

每个栈中的数据(基本数据类型和对象的引用)都是私有的,其它栈是无法进行访问的。

栈分为三个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。

方法区:

又被称为静态区,它跟堆一样,被所有的线程共享,方法区包含所有的 class 信息 和 static修饰的变量。

方法区中包含的都是整个程序中永远唯一的元素,如:class、static变量。

1.1、Java 中的数据类型

Java 中的数据类型有两种:

1、基本类型(primitive types): 共有8种,即:int、short、long、byte、char、float、double、boolean(注意并没有 String 的基本类型),这8中类型的定义是通过诸如:int a = 5;long b = 22L;的形式来定义的,称为自动变量。注意:自动变量存的是字面值,不是类的实例,即不是类的引用,这里并没有类的存在;

如:int a = 5; 这里的 a 是一个指向 int 类型的引用,指向 5 这个字面值,这些字面值的数据由于大小可知,生存期可知( 这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了 ),出于追求速度的原因,这些字面值就存在于栈区中;

另外,栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义

int a = 3;

int b = 3;

编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理int b = 3;在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均指向3的情况。

特别注意的是:这种字面值的引用与类对象的引用不同。假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另一个对象引用变量也即刻反映出这个变化。相反,通过字面值的引用来修改其值,不会导致另一个指向此字面值的引用的值也跟着改变的情况。如上例,我们定义完a与 b的值后,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。

2、 包装类数据:如:String、Integer、Double等将相应的基本数据类型包装起来的类,这些数据全部存放在 堆 中, Java 用 new()语句来显示地告诉编译器,在运行时才根据需要动态创建,因此比较灵活,但缺点是要占用更多的时间。

1.2、类实例化时内存中发生的变化

首先我们先对下面的代码进行分析:

public class People{

String name; // 定义一个成员变量 name

int age; // 成员变量 age

Double height; // 成员变量 height

void sing(){

System.out.println("人的姓名:"+name);

System.out.println("人的年龄:"+age);

System.out.println("人的身高:"+height);

}

public static void main(String[] args) {

String name; // 定义一个局部变量 name

int age; // 局部变量 age

Double height; // 局部变量 height

People people = new People() ; //实例化对象people

people.name = "张三" ; //赋值

people.age = 18; //赋值

people.stuid = 180.0 ; //赋值

people.sing(); //调用方法sing

}

}

代码解析:

这段代码首先定义三个成员变量:String name、int age、Double height 这三个变量都是只声明了没有初始化,然后定义了一个成员方法 sing();

在 main()方法里同样定义了三个一样的变量,只不过这些是局部变量;

在main() 函数里实例化对象 people , 内存中在堆区内会给实例化对象 people 分配一片地址,紧接着我们对实例化对象 people 进行了赋值。people 调用成员方法 sing() 。mian()函数打印输入人的姓名,人的年龄和人的身高,系统执行完毕。

下面通过图解法展示实例化对象的过程中内存的变化:

375c833d338845ed39c4fd112d95c4d7.png

在程序的执行过程中,首先类中的成员变量和方法体会进入到方法区,如图:

5c48986e6fb39f94db64242b5f07a756.png

程序执行到 main() 方法时,main()函数方法体会进入栈区,这一过程叫做进栈(压栈),定义了一个用于指向 Person 实例的变量 person。如图:

0228208ecc3dea9ec8f6d4392d26c83f.png

程序执行到 Person person = new Person(); 就会在堆内存开辟一块内存区间,用于存放 Person 实例对象,然后将成员变量和成员方法放在 new 实例中都是取成员变量&成员方法的地址值 如图:

356401e2161daf48d6236e167f2624a2.png

接下来对 person 对象进行赋值, person.name = “小二” ; perison.age = 13; person.height= 180.0;

先在栈区找到 person,然后根据地址值找到 new Person() 进行赋值操作。

如图:

2931860fd72af4c9481b3add09bde562.png

当程序走到 sing() 方法时,先到栈区找到 person这个引用变量,然后根据该地址值在堆内存中找到 new Person() 进行方法调用。

在方法体void speak()被调用完成后,就会立刻马上从栈内弹出(出站 )

最后,在main()函数完成后,main()函数也会出栈 如图:

fc173800625697df7ee78ae46a97236d.png

以上就是Java对象在内存中实例化的全过程。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值