Java進階——數組與內存控制
下面將會深入探討Java數組的靜態特徵。使用Java數組前必須對數組進行初始化,初始化的過程即給數組的所有元素都分配合適的內存空間,並指定初始值的過程。數組初始化以後將不能重新改變數組對象在內存中的位置和大小。從用法角度看,數組元素相當於普通變量,程序既可以吧數組的值賦給普通變量,也可以把普通變量的值賦給數組元素。但其實質是對內存中數組元素對象的操作。
1、數組初始化
Java語言是典型的靜態語言,所以Java的數組也是靜態的。Java中的數組必須經初始化後纔可以使用,初始化即爲數組對象的所有元素分配一個連續的內存空間,併爲每個元素指定初始值。數組的初始化有兩種方式:
>靜態初始化:由程序員顯式地指定每個數組元素的初始值,由系統決定數組的長度;
>動態初始化:程序員只指定數組長度,由系統爲數組元素分配初始值。
下面是兩種初始化方式的實例:
public class test01 {
public static void main(String[] args) {
//靜態初始化數組
String[] str0 = new String[]{"小武靈靈" , "41009160"};
//靜態初始化的簡化形式
String[] str1 = {"小武靈靈" , "41009160"};
//動態初始化
String[] str2 = new String[2];
}
}
Java語言的數組變量是引用類型的變量,上面的三個數組變量按照聲明的前後順序(注意這裏不是初始化的先後順序)存儲在main棧區中,其引用的數組是按照數組元素的順序分別在堆內存中分配到長度爲2的連續空間。
注意,雖然這裏str0和str1變量引用的數組內容相同,但是絕不是同一個數組,也就是說內存中除了str2外,還有兩個長度內容都相同的數組。
注意,鼎泰初始化和動態初始化不能同時使用。
而執行動態初始化時,程序員指定了數組的長度,即爲每個數組元素指定所需的內存空間,系統負責爲這些數組元素分配初始值。系統將按照如下規則分配到初始值:
>基本類型:
>整數類型(byte , short , int , long):數組元素的初始值爲0;
>浮點類型(float , double):0.0;
>字符類型(char , string):’\u000’;
>布爾型(boolean):false;
>引用類型(類、接口和數組):null
1)、數組變量和數組對象
Java的數組變量是一種引用類型的變量,通常情況下被存放在棧內存中,它並不是數組本身,它只是指向堆內存中的數組對象(跟C語言裏的指針差不多)。
數組對象是保存在堆內存中的連續內存空間。
理解了這兩個概念之後,我們可以對數組的初始化有一個更深的認識,即對數組執行初始化,其實並不是對數組變量執行初始化,而是要對數組對象執行初始化(即爲該數組對象分配一塊連續的內存空間)。
認識到這一點之後我們可以解釋這樣一種假象,即認爲數組的長度是可變的,例如下面的實例:
public class test01 {
public static void main(String[] args) {
String[] str0 = new String[]{"小武靈靈" , "41009160" , "陝西師範大學"};
String[] str1 = new String[2];
str1 = str0;
System.out.println("str1的長度爲:" + str0.length);
}
}
相信大家可以自行解釋這種假象,這裏我就不解釋啦!
2)、基本類型數組的初始化
基本類型數組的初始化過程爲:程序直接爲數組分配內存空間,再將數組元素的值存入對應的內存裏。例如下面的代碼塊:
String[] str;
str = new String[]{“小武靈靈” , “41009160”, “地信1班”};
注意,所有局部變量都是放在棧內存裏保存的,不管其是基本類型的變量,還是引用類型的變量,都是存儲在各自的方法棧區中;但引用類型變量所引用的變量(包括數組、普通Java對象)則總是存儲在對內存中。
注意,引用變量本質上只是一個指針,只要程序通過引用變量訪問屬性,或通過引用變量來調用方法,該引用變量將會由它所引用的對象代替。
3)、引用類型數組的初始化
引用類型數組的數組元素依然是引用類型的,因此數組元素裏存儲的還是引用,它指向另一塊內存,這塊內存裏存儲了該引用變量所引用的對象。例如:
class Student {
public String name;
public int number;
public void output() {
System.out.println(name + “,”+number);
}
}
public class Main{
public static void main(String[] args) {
Student[] stu = new Student[2];
System.out.println(“數組長度:” + stu.length);
Student xiaowu = new Student();
xiaowu.name = “小武”;
xiaowu.number = “41009160”;
stu[0] = xiaowu;
//下面兩行代碼所輸出的結果都是一樣的
stu[0].output();
xiaowu.output();
}
}
在上面的程序中,內存是這樣分配的:首先引用數組變量stu[]初始化完成後,內存中開闢出了長度爲二的空間用來存儲student對象;對引用變量xiaowu初始化完成後,內存中開闢了出了能夠存儲一個student對象長度的空間,用來存儲student對象;將xiaowu賦給stu[0],則在內存中數組stu[0]指向對象xiaowu。
2、數組的使用
當數組引用變量指向一個有效的數組對象之後,程序就可以通過該數組引用變量來訪問數組對象。Java語言不允許直接訪問對內存中的數據,因此無法直接訪問堆內存中的數組對象,程序將通過數組引用變量來訪問數據。
要對數組有一個更深刻的認識,需要理解兩個關係:數組元素與變量間的關係、多維數組與變量間的關係。
1)、數組元素與變量間的關係
只要在已有數據類型之後增加括號,就會產生一個新的數組類型,例如:
>int à int[]:在int類型後增加[]即變爲int[]數組類型
>Stirng à String[]:在String類型後增加[]即變爲String[]數組類型
當程序需要多個類型相同的變量來保存程序狀態時,可以考慮使用數組來保存這些變量。當一個數組初始化完成,就相當於定義了多個類型相同的變量。
無論哪種類型的數組,其數組元素其實相當於一個普通變量,把數組類型之後的括號去掉之後得到的類型就是該數組元素的類型。
從這個角度講,數組元素就是變量。
注意,這裏的數組元素並不是指數組對象中的數組元素,而是指數組變量的數組變量元素。
注意,main方法聲明的變量都屬於局部變量,因此它們都被保存在main方法棧中;但數組元素則作爲數組對象的一部分,總是保存在堆內存中,不管它們是基本類型的數組元素,還是引用類型的數組元素。
2)、多維數組與變量間的關係
理解了數組元素與變量之間關係之後,如果已有的類型是int,在其後增加[]後得到一個數組類型;以int[]類型爲已有類型,在其後增加[]後得到int[][]依然是數組類型;
以int[][]類型爲已有類型,在其後增加[]後得到int[][][]依然是數組類型。
從上面可以看出,所謂多維數組,其實就是數組元素依然是數組的1維數組,2維數組是數組元素是1維數組的數組,3維數組是數組元素是2維數組的數組,4維數組是數組元素是3維數組的數組……N維數組是數組元素是N-1維數組的數組。
所以,實際上我們可以這樣認爲:沒有多維數組,只有基本數據類型和引用數據類型。
綜上,數組的初始化有靜態初始化和動態初始化兩種;數組對象和數組變量不是同一個概念對於數組變量而言;數組元素的類型分爲基本類型的數組和引用類型的數組;所有局部變量都是存放在棧內存裏保存的,但引用類型變量所引用的對象則總是存儲在堆內存中;一定要區分它何時只是數組變量,何時代表數組對象本身;數組元素就是變量,沒有多維數組。