学习笔记之从内存了解Java数组

前言

数组是日常中很常用的一种类型。
在java中,与c语言不同的是,数组被当做一个对象,一种类型,他的定义也和c语言不一样,看代码:

//定义一个数组
int array[10]; //c语言
int[] array = new int[10];//java

从中也可以很明显地看出他们思想的不同,但实际上他们在内存中的实质是差不多的。不同的是java还可以使用引用类型的数组,例如:

Person[] persons = new Person[10];

而因为c语言中没有对象这种概念所以也没有这种类型。但因为C语言有结构体,所以也有类似的结构体数组,例如:

struct student stu[3];

上面讲的日常的使用,无论是c还是java我们都了解,但是他们在内存中的存储形式了解过吗?了解数组在内存中的存储形式,才能帮助我们更好的了解数组,以此来更好地使用他。下面我是从java的角度来讲述数组在java中的内存形式,至于c语言读者有兴趣可自行学习。

简单了解堆与栈

在java中内存被划分为堆和栈两个区域,在堆中还有一个静态区域,我把它简称为静态域。那么他们分别来干什么的呢?

  • 栈区:栈区主要用来存放方法以及引用变量。例如我们调用了eat()这个方法,那么这个方法就会入栈。另外当我们定义Preson person;(注意还没new)的时候也会在栈区放一个person引用。
  • 堆区:堆区主要用来存放对象。例如Person person = new Person();这样new Person()这个实例就放在堆区中。
  • 静态域:主要存放类以及静态变量静态方法等。

上面简单讲述一下三种内存,我没具体讲述,因为这不是重点,简单了解一下就好。重点在于Person person = new Person();这个person对象的存储。“栈区放一个person引用。”“new Person()这个实例就放在堆区中”这两句话不知道读者可否理解?简单点说,我们新建了一个person对象,这个对象的实体就存在堆中。然后这个实例引用,也就是变量person,存在栈中。虽然java没有指针这个东西,但是也可以类比c语言的指针。变量person可以看做一个指针,指向堆内存中存放person这个实例的内存。所以应该可以理解堆和栈的区别了吧。
同样对于数组,例如int[] num = new int[10];首先会在栈中放一个num引用,再在堆中开辟一个空间存放数组,然后再让引用num指向开辟的这个空间的首地址。这样应该就可以理解java数组在内存中的模型了吧。

引用类型数组

基本类型的数组的内存模型相信读者已经很清晰了,这里就不讲了。重点讲一下引用类型数组。
刚才讲到在java中,有引用类型的数组,例如Person[];c语言中也有结构体数组,但是,和结构体数组不同的是:引用类型数组存放的是引用,而结构体数组存放的是真正的结构体数据而不是引用。怎么理解呢,先看看C语言的结构体数组,看一下代码:

typedef struct{
		int a;
		int b;
	}s;
	s test[3];

首先他在内存中开辟了一个8*3字节的空间(一个int4个字节),每8个字节存放一个结构体s;然后再把指针test指向开辟的内存的首地址。这个相信应该很好理解。再来看看java:

Student[] persons = new Student[2];

Student student1 = new Student();
		student1.setAge(12);
		student1.setName("mike");
Student student2 = new Student();
		student2.setAge(13);
		student2.setName("sali");
		
		persons[0] = student1;
		persons[1] = student2;

这里首先在栈中创建一个persons引用,然后在堆中开辟一个内存,再把persons指向这个内存的首地址。但是,重点,这里开辟的内存并不是像c语言那样一整个结构体的内存,这里开辟的内存是用来存放引用的,也就是存放地址的,并不是真正的person类实例的地址,因为现在还没实例化Student对象,所以开辟的这个内存里面都是null,空指针。
当执行Student student1 = new Student();Student student2 = new Student();的时候,会在栈中放student1和student2两个引用,然后再堆中开辟内存放Student实例。所以这里student1和student2就是指向真正Student实例地址的引用。
persons[0] = student1;persons[1] = student2;最后这两句,把student1和student2两个引用所指向的地址给了数组persons,这样数组persons的两个引用元素指向的就是student1和student2所指向的实例。有点绕,但是希望读者好好理解一下。
接下来有个问题帮助更好地理解,先看代码:

		Student[] persons = new Student[2];
		
		Student student1 = new Student();
		student1.setAge(12);
		student1.setName("mike");
		Student student2 = new Student();
		student2.setAge(13);
		student2.setName("sali");
		
		persons[0] = student1;
		persons[1] = student2;
		
		student1.setName("huan");
		System.out.println(persons[0].getName());

这里就多了最后两句,输出的是什么?
答案是huan。其实很好理解,因为student1和persons[0]都是一个引用,都是指向同个实例,那么student1改动的内存,也就是改动了persons[0]所指向的内存,那么值肯定是会改变的。这样不知道你们可否理解了呢?

小结

了解底层内存机制,才能更好的了解数组,才能更好地使用数组。上面讲的有一点绕,但是希望读者可以好好了解。不只是数组,其他的数据结构也是要了解内存模型才能很好地理解并运用。不同的语言有不同的内存模型。看完有不懂得可以评论区留言。
·
·
·

参考资料

《疯狂java讲义》

猜你喜欢

转载自blog.csdn.net/weixin_43766753/article/details/101557540
今日推荐