Java從內(nèi)存角度帶你理解數(shù)組名實質是個地址的論述
本文從Java語言的角度,探討一維數(shù)組與二維數(shù)組的內(nèi)存解析。
一、內(nèi)存的簡化結構
下圖即為內(nèi)存的簡化結構。在Java語言中,內(nèi)存的存儲分配是這樣的:
棧:局部變量
堆:new出來的東西,如對象、數(shù)組等
方法區(qū):包括靜態(tài)域(static)和常量池(String的內(nèi)容就存儲在這里)
內(nèi)存的簡化結構
這張內(nèi)存簡化圖非常重要,需要大家留有印象。
接下來我們在該圖和結論的基礎上,分步來看一維數(shù)組與二維數(shù)組的內(nèi)存解析。
二、一維數(shù)組的內(nèi)存解析
1. 分步解析
示例代碼
public class Test{ public static void main(String args[]){ int[] arr; arr = new int[10]; for ( int i=0; i<10; i++ ) { arr[i] =2*i+1; System.out.println(arr[i]); } } }
step1int[] arr;
此時在棧中創(chuàng)建出了變量arr:
step2 arr= new int[10];
接著使用new關鍵字,來創(chuàng)建一個一維數(shù)組。需要注意的是,基本數(shù)據(jù)類型數(shù)組在顯式賦值之前, Java會自動給它們賦默認值。由于一維數(shù)組的每個元素都是int類型,因而默認值為0.
step3 arr[i] =2*i+1;
在for循環(huán)中遍歷數(shù)組arr并為其元素賦值:
綜合起來看,內(nèi)存狀態(tài)如下:
2. 綜合解析
如圖,創(chuàng)建數(shù)組并賦值的過程可以簡化成如下示意圖。
左側為棧區(qū),右側為堆區(qū)
當聲明數(shù)組 int[ ] arr時,arr屬于局部變量,在棧中創(chuàng)建。如上圖中,int[ ] arr1和String[ ] arr2的操作執(zhí)行后,在左側的棧中創(chuàng)建了變量arr1與arr2.若沒有new的操作,實際上還未給數(shù)組開辟存儲空間。
只有當通過new關鍵字創(chuàng)建數(shù)組對象后,系統(tǒng)才在堆中劃分相應的存儲空間,并依據(jù)數(shù)組元素的數(shù)據(jù)類型給新劃分的空間自動賦初值。如上圖中,在new int[4]后,堆區(qū)開辟了4個連續(xù)的存儲空間,并賦值為0,而new String[3]后,堆區(qū)又開辟了3個存儲空間用來存儲String類型的數(shù)據(jù),引用類型String默認初值為null.
同時,數(shù)組的地址(即數(shù)組首元素的地址,假設為0x12ab)賦值給棧區(qū)中的變量arr1,即變量arr1中存著數(shù)組的地址,通過該地址,arr1可以輕松地在堆中找到它對應的數(shù)組。
我們通過中括號 [ ] 來訪問數(shù)組中的各個元素。我們執(zhí)行arr1[0] = 10;這一代碼時,實際便是根據(jù)棧內(nèi)arr1中存著的地址,找到將堆區(qū)中的數(shù)組空間,并將第一個空間中的0改為了10.
而對于arr2[ ]數(shù)組,在new過之后又new了一次,第二次通過new String[5]開辟了一片5個存儲空間的數(shù)組。這時變量arr2中原本存著的地址0x34ab被新地址0x78cd覆蓋,arr2變量存放了新數(shù)組的地址。原數(shù)組在后續(xù)的某個時間內(nèi),將被自動回收。
當然,當棧區(qū)存有數(shù)組地址的變量arr1與變量arr2最終出棧后,在堆區(qū)劃分的數(shù)組空間也將在之后被回收。
三、二(多)維數(shù)組的內(nèi)存解析
1. 綜合解析
二維數(shù)組是“數(shù)組的數(shù)組”,即一個一維數(shù)組中每個元素也是數(shù)組。由于數(shù)組既可以存儲基本數(shù)據(jù)類型,也可以存儲引用數(shù)據(jù)類型,因而“數(shù)組中存數(shù)組”的理解是可行的。
二維數(shù)組的創(chuàng)建過程與一維數(shù)組類似,這里便不再分步解析。我們直接來看內(nèi)存解析圖:
二維數(shù)組的內(nèi)存解析
與一維數(shù)組的不同之處在于,二維數(shù)組中外層元素也用于存儲地址。在創(chuàng)建二維數(shù)組時,除了在堆區(qū)中為外層元素(一維數(shù)組)開辟了存儲空間外,還為內(nèi)層元素開辟了存儲空間。同時,內(nèi)層元素的首元素地址返回給外層元素。
這樣,數(shù)組名arr1與一維數(shù)組名arr1[ ] 像橋梁一樣連接到內(nèi)層元素arr1[ ][ ]。通過地址和存有地址的棧區(qū)變量arr1、堆區(qū)中的一維數(shù)組arr1[ ],我們可以輕松地訪問到內(nèi)層元素。
其中,內(nèi)層元素的長度可以不相等。int[ ][ ] arr = new int[ ][ ]{{1,2,3},{4,5},{6,7,8}};這樣也是可行的,從圖中就能清晰的看出,內(nèi)層元素之間其實是相對獨立的。
2. 默認初始化方式對初始值的影響
針對于 int[][] arr = new int[4][3]; 這樣將內(nèi)外層元素個數(shù)都指定了的初始化方式,外層元素的初始化值為地址值,內(nèi)層元素的初始化值則與一維數(shù)組初始化情況相同(由元素的數(shù)據(jù)類型而決定)。
而針對 int[][] arr = new int[4][]; 這樣省略內(nèi)層元素長度的初始化方式而言,外層元素的初始化值為null(相當于未賦值的引用數(shù)據(jù)類型),內(nèi)層元素則根本沒有初始化值而言(空間還沒開辟),不能調用,否則編譯器將報錯。
測試
//測試代碼 public class ArrayTest { public static void main(String[] args) { int[][] arr = new int[4][3]; System.out.println(arr[0]); //[I@15db9742 System.out.println(arr[0][0]); //0 //System.out.println(arr); System.out.println("***********************"); float[][] arr1 = new float[4][3]; System.out.println(arr1[0]); //地址值 System.out.println(arr1[0][0]); //0.0 System.out.println("***********************"); String[][] arr2 = new String[4][2]; System.out.println(arr2[1]); //地址值 System.out.println(arr2[1][1]); //null System.out.println("*********************"); double[][] arr3 = new double[4][]; System.out.println(arr3[1]); //null // System.out.println(arr3[1][0]); //報錯 } }
總結
單看數(shù)組名,實際上是一個創(chuàng)建在棧區(qū)的局部變量。整個數(shù)組數(shù)據(jù)量可能較大,直接把數(shù)組內(nèi)所有的元素都存放在棧區(qū)是不太妥當?shù)摹R蚨?,?shù)組的主體部分實際上開辟在堆區(qū)。
要想訪問數(shù)組,若堆區(qū)的內(nèi)容與棧區(qū)的數(shù)組名arr之間沒有任何關系,是無法找到想要訪問的數(shù)組內(nèi)容的。因而,堆區(qū)會返回開辟的數(shù)組空間首元素的地址作為數(shù)組主體的地址,傳給棧區(qū)的局部變量arr(它是一個數(shù)組名)。若是二維數(shù)組,內(nèi)層元素的首元素地址會作為外層元素(也就是一維數(shù)組名)的內(nèi)容。
通過地址和存儲地址的“數(shù)組名”、“一維數(shù)組名”,我們可以一連串地找到我們想要訪問的數(shù)組元素。
該部分內(nèi)容我用文字表述可能不夠簡練,大家更多地可以看圖,圖為重點,通過圖示來理解內(nèi)存解析更好一些。
到此這篇關于Java從內(nèi)存角度帶你理解數(shù)組名實質是個地址的論述的文章就介紹到這了,更多相關Java數(shù)組名內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- Java數(shù)組(Array)最全匯總(上篇)
- Java之數(shù)組在指定位置插入元素實現(xiàn)
- Java自定義一個變長數(shù)組的思路與代碼
- Java中如何將?int[]?數(shù)組轉換為?ArrayList(list)
- Java中將 int[] 數(shù)組 轉換為 List分享
- java如何將int數(shù)組轉化為Integer數(shù)組
- 淺談Java當作數(shù)組的幾個應用場景
- 計算Java數(shù)組長度函數(shù)的方法以及代碼分析
- Java C++題解leetcode915分割數(shù)組示例
- Java?從json提取數(shù)組并轉換為list的操作方法
- Java數(shù)據(jù)結構之稀疏數(shù)組的實現(xiàn)與應用
- Java?C++題解leetcode1441用棧操作構建數(shù)組示例
- Java postgresql數(shù)組字段類型處理方法詳解
- Java中數(shù)組的常見操作合集
- 關于Java?SE數(shù)組的深入理解
- Java二維數(shù)組與稀疏數(shù)組相互轉換實現(xiàn)詳解
- Java數(shù)組隊列及環(huán)形數(shù)組隊列超詳細講解
- Java數(shù)組(Array)最全匯總(中篇)
相關文章
IDEA配置tomcat的方法、IDEA配置tomcat運行web項目詳解
這篇文章主要介紹了IDEA配置tomcat的方法、IDEA配置tomcat運行web項目詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07springboot之security?FilterSecurityInterceptor的使用要點記錄
這篇文章主要介紹了springboot之security?FilterSecurityInterceptor的使用要點記錄,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12