前言
數組是日常中很常用的一種類型。
在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講義》