可能Java 數組大家都很熟悉,最近我遇到了一個關於Java 數組內存分配的問題。
呵呵。突然就發現許多書上“基本數據類型存儲在棧內存當中,對象則保存在堆內存”這句話完全是錯誤的。下面是個簡單的例子代碼:
代碼如下:
public class Test {
public static void main(String[] argv) {
// 靜態初始化數組
String[] names = { "Michael", "Orson", "Andrew" };
// 動態初始化數組
String[] animal = new String[4];
// 讓animal 指向 namens 數組所引用的數組
names = animal;
System.out.println(names.length);
System.out.println(animal.length);
}
}
“Java 數組大小是不能改變的”這可能大家都聽過,那上面這段代碼就有問題了,animal [] 長度為4,而names [] 數組的長度只有3,但是經過一個賦值語句,兩個數組的大小就都變為4了。這不是改變了數組的大小嗎? 問題就這樣擋在面前了!好吧,問問技術前輩吧,就這樣對數組的存儲方式有了全新的認識。下面是我的一點理解:(如果有錯誤的,剛好被大神你看到了,也請你能夠指出來。)
上面的的 names 和 animal 不代表這個數組對象,而僅僅是數組的變量而已,和C裡面的指針是一樣的,這樣的變量叫做引用變量。數組對象是保存在堆內存當中,大小當然是不能改變的,但是數組變量卻能夠指向其他的數組對象,可以看看下面這個圖:
藍虛線是賦值語句 names = animal; 之前 names 和 animal 數組變量指向的堆內存當中數組對象; 紅線是是賦值語句 names = animal;之後 names 和 animal 數組變量都同時指向一個數組對象。當然這時候 Java 垃圾回收機制這時候就會發現那個沒人引用的數組對象然後把它帶走。 從上面還可以看到,“Michael”,"Orson","Andrew" 這些都是基本的數據類型吧。但是他們卻存儲在堆內存當中。 實際上應該這樣說:局部變量放在棧內存當中,(像上面的 names[],animal[] 這種引用類型的變量,還有一些基本類型的變量),但應用變量所引用的對象是保存是堆內存當中的。(包括數組還有一些我們平常寫的普通的類對象)Java在堆內存當中的對象通常是不允許直接訪問的,但你可以想到直接訪問的後果。為了訪問堆內存當中的對象,這時候就需要引用變量這個中介。什麼時候Java存儲在棧內存中的變量是僅僅是引用變量? 什麼時候它又換了身份變為貨真價實的JAVA對象納?嗯,看看下面這個例子:
代碼如下:
public class Animal {
private String name;
private int age;Animal(String name, int age) {
this.name = name;
this.age = age;
}public void info() {
System.out.println(name + " " + age);
}
}
public class Test { public static void main(String[] argv) {
// 動態初始化數組
Animal[] animal = new Animal[2];
Animal cat = new Animal("cat", 1);
Animal dog = new Animal("dog", 2);
animal[0] = dog;
animal[1] = cat;// 當數組變量引用對象的方法(或者屬性)的時候,它就變為實際的Java 對象
System.out.println(animal.length);
//dog 這個原本存儲在棧內存當中的對象引用通過調用對象的方法變為實際的對象
dog.info();
animal[0].info();
}
}
只有當棧內存中的引用變量調用了對象的方法或者是指向了對象的屬性的時候,它就從變量真正成了對象了。(比如上面例子中的 cat,dog 對象引用變量,animal[]數組變量)。 通過animal[0] = dog;
animal[1] = cat; 使得兩個變量都指向了存儲在堆內存當中的對象,所以他們倆個打印出來的信息是一模一樣的.
上圖中藍線是賦值語句: animal[0] = dog;
animal[1] = cat; 之前的變量指向的狀態,紅虛線是賦值語句之後的狀態,animal[0]和dog ,animal[1] 和cat 所指向的都是相同的堆內存空間。