學習筆記之從內存瞭解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講義》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章