java 数组 内存_图解Java数组的内存分配
1. Java數組是靜態的
Java是靜態語言,所以Java的數組也是靜態的,即:數組被初始化后,長度不可變
靜態初始化:顯式指定每個數組元素的初始值,系統決定數組長度
String[] books = new String[]{"瘋狂Java講義","Thinking in Java","Java核心技術卷"};
String[] names = new String[]{"張三","李四","王五","趙六"};
動態初始化:顯式指定數組的長度,系統決定每個數組元素的初始值
String[] strArr = new String[5];
以上兩種初始化的內存使用情況如圖所示:
動態初始化數組由系統決定每個數組元素的初始值,遵循以下規則:
byte、short、int、long類型的數組的初始值為0
float、double類型的數組的初始值為0.0
char類型的數組的初始值為'\u0000',表現為空字符'',但要占位
boolean類型的數組的初始值為false
引用類型的數組的初始值為null
一旦數組初始化完成,則長度不可改變,以下代碼造成了數組初始化后長度可改變的假象:
books = names;
strArr = books;
System.out.println(books.length); // 4
System.out.println(strArr.length); // 4
Java的數組變量是一種引用類型的變量,數組變量并不是數組本身,它只是指向堆內存中的數組對象,改變一個數組變量所引用的數組,可以造成數組長度可變的假象。
上述代碼讓books數組變量和strArr數組變量都指向了names數組變量引用的數組,這樣做的結果就是讓三個數組變量都引用了相同的數組對象。內存圖如下:
而原來strArr和books引用的數組對象的長度不會改變,由于已經沒有變量引用它們,它們成為垃圾,等待垃圾回收機制來回收。此時,程序使用strArr、books、和names這三個變量時,將會訪問同一個數組對象。
JavaScript是動態語言,其數組的長度可以改變:
var arr = [];
document.write("arr的長度為:" + arr.length + "
"); // 0
arr[0] = 1;
document.write("arr的長度為:" + arr.length); // 1
2. 數組一定要初始化嗎?
實際上,懂得了Java數組的內存機制后,我們可以說:數組不是必須初始化,或者說,數組初始化不僅有之前介紹的兩種方法,可以完全換一種方法:
int[] nums = new int[]{1, 2, 3, 4, 5};
int[] arr;
arr = nums;
對于數組對象來說,必須初始化,也就是為該數組對象分配一塊連續的內存空間,連續內存空間的長度就是數組對象的長度。
對于數組變量來說,不需要進行初始化,只需讓其指向一個有效的數組對象就可以。
實際上,所有引用類型的變量,其變量本身不需要任何初始化,需要初始化的是它所引用的對象。
3. 基本類型數組的初始化
程序直接先為數組分配內存空間,再將數組元素的值存入對應的內存中。
int nums; ①
System.out.println(nums); ②
nums = new int[]{1, 2, 3, 4}; ③
System.out.println(nums.length); ④
我們來分析①-④步的內存:
① 定義nums數組變量后的存儲示意圖如下:
對于②行代碼來說沒有任何問題,雖然nums變量并未引用到有效的數組對象,但此時并未通過nums變量訪問數組對象的方法或屬性,所以沒有問題。
③ 靜態初始化nums數組后的存儲示意圖如下:
對于④行代碼來說,此時訪問了nums數組對象的屬性,所以要求nums必須引用一個有效的對象。
當通過引用變量來訪問實例屬性或者調用非靜態方法時,如果該引用變量還未指向有效的對象,程序就會拋出運行時異常:NullPointerException,例如,第二行代碼如果為:
System.out.println(nums.length);
就會拋出NullPointerException
誤區:基本類型的數據的值存儲在棧內存中
實際上,應該是所有的局部變量都保存在棧內存中,不管是基本類型還是引用類型,局部變量都保存在各自的方法棧中。
4. 引用類型數組的初始化
引用類型數組的數組元素仍然是引用類型,因此數組元素里存儲的還是引用,它指向另一塊內存,這塊內存里存儲了該引用變量所引用的對象,包括數組和Java對象。
下面程序的執行代表了引用類型數組初始化的典型過程:
class Person{
String name;
int age;
public void info(){
System.out.println("name:" + name + ", age:" + age);
}
}
// 創建兩個Person對象
Person p1 = new Person();
p1.name = "張三";
p1.age = 20;
Person p2 = new Person();
p2.name = "李四";
p2.age = 30;
// 將兩個Person對象賦給數組元素
person[0] = p1;
person[1] = p2;
// 結果一樣,p1和person[0]指向同一個對象
p1.info();
person[0].info();
分析內存:
5. 數組元素就是變量
當數組引用變量指向一個有效的數組對象之后,程序就可以通過該數組變量訪問數組對象。
Java語言不允許直接訪問堆內存中的對象,因此無法直接訪問堆內存中的數組對象,程序將通過數組引用變量來訪問數組對象。
Java語言避免直接訪問堆內存可以使程序更加健壯,如果程序直接訪問并修改堆內存中的數據,可能破壞內存的數據完整性,從而導致程序Crash。
無論哪種類型的數組,其數組元素其實相當于一個普通變量。
下面程序師范了將數組元素和普通變量相互賦值的情形:
class Cat{
String name;
double height;
public Cat(String name, double height){
this.name = name;
this.height = height;
}
public void info(){
System.out.println("name:" + name + ", height:" + height);
}
}
public class TestArray {
public static void main(String[] args) {
int[] a = new int[5];
for(int i=0; i
a[i] = ++i;
}
int x = a[0];
int y = 99;
a[4] = 99;
Cat[] cats = new Cat[2];
cats[0] = new Cat("咪咪", 10.0);
cats[1] = new Cat("卡卡", 12.2);
Cat c1 = cats[0];
Cat c2 = new Cat("豆豆", 11.1);
cats[1] = c2;
}
}
內存分析:
6. 沒有多維數組
所謂多維數組,其實只是數組元素元素依然是數組的數組,1維數組的數組元素的基本類型或者引用類型,2維數組的數組元素是1維數組,3維數組的數組元素是2維數組...N維數組的數組元素是N-1維數組。
Java允許將多維數組當成1維數組來處理,初始化多維數組時可以先初始化最左邊的維數,此時該數組的每個數組元素都相當于一個數組引用變量,這些數組還需要進一步初始化。
如下程序示范了多維數組的用法:
public static void main(String[] args) {
int[][] a;
a = new int[4][];
// 把a當成一維數組,遍歷a數組的每個數組元素
for(int i=0; i
System.out.println(a[i]); // 輸出都為null
}
a[0] = new int[2];
a[0][1] = 2;
for(int i=0; i
System.out.println(a[0][i]); // 輸出0和2
}
}
內存分析:
說明:一般我們認為通過數組的length屬性可以獲取數組的長度,其實這個說法不夠準確,例如數組a,它是一個傳統的“二維數組”,它的長度好像定義不是很明確,其實,數組的長度指的是數組所分配的連續內存空間的長度,所以說a數組的長度為4.
我們用一個“極端”的程序來更好的理解數組在內存中的分配機制:
public static void main(String[] args) {
Object[] objArr1 = new Object[3];
objArr1[1] = new Object[2];
Object[] objArr2 = (Object[]) objArr1[1];
objArr2[1] = new Object[3];
Object[] objArr3 = (Object[]) objArr2[1];
objArr3[1] = new int[5];
int[] iArr = (int[]) objArr3[1];
for(int i=0; i
iArr[i] = ++i;
}
}
內存分析:
此時的objArr1不再是一個簡單的一維數組,它是一個四維數組。
最后我們執行如下語句:
System.out.println(((int[])((Object[])((Object[])objArr1[1])[1])[1])[2]);
通過以上內存分析不難得出結果為3.
通過上面的分析不難得出,多維數組的本質仍然是一維數組。
因為Java是強類型語言,所以上訴代碼需要多次強制類型轉化,如果用弱類型語言實現會簡單很多,比如用JavaScript實現:
var objArr1 = [];
objArr1[1] = [];
var objArr2 = objArr1[1];
objArr2[1] = [];
var objArr3 = objArr2[1];
objArr3[1] = [];
var iArr = objArr3[1];
for(var i=0; i<5; i++){
iArr[i] = i;
}
document.write(objArr1[1][1][1][2]);
總結
以上是生活随笔為你收集整理的java 数组 内存_图解Java数组的内存分配的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python序列操作函数有哪些_Pyth
- 下一篇: java中的方法在哪里_Java中的本机