🌀🌀🌀作者:@小魚不會騎車
🍁🍁🍁專欄:《java練級之旅》
🎀🎀🎀個人簡介:一名專科大一在讀的小比特,努力學習程式設計是我唯一的出路😎😎😎
🙈🙈🙈作者心裡話![]()
《java練級之路》之數組為什麼成為引用類型
小魚一直都是秉承着“開開心心看部落格,快快樂樂學知識”這個觀點來給大家用一些接地氣的話進行講解,可能會有人覺得小魚太墨迹了,小魚這裡也很樂意接受大家的意見,會進行采納,大家也可以指出小魚的不足,小魚也會積極的進行改變,總之,願我們越來越優秀
前言
在這篇文章中,小魚會細緻的講解大家在初識java時對于java中數組的認知,前面一篇文章講解了數組的建立及初始化,那麼這片文章呢,就會講到關于數組在記憶體中是如何存儲的,準備好了嗎各位開始發車了😜
(數組是引用類型)
🍎 初識JVM的記憶體分布
💡在我們的java中,記憶體是一段連續的存儲空間,主要用來存儲程式運作時資料的。比如:
如果對記憶體中存儲的資料不加區分的随意存儲,那對記憶體管理起來将會非常麻煩。
- 程式運作時代碼需要加載到記憶體
- 程式運作産生的中間資料要存放在記憶體
- 程式中的常量也要儲存
- 有些資料可能需要長時間存儲,而有些資料當方法運作結束後就要被銷毀
是以JVM也對所使用的記憶體按照功能的不同進行了劃分:
總共有五部分,
- 程式計數器 (PC Register): 隻是一個很小的空間, 儲存下一條執行的指令的位址
- 虛拟機棧(JVM Stack): 與方法調用相關的一些資訊,每個方法在執行時,都會先建立一個棧幀,棧幀中包含有:局部變量表、操作數棧、動态連結、傳回位址以及其他的一些資訊,儲存的都是與方法執行時相關的一些資訊。比如:局部變量。當方法運作結束後,棧幀就被銷毀了,即棧幀中儲存的資料也被銷毀了。
- 本地方法棧(Native Method Stack): 本地方法棧與虛拟機棧的作用類似. 隻不過儲存的内容是Native方法的局部變量.在有些版本的 JVM 實作中(例如HotSpot), 本地方法棧和虛拟機棧是一起的。
- 堆(Heap): JVM所管理的最大記憶體區域. 使用 new 建立的對象都是在堆上儲存 (例如前面的 new int[]{1, 2,3} ),堆是随着程式開始運作時而建立,随着程式的退出而銷毀,堆中的資料隻要還有在使用,就不會被銷毀。
- 方法區(Method Area): 用于存儲已被虛拟機加載的類資訊、常量、靜态變量、即時編譯器編譯後的代碼等資料. 方法編譯出的的位元組碼就是儲存在這個區域
但是我們在學習這一篇的内容時隻會涉及到棧區和堆區,稍後會進行詳細講解。
🍎 基本類型變量與引用類型變量
🍓1. 基本資料類型和引用類型變量
🐵我們接下來用三種方式開辟數組
int[]array1={1,2,3,4};//開辟一個靜态數組
int[]array2=new int[]{1,2,3,4};//同上(new int []可以舍去)
int[]array3=new int[4];//開辟一個動态數組
這是一個int類型,在記憶體中的存放方式如圖
int a=10;
a在main函數裡是局部變量,是以在棧上配置設定記憶體。
那麼我們的數組呢?在記憶體中是如何存儲的?🧐
在記憶體中,堆區比棧區大一些❗️❗️❗️
👉大家看,我們的第一個和第二個開辟數組的方式其實是一樣的,都是new一個空間出來,那麼new出來的空間是哪裡的呢?其實就是堆上的,我們從堆上開辟了一塊記憶體,用來存放這個而數組的值,
我們假設array1在堆中開辟記憶體的位址是0x123,那麼我們變量array1存放的就是0x123,也就是存放堆上開辟空間的位址。
🌈大家剛才看到了
int a=10
,在棧上存放的是int類型的資料,但是
int[]arry1
是一個引用類型,存放的是堆上開辟的記憶體的位址,那我們就可以稱array1這種變量為引用變量(引用),這時候我們就可以通過array1引用變量中存放的位址,找到這塊開辟的記憶體空間,
✅專業術語:array1指向了一個對象,這個對象就是在堆上開辟的記憶體
就這樣,我們就可以通過這個位址找到這塊空間。
int a=10;
int []array={1,2,3,4};
✅總結上述代碼
- a,array都是函數内部建立的變量,是以,其空間都在main方法對應的棧幀中配置設定。
- a是内置類型的變量,是以其空間儲存的就是給該變量初始化的值。
- array是數組類型的引用變量,其内部儲存的内容可以簡單了解成,是數組在堆空間中的首位址。
🍎 再談引用變量
🍓 1. 第一道基礎題
接下來給大家看一串代碼
int[]array1={1,2,3,4};
array1[0]=99;
int[]array2=array1;
array2[0]=100;
System.out.println(Arrays.toString(array1));
System.out.println(Arrays.toString(array2));
🐻大家猜第一個列印什麼第二個列印什麼?
大家現在可以暫時思考一下,心中有答案之後再往下劃,看看答案是不是跟自己想的一樣。
🐧好的,接下來就會給大家進行解析,我們先把代碼拷貝到畫圖闆
1️⃣我們現在進行了第一步,開辟空間,
2️⃣将首元素改為99
3️⃣将array1的值給array2
我們将a指派給b我們的b就是10,那我們把array1給array2呢?因為我們的array1中存放的是位址,是以我們把array1的值指派給array2就是把位址指派給array2,如圖
是以我們的array2指向的空間也是位址為0x123的空間,
4️⃣将array2[0]的值改為100
因為我們array1和array2指向的同一塊空間,是以修改array2就就是修改array1,是以我們最後的輸出就是{100, 2, 3, 4}
綜合上述
🍓 2. 第二道基礎題
🙈好的,大家再來看這串代碼(小魚又要挖坑了呦)
int[]array1={1,2,3,4};
int[]array2={11,22,33,44};
array1=array2;
array1[0]=1234;
System.out.println(Arrays.toString(array1));
System.out.println(Arrays.toString(array2));
讓我們來思考思考,這個會輸出什麼呢?
答案就在下面
輸出
大家想到的答案和這個一樣嘛?
接下來小魚給大家講解
1️⃣第一步依舊是開辟一塊空間
2️⃣就是把array2指派給array1
這樣我們array1的存放的位址和array2存放的位址就一樣,我們就可以通過array1找到array2位址指向的空間,
3️⃣ 我們将array[0]的值改為1234,
🐧好的,現在我們現在就可以發現,我們的array1和array2指向的是同一塊空間,并且我們的array1将下标為0的元素改為了1234,于是最後輸出就得到了我們上面的答案,當然,此時由于我們的array1找不到之前的空間了,是以之前開辟的空間就會被系統自動回收,不用擔心記憶體洩漏
✅結論
當兩個引用同時指向一塊對象時,通過其中任意一個引用去通路該對象并修改該對象的值,另一個引用去通路的時候,值也是被改變的
💡可以了解為對象時一個“空調”,我用這個“遙控器1”(array1)去修改它的溫度之後,我的“遙控器2”(array2)再去通路這個空調,就是已經被修改的溫度,當我的“遙控器2”去修改也是同理
🍎 認識null
🍓1. java中null注意事項
🌈在java當中局部變量一定要初始化不然會報錯❗️❗️❗️
🍰我們的b是int類型可以指派個0初始化,但是我們的array4呢?他是個引用,他存的是位址,該怎麼做才能讓他不去指向别的變量呢。我們可以用null(小寫)這個就是空指針,當我們置成空指針時,array4就不指向任何對象了
int b=0;
int[]array4=null;
System.out.println(b);
System.out.println(array4);
☀️但是呢,在我們初始化為空指針後,如果我想要通路它的第0個元素,就會報錯。
int[]array4=null;
System.out.println(array4[0]);
🍒這個就是咱們以後會經常涉及到的問題,也可以成為NPE,意思是空指針異常,當出現這個問題的時候,就需要去檢查了,這裡是8
😾這時候我們就需要根據提示
🍀我們的發現array4是null,如果我們的引用是null我們通過這個引用不管去做任何事他都會報錯,
int[]array4=null;
System.out.println(array4.length);
🍎 總結
- 我們的數組是存放在哪裡
- 如何通路到這個數組
- 數組的值是如何被修改的
- 在通路數組時需要注意的事項
- 如何避免空指針異常