關(guān)于Java?SE數(shù)組的深入理解
1、數(shù)組的基本概念
1.1 我們?yōu)槭裁葱枰獢?shù)組?
假設(shè)說我們要存每個(gè)同學(xué)的期末考試總成績(jī),如果我們還不知道數(shù)組的話,那我們是不是得新建100個(gè)變量,而且賦值和打印也相當(dāng)?shù)穆闊?nbsp;而且我們發(fā)現(xiàn)成績(jī)的數(shù)據(jù)類型都是一樣的,所以就會(huì)有數(shù)組這個(gè)概念,數(shù)組即是相同類型元素的集合,而且是一塊連續(xù)的存儲(chǔ)空間,每個(gè)空間都有編號(hào),也就是我們口中常說的數(shù)組下標(biāo)。而且使用數(shù)組,也可以是代碼變得更簡(jiǎn)化,方便的進(jìn)行排序查找等,現(xiàn)在,我們就來進(jìn)入數(shù)組的學(xué)習(xí)把:
1.2 數(shù)組的創(chuàng)建與初始化
我們這期是由淺到深講解的,所以在前邊有一些不理解的問題,請(qǐng)堅(jiān)持往后看,會(huì)得到解決的。
在Java中對(duì)數(shù)組的創(chuàng)建并初始化有兩種方式,分別是靜態(tài)初始化和動(dòng)態(tài)初始化:
public static void arrayInit() { int[] array1 = { 1,2,3,4,5,6,7,8,9,10 }; //靜態(tài)初始化 int[] array2 = new int[] { 1,2,3,4,5,6,7,8,9,10 }; //靜態(tài)初始化 }
這兩種寫法有什么不同呢?第一種雖然省去了 new int[ ],但是在編譯的時(shí)候還是會(huì)還原成第二種方式,這兩種寫法本質(zhì)上沒有區(qū)別,都是在JVM棧上開辟一個(gè) array1和array2元素,同時(shí)也會(huì)在堆上開辟兩個(gè)一維數(shù)組,這兩個(gè)變量分別存這這兩個(gè)數(shù)組的地址,當(dāng)然,這里你不懂沒關(guān)系,后面我們也會(huì)畫圖一一講解到,你只需要知道,這兩種定義并初始化的方式只是寫法上的不同就可以了!
那定義了數(shù)組但是不初始化呢?
public static void arrayInit() { int[] array3 = new int[10]; //動(dòng)態(tài)初始化 int[] array4 = null; }
如果是上面這種創(chuàng)建但是不初始化,array3 里面會(huì)默認(rèn)初始化成0,也就是說,如果數(shù)組中存儲(chǔ)的元素為基本類型,默認(rèn)值為基本類型對(duì)對(duì)應(yīng)的默認(rèn)值0,像 array4 賦值成 null 這種情況,就可以理解成這個(gè)數(shù)組沒有引用任何對(duì)象,這個(gè)地方聽不懂沒關(guān)系,往后看就能懂了。
類型 | 默認(rèn)值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0 |
char | /u0000 |
boolean | false |
不知道學(xué)過C語(yǔ)言的小伙伴還記不記得,C語(yǔ)言數(shù)組是可以不完全初始化的,比如說你一個(gè)數(shù)組5個(gè)元素,你只初始化前三個(gè)元素,在C語(yǔ)言中后續(xù)未初始化的元素會(huì)默認(rèn)補(bǔ)0,但是在Java中你就算完全初始化了,如果你指定了元素個(gè)數(shù)的話,也會(huì)報(bào)錯(cuò),只有在使用 new int[10],動(dòng)態(tài)初始化的時(shí)候才能寫明數(shù)組元素個(gè)數(shù),你們也可以下來自己試試。
在Java的數(shù)組中,我們有幾點(diǎn)需要注意:
- 數(shù)組 { } 里面的元素類型一定要跟 [ ] 前面的類型保持一致
- 靜態(tài)初始化的時(shí)候不能指定長(zhǎng)度,動(dòng)態(tài)初始化的時(shí)候 new int[10]需要指定長(zhǎng)度
- 雖然沒有指定數(shù)組長(zhǎng)度,但是編譯器在編譯的時(shí)候會(huì)根據(jù) { } 里面的元素個(gè)數(shù)來確定數(shù)組的長(zhǎng)度
- 雖然Java數(shù)組可以寫成跟C語(yǔ)言一樣的寫法 int array[ ],但不推薦,因?yàn)檫@樣語(yǔ)義不明確!
- new int[n] ,[ ]中可以是變量
這里有小伙伴就有個(gè)小問題了,如果我定義了但是沒有初始化,我能不能在下面重新賦值呢?
public static void arrayInit() { int[] array3; array3 = { 1,2,3,4,5 }; }
這樣是絕對(duì)不可以的,雖然你現(xiàn)在可能不是很理解,但我們前面有提過,數(shù)組變量 array3 里面放的是地址,這個(gè)在下面我們會(huì)細(xì)講。這里你知道即可。
那如何正確的在定義之后初始化呢?或者重新賦值呢?
public static void arrayInit() { int[] array1; array1 = new int[10]; int[] array2; array2 = new int[]{ 1,2,3,4,5 }; }
像如上代碼這樣是ok的,本質(zhì)上其實(shí)就是在堆區(qū)創(chuàng)建了一個(gè)新對(duì)象,這個(gè)我們還是在后面細(xì)講,這里讓你們先見一見,為后面細(xì)節(jié)學(xué)習(xí)作鋪墊。
1.3 數(shù)組的使用
數(shù)組是定義好了,我們?nèi)绾稳ナ褂盟??在前面我們提到過,數(shù)組是一塊連續(xù)的內(nèi)存空間,都有對(duì)應(yīng)的編號(hào),也就是下標(biāo),我們就可以通過下標(biāo)的方式快速訪問數(shù)組的任意位置的元素:
public static void main(String[] args) { int[] array = { 1,2,3,4,5 }; array[2] = 88; System.out.println(array[2]); }
同時(shí)也可以對(duì)下標(biāo)的值進(jìn)行修改,但是這里我們要注意幾點(diǎn),數(shù)組的下標(biāo)是從 0 開始的,所以最后一個(gè)元素下標(biāo)應(yīng)該是 n-1,也就是Java中大部分采用的都是左閉右開的,即:[ 0,n )
如果我們下標(biāo)越界訪問了,不用擔(dān)心,程序會(huì)直接剖出下標(biāo)越界異常,我們根據(jù)報(bào)錯(cuò)提示去找對(duì)應(yīng)的位置即可。
1.4 數(shù)組的遍歷
在Java中對(duì)數(shù)組其實(shí)有三種遍歷打印的方式,每一種的情況都會(huì)講明優(yōu)缺點(diǎn),接著往下看:
for 循環(huán)打印:
public class TestDemo { public static void main(String[] args) { int[] array = { 1,2,3,4,5 }; for (int i = 0; i < array.length; i++) { System.out.print(array[i] + " "); } System.out.println(); } }
這種方式是很常規(guī)的通過下標(biāo)遍歷訪問的方式,對(duì)于數(shù)組的長(zhǎng)度我們可以通過,數(shù)組對(duì)象.length 來獲取數(shù)組的長(zhǎng)度。
for-each 循環(huán)打印:
public class TestDemo { public static void main(String[] args) { int[] array = { 1,2,3,4,5 }; for (int a : array) { System.out.print(a + " "); } System.out.println(); } }
這種打印方式呢,在 for 循環(huán)里冒號(hào)的左邊必須是你要打印的數(shù)組里元素的類型定義的變量,右邊則是你要打印數(shù)組的數(shù)組名,下面就可以通過你定義的變量進(jìn)行打印了。for-each是for循環(huán)的另一種使用方式,他能在代碼上更簡(jiǎn)潔,也可以避免循環(huán)條件和更新語(yǔ)句的書寫錯(cuò)誤,但是這樣也有缺陷,比如說這種方式并不能拿到數(shù)組的下標(biāo)。
Arrays工具類打印
public class TestDemo { public static void main(String[] args) { int[] array = { 1,2,3,4,5 }; String ret = Arrays.toString(array); //既然返回的是字符串,我們就用字符串類型來接收 System.out.println(ret); System.out.println(Arrays.toString(array)); //既然有返回值,那我們也可以直接鏈?zhǔn)皆L問 } }
首先我們要介紹下Arrays,他是在 java.util 包下的一個(gè)工具類,里面定義了很多對(duì)數(shù)組的操作方法,而 Arrays.toString 他的作用是把數(shù)組轉(zhuǎn)換成字符串并返回,所以嚴(yán)格意義上它并不是遍歷數(shù)組,只是把數(shù)組轉(zhuǎn)化成對(duì)應(yīng)的字符串而已。
以上三種方式各有優(yōu)缺點(diǎn),初學(xué)者結(jié)合實(shí)際情況作應(yīng)用
2、引用類型數(shù)組的深入講解
2.1 簡(jiǎn)單了解 JVM 的內(nèi)存分布
Java的程序需要運(yùn)行在虛擬機(jī)上的,如果對(duì)于內(nèi)存中的數(shù)據(jù)隨意存儲(chǔ)的話,那以后對(duì)內(nèi)存管理起來將會(huì)是很麻煩的一件事,所以JVM也對(duì)所使用的內(nèi)存按照不同的功能進(jìn)行了劃分:
但是我們今天只重點(diǎn)關(guān)心堆和虛擬機(jī)棧這兩塊空間,在后續(xù)學(xué)習(xí)中會(huì)依次詳細(xì)介紹這里面的內(nèi)容:
虛擬機(jī)棧:與方法調(diào)用相關(guān)的一些信息,每個(gè)方法在執(zhí)行的時(shí)候都會(huì)先創(chuàng)建一個(gè)棧幀,棧幀中包含:局部變量,操作數(shù)棧,動(dòng)態(tài)鏈接,返回地址,以及一些其他的信息,保存的都是與方法執(zhí)行相關(guān)的一些信息,比如:局部變量,當(dāng)方法運(yùn)行結(jié)束了后,棧幀也被銷毀,即棧幀中保存的數(shù)據(jù)也被銷毀了
堆:它是JVM管理的最大內(nèi)存區(qū)域,使用 new 創(chuàng)建的對(duì)象都是在堆上保存的,堆是隨著程序的開始而創(chuàng)建,隨著程序的退出而銷毀,堆中的數(shù)據(jù)只要還有在使用,就不會(huì)被銷毀。
2.2 基本類型變量與引用類型變量的區(qū)別
對(duì)這個(gè)理解特別重要,很關(guān)系到你后面進(jìn)行學(xué)習(xí),首先我們要區(qū)別這個(gè)兩個(gè)的區(qū)別,先說基本類型變量,這種變量中直接存放的是所對(duì)應(yīng)的值,而引用類型創(chuàng)建的變量,一般稱為對(duì)象的引用,這種變量里存儲(chǔ)的是對(duì)象所在空間的地址,下面我們用一小段代碼,并且畫圖讓大家理解的更清楚:
public class TestDemo { public static void main(String[] args) { int a = 10; int b = 20; int[] array = { 1,2,3,4,5 }; } }
其實(shí)看到這張圖,相信大家就能很好的理解了,其實(shí)我們引用變量并不是存儲(chǔ)了對(duì)象本身,只是存儲(chǔ)了對(duì)象的其實(shí)地址,那我們的 array 只是存儲(chǔ)了在堆中開辟的一維數(shù)組的地址!并沒有把整個(gè)數(shù)組都保存在變量中!通過該對(duì)象的地址,引用變量就可以去操作這個(gè)對(duì)象了!
2.3 通過方法更深刻理解引用變量
有了上面的認(rèn)識(shí),我們就要來理解下面這兩個(gè)方法的作用了,相信你看完會(huì)有更深刻的認(rèn)識(shí):
是不是結(jié)果可能跟你想得有點(diǎn)不一樣呢?不用擔(dān)心,我們來一個(gè)個(gè)分析下:
首先我們執(zhí)行的是 func1 我們知道 array 變量中存的是一個(gè)對(duì)象的地址,那么通過傳參給 func1 的 arr1,首先要建立棧幀,把 array 存的地址拷貝到 arr1 當(dāng)中,這樣一來就相當(dāng)于 arr1 也指向了那個(gè)變量,但是我們又 new 了一個(gè)對(duì)象,并把新對(duì)象的地址賦值給了 arr1,相當(dāng)于 arr1 原來存放的地址已經(jīng)被替換了,也就說 arr1 指向了一個(gè)新的對(duì)象,因?yàn)橹皇前?array 存的地址拷貝給了 arr1 所以執(zhí)行完 func1 這個(gè)方法之后,array 并不會(huì)受任何影響,當(dāng)方法結(jié)束,arr2 變量銷毀,因?yàn)閍rr2 銷毀之后沒有變量接著引用在 func1 中 new 的新對(duì)象,所以此時(shí)新的對(duì)象就被 JVM 回收了!
當(dāng)我們執(zhí)行完 func1 時(shí),就是我們說的結(jié)果,所以 array 的值不受任何影響!
我們接著再來看執(zhí)行 func2 之后的結(jié)果,首先前半部分與 func1 一樣,都是傳遞的 array 指向?qū)ο蟮牡刂?,但?func2 里面語(yǔ)句是直接對(duì) arr2 中對(duì)象的地址進(jìn)行下標(biāo)訪問,修改了 [1] 下標(biāo)處的值,因?yàn)楸举|(zhì) array 和 arr2 引用的都是同一個(gè)對(duì)象,當(dāng) arr2 修改了對(duì)象的值,所以當(dāng)函數(shù)結(jié)束后接著打印 array 指向?qū)ο蟮闹悼隙ㄒ脖恍薷牧?!方法結(jié)束,arr2 被銷毀,但是 arr2 指向的對(duì)象仍然被 array 指向著,所以JVM不會(huì)回收此時(shí)的對(duì)象!
看到這,你肯定有了更深刻的理解,前面有疑問的地方肯定得到了解決,所謂的 "引用" 本質(zhì)上只是存了一個(gè)地址,Java將數(shù)組設(shè)定成引用類型,這樣后續(xù)進(jìn)行數(shù)組傳參,其實(shí)只是將數(shù)組的地址傳入到形參當(dāng)中,這樣就可以避免對(duì)整個(gè)數(shù)組的拷貝!
在我們目前認(rèn)識(shí)中,如果對(duì)象沒有被引用,則會(huì)自動(dòng)回收,所以不用考慮內(nèi)存泄漏的問題
2.4 數(shù)組作為函數(shù)返回值
假設(shè)這里我們有一個(gè)題,需要實(shí)現(xiàn)一個(gè)方法,這個(gè)方法需要獲取斐波那契數(shù)列的前 n 項(xiàng),需要返回一個(gè)數(shù)組回來,本質(zhì)其實(shí)就是返回?cái)?shù)組的地址,如何實(shí)現(xiàn)呢?
public static int[] fib(int n) { if (n <= 0) { return null; } int[] array = new int[n]; array[0] = array[1] = 1; for (int i = 2; i < n; i++) { array[i] = array[i - 1] + array[i - 2]; } return array; } public static void main(String[] args) { int[] array = fib(8); System.out.println(Arrays.toString(array)); }
當(dāng)然這個(gè)方法如果 n 為1 就會(huì)越界,這個(gè)下來可以自己優(yōu)化下,來到這里我們就來介紹下Arrays這個(gè)工具類里面的一些方法了: 像一些將數(shù)組轉(zhuǎn)換成字符串,數(shù)組二分查找,數(shù)組排序等等,可以下來查閱下幫助手冊(cè),這里我就不細(xì)說了,交給大家自己去擴(kuò)展了,如果以后用到,我會(huì)進(jìn)行說明。
3、二維數(shù)組
3.1 二維數(shù)組的概念和內(nèi)存布局
這里我們一定要有一個(gè)概念,二維數(shù)組是一個(gè)特殊的一維數(shù)組,如何理解呢?用文字來說,二維數(shù)組的每個(gè)元素是一維數(shù)組,也就是說,二維數(shù)組的每個(gè)元素里面放的是一維數(shù)組的地址!
相信聽完上面的話,大家可能不是很理解,那我們就定義一個(gè)二維數(shù)組,并畫圖:
public static void main(String[] args) { int[][] array = { {1,2,3}, {4,5,6} }; }
這里就很清晰明了了,圖中也能看到,二維數(shù)組本質(zhì)就是一個(gè)一維數(shù)組,只不過這個(gè)一維數(shù)組的每個(gè)元素存的是地址而已!
3.2 二維數(shù)組的定義和初始化
當(dāng)然,二維數(shù)組和一維數(shù)組一樣,同樣的三種定義方式:
int[][] array1 = { {1,2,3}, {4,5,6} }; //兩行三列的二維數(shù)組 int[][] array2 = new int[][]{ {1,2,3}, {4,5,6} }; int[][] array3 = new int[2][3];
如果只是單純的 int[][] array; 這樣數(shù)組里面沒有任何引用對(duì)象,并不能直接使用未引用對(duì)象的數(shù)組,如果不引用最好置null
3.3 二維數(shù)組遍歷
for 循環(huán)打印:
public class TestDemo { public static void main(String[] args) { int[][] array1 = { {1,2,3}, {4,5,6} }; //兩行三列的二維數(shù)組 for (int i = 0; i < array1.length; i++) { for (int j = 0; j < array1[i].length; j++) { System.out.print(array1[i][j] + " "); } System.out.println(); } System.out.println(); } }
這里第一個(gè) for 里面的長(zhǎng)度求的是 array1.length,因?yàn)樗旧砭褪且粋€(gè)特殊一維數(shù)組,這樣求出的就是它的行,第二個(gè)for array1[i].length,自然求得就是每一行一維數(shù)組長(zhǎng)度了
for-each 循環(huán)打?。?/strong>
public class TestDemo { public static void main(String[] args) { int[][] array1 = { {1,2,3}, {4,5,6} }; //兩行三列的二維數(shù)組 for (int[] arr : array1) { //二維數(shù)組的每個(gè)元素是一維數(shù)組,所以每個(gè)元素的類型是int[] for (int a : arr) { //arr是一維數(shù)組,每個(gè)數(shù)組的元素的int System.out.print(a + " "); } System.out.println(); } } }
Arrays 工具類有個(gè)方法也可以打印二維數(shù)組,是Arrays.deepToString,這個(gè)方法也是打印二維數(shù)組的但只能在一行顯示,感興趣的可以下來自己實(shí)驗(yàn)下。
3.4 不規(guī)則的二維數(shù)組
在Java中,二維數(shù)組的列可以省略,也就是把二維數(shù)組看成一個(gè)特殊的一維數(shù)組的每個(gè)元素指向的一維數(shù)組是可以元素個(gè)數(shù)不同的,如何理解這句話呢?我們還是一樣通過一小段并且以畫圖的形式給大家講解:
int[][] array = new int[2][]; array[0] = new int[3]; array[1] = new int[2];
當(dāng)然,他的遍歷方法是跟普通二維數(shù)組一樣的, 這里我就不多說,自己摸索,實(shí)在不懂可以問下博主。
總結(jié)
到此這篇關(guān)于關(guān)于Java SE數(shù)組的文章就介紹到這了,更多相關(guān)Java SE數(shù)組理解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java中生成任意之間數(shù)的隨機(jī)數(shù)詳解
這篇文章主要介紹了java中生成任意之間數(shù)的隨機(jī)數(shù)詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09Java編程實(shí)現(xiàn)驗(yàn)證哥德巴赫猜想
這篇文章主要介紹了Java編程實(shí)現(xiàn)驗(yàn)證哥德巴赫猜想,具有一定參考價(jià)值,需要的朋友可以了解下。2017-12-12對(duì)Jpa中Entity關(guān)系映射中mappedBy的全面理解
這篇文章主要介紹了對(duì)Jpa中Entity關(guān)系映射中mappedBy的全面理解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12解決mybatis一對(duì)多查詢r(jià)esultMap只返回了一條記錄問題
小編接到領(lǐng)導(dǎo)一個(gè)任務(wù)需求,需要用到使用resultMap相關(guān)知識(shí),在這小編記錄下這個(gè)問題的解決方法,對(duì)mybatis一對(duì)多查詢r(jià)esultMap項(xiàng)目知識(shí)感興趣的朋友一起看看吧2021-11-11SpringBoot中的@Configuration注解詳解
這篇文章主要介紹了SpringBoot中的@Configuration注解詳解,Spring Boot推薦使用JAVA配置來完全代替XML 配置,JAVA配置就是通過 @Configuration和 @Bean兩個(gè)注解實(shí)現(xiàn)的,需要的朋友可以參考下2023-08-08