本文總共13700字,估計(jì)閱讀時(shí)間34分鐘窒舟。如果你已經(jīng)會(huì)了一部分內(nèi)容系忙,請(qǐng)查看文末數(shù)組常見(jiàn)問(wèn)題。
本文屬于老薛原創(chuàng)內(nèi)容惠豺,轉(zhuǎn)載請(qǐng)注明出處:http://www.reibang.com/p/0dfca97cc728
數(shù)組
一银还、本章內(nèi)容知識(shí)點(diǎn)概括
本文總共13700字风宁,估計(jì)閱讀時(shí)間34分鐘。如果你已經(jīng)會(huì)了一部分內(nèi)容蛹疯,請(qǐng)查看文末數(shù)組常見(jiàn)問(wèn)題戒财。
二、數(shù)組基礎(chǔ)知識(shí)
2-1:什么是數(shù)組
數(shù)組本質(zhì)就是用來(lái)存儲(chǔ)一組數(shù)的器皿捺弦。
2-1-1:數(shù)組的定義
在內(nèi)存中開(kāi)辟一塊連續(xù)的內(nèi)存區(qū)域饮寞,用來(lái)存儲(chǔ)相同數(shù)據(jù)類型的有序集合。
PS:這里有兩點(diǎn)我們需要注意我們?cè)诤罄m(xù)章節(jié)中會(huì)發(fā)現(xiàn)列吼,現(xiàn)在簡(jiǎn)單做個(gè)普及:
- 相同數(shù)據(jù)類型 :數(shù)組中存儲(chǔ)的數(shù)據(jù)是相同的數(shù)據(jù)類型數(shù)據(jù)幽崩,不能出現(xiàn)多種數(shù)據(jù)類型,但是引用類型(Object)除外寞钥。這里大家可以思考一下為什么這么說(shuō)?
- 有序 : 數(shù)組存儲(chǔ)元素在內(nèi)存層面上講是有序的慌申,因?yàn)槭沁B續(xù)的存儲(chǔ)空間;另一方面數(shù)組獲取值是通過(guò)索引去實(shí)現(xiàn)凑耻,索引也是有序的太示。
2-2: 數(shù)組的定義以及賦值
2-2-1:數(shù)組的定義
2-2-1-1:數(shù)組定義第一種方式:
public class ArraysTest01 {
public static void main(String[] args) {
//聲明一個(gè)數(shù)組
int[] arrs ;// 聲明了一個(gè)int類型的數(shù)組
}
}
PS小結(jié):定義格式 數(shù)據(jù)類型 [] 變量名
,例子 int [] arrs
;
2-2-1-2:數(shù)組定義第二種方式(不推薦使用):
public class ArraysTest01 {
public static void main(String[] args) {
//第二種聲明方式
String strs[];// 聲明一個(gè)String類型的數(shù)組
}
}
PS小結(jié): 定義格式 數(shù)據(jù)類型 變量名[]
,例子int arrs[]
2-2-1-3:考慮為什么第二種方式不推薦
數(shù)組本質(zhì)上也是一種數(shù)據(jù)類型香浩,所以第一種寫(xiě)法更能體現(xiàn)這種關(guān)系类缤。所以很多語(yǔ)言中對(duì)于數(shù)組的定義,第二種寫(xiě)法編譯都是錯(cuò)誤的邻吭,比如在C#中是無(wú)法使用第二種聲明方式的餐弱。
2-2-2:數(shù)組的初始化
當(dāng)聲明一個(gè)數(shù)組之后,我們需要使用時(shí)囱晴,本質(zhì)上和使用局部變量是一樣的膏蚓,我們無(wú)法將一個(gè)未初始化的數(shù)組變量直接使用,所以需要初始化動(dòng)作
2-2-2-1:靜態(tài)初始化
2-2-2-1-1:第一種方式
public class ArraysTest02 {
public static void main(String[] args) {
//1:靜態(tài)初始化:
// 會(huì)在存儲(chǔ)中開(kāi)辟存儲(chǔ)3個(gè)字符串對(duì)象的數(shù)組畸写,
// 并且這個(gè)三個(gè)元素的值也確定了分別是java驮瞧、htlm、css枯芬。
String[] strs = new String[]{"java","html","css"};
System.out.println("查看當(dāng)前數(shù)組的長(zhǎng)度:"+strs.length);
}
}
輸出內(nèi)容:
查看當(dāng)前數(shù)組的長(zhǎng)度是:3
strs.length用來(lái)顯示當(dāng)前數(shù)組的元素個(gè)數(shù)或者叫數(shù)組長(zhǎng)度
PS小結(jié):
類型 | 結(jié)構(gòu) | 例子 |
---|---|---|
結(jié)構(gòu) | type[] type_names = new type[]{e1,e2论笔,....} | String[] strs = new String[]{"java","html","css"} |
2-2-2-1-2:第二種方式
public class ArraysTest02 {
public static void main(String[] args) {
//1:靜態(tài)初始化第二種方式:
//會(huì)開(kāi)辟存儲(chǔ)三個(gè)字符串對(duì)象的字符串?dāng)?shù)組
String[] names = {"張三","李四","老薛"};
System.out.println("names數(shù)組的長(zhǎng)度:"+names.length);
}
}
輸出內(nèi)容:
names數(shù)組的長(zhǎng)度:3
PS小結(jié):
類型 | 結(jié)構(gòu) | 例子 |
---|---|---|
結(jié)構(gòu) | type[] type_names = {e1,e2,....} | String[] strs = {"java","html","css"} |
2-2-2-2:動(dòng)態(tài)初始化
public class ArraysTest02 {
public static void main(String[] args) {
//3:動(dòng)態(tài)初始化:
// 在內(nèi)存中開(kāi)辟一個(gè)存放5個(gè)數(shù)據(jù)的int類型的數(shù)組 將地址賦值給arrs變量存放
int[] arrs = new int[5];
//4:打印輸出數(shù)組
System.out.println(arrs);
}
}
輸出內(nèi)容:
[I@6bdf28bb
PS小結(jié):
打印內(nèi)容 | 含義 |
---|---|
[ | 以后遇到對(duì)象輸出時(shí)千所,"["開(kāi)頭狂魔,證明當(dāng)前對(duì)象是一個(gè)數(shù)組類型 |
I | 代表當(dāng)前數(shù)組的類型是int類型 |
@ | 自動(dòng)拼接 |
6bdf28bb | 當(dāng)前數(shù)組對(duì)象的hash值 |
2-2-3:常見(jiàn)錯(cuò)誤
- 聲明和初始化數(shù)組時(shí),忘記指定長(zhǎng)度
int[] arrs = new int[]; //編譯錯(cuò)誤
- 重新給數(shù)組賦值時(shí)淫痰,不能通過(guò){}指定最楷,語(yǔ)法不允許
int[] arrs = new int[4];
arrs = {1,20,23};//語(yǔ)法不允許
- 聲明數(shù)組時(shí),小心數(shù)組的長(zhǎng)度,出現(xiàn)越界異常
int[] arrs = new int[4];
System.out.println(arrs[5]); //java.lang.ArrayIndexOutOfBoundsException異常
2-3:數(shù)組常見(jiàn)屬性
2-3-1: 數(shù)組length屬性
數(shù)組在聲明賦值時(shí)籽孙,都需要指定當(dāng)前數(shù)組的長(zhǎng)度烈评,通過(guò)length屬性獲取當(dāng)前數(shù)組的長(zhǎng)度。
public class ArraysTest03 {
public static void main(String[] args) {
//1:聲明一個(gè)int類型的數(shù)組
int[] arrs = new int[5];//聲明了一個(gè)存放5個(gè)數(shù)據(jù)的int類型的數(shù)組 名字叫arrs
//2:查看數(shù)組中的元素
System.out.println("查看數(shù)組中的元素:"+arrs[3]);
//3:獲取當(dāng)前數(shù)組的長(zhǎng)度
System.out.println("獲取數(shù)組的長(zhǎng)度:"+arrs.length);
}
}
2-3-2:數(shù)組的索引屬性
數(shù)組中的元素是有序說(shuō)的有序稱是索引有序蚯撩。
數(shù)組中存儲(chǔ)的元素础倍,都存在一個(gè)下標(biāo),我們可以通過(guò)下標(biāo)去獲取胎挎、修改以及添加元素到數(shù)組中去。一定要注意數(shù)組的索引是從0開(kāi)始的忆家,從length-1結(jié)束犹菇。數(shù)組的索引是[0,length-1]
public class ArraysTest03 {
public static void main(String[] args) {
//1:聲明一個(gè)int類型的數(shù)組
int[] arrs = new int[5];
//2:查看數(shù)組中的元素
System.out.println("查看數(shù)組中的元素:"+arrs[3]);
//3:獲取當(dāng)前數(shù)組的長(zhǎng)度
System.out.println("獲取數(shù)組的長(zhǎng)度:"+arrs.length);
//4:通過(guò)索引獲取數(shù)組中第5個(gè)元素的值
System.out.println("查看數(shù)組中第5個(gè)元素的值:"+arrs[4]);
//5:通過(guò)索引改變對(duì)應(yīng)位置上的元素值
arrs[4] = 10;
System.out.println("索引是4的元素,也就是第5個(gè)元素的值是:"+arrs[4]);
}
}
2-3-3:內(nèi)存分析
2-3-3-1:代碼實(shí)例:
public class ArraysTest05 {
public static void main(String[] args) {
//1:聲明一個(gè)存放5個(gè)int類型元素的數(shù)組
int[] arrs = new int[5];
//2:指定數(shù)組索引是0的芽卿,也就是第一個(gè)元素的值是10
arrs[0] = 10;
//3:獲取索引是1的揭芍,也就是第2個(gè)元素的值
System.out.println("查看第2個(gè)元素的值是:"+arrs[1]);
}
}
2-3-3-2:內(nèi)存展示01
ps:第一步,創(chuàng)建int數(shù)組對(duì)象時(shí)卸例,內(nèi)存結(jié)構(gòu)
- 1>称杨、會(huì)在堆內(nèi)存當(dāng)中開(kāi)辟5個(gè)長(zhǎng)度的連續(xù)的存儲(chǔ)空間,用來(lái)存儲(chǔ)int類型的元素筷转。
- 2>姑原、由于當(dāng)前數(shù)組的類型是int類型,所以每個(gè)存儲(chǔ)空間的元素都是int的默認(rèn)值0填充呜舒。如果是String類型則默認(rèn)值為null锭汛。
- 3>、將堆內(nèi)存的地址付給arrs變量存儲(chǔ)
2-3-3-3:內(nèi)存展示02
ps:第二步袭蝗,將索引是1的元素的位置改為10唤殴,注意索引是從0開(kāi)始,length-1結(jié)束
在按照元素的索引獲取元素值時(shí)到腥,注意圖中的索引朵逝,我們都是安裝元素的索引對(duì)于數(shù)組中的元素進(jìn)行獲取和修改。
2-3-3-4:為什么說(shuō)數(shù)組是不可變的
2-3-3-4-1:測(cè)試用例
public class ArraysTest05 {
public static void main(String[] args) {
//1:聲明一個(gè)存放5個(gè)int類型元素的數(shù)組
int[] arrs = new int[5];
//2:指定數(shù)組索引是0的乡范,也就是第一個(gè)元素的值是10
arrs[0] = 10;
//3:獲取索引是1的配名,也就是第2個(gè)元素的值
System.out.println("查看第2個(gè)元素的值是:"+arrs[1]);
//4:重新賦值一個(gè)新的數(shù)組 這里是改變了數(shù)組長(zhǎng)度嗎?并沒(méi)有
arrs = new int[7];
//5:給arrs第6個(gè)元素賦值
arrs[5] = 33;
//6:輸出索引是5的元素及第6個(gè)元素
System.out.println(arrs[5]);
}
}
2-3-3-4-2:內(nèi)存分析
2-3-3-4-3:結(jié)論
結(jié)論:數(shù)組一旦聲明,其長(zhǎng)度是不可變得篓足,我們通常說(shuō)的改變長(zhǎng)度其實(shí)都是通過(guò)重新定義一個(gè)新的數(shù)組段誊,改變局部變量的引用而已。
2-4: 針對(duì)于數(shù)組的CRUD
2-4-1:數(shù)組的修改以及獲取元素
2-4-1-1:獲取元素(通過(guò)索引)
public class ArraysTest06 {
public static void main(String[] args) {
//1:聲明一個(gè)存放3個(gè)String字符串對(duì)象的數(shù)組
String[] strs = new String[3];
//2:獲取strs數(shù)組中的第一個(gè)元素
System.out.println("strs數(shù)組中的第一個(gè)元素是:"+strs[0]);
}
}
? PS:輸出內(nèi)容
strs數(shù)組中的第一個(gè)元素是:null
2-4-1-2:修改元素(通過(guò)索引)
public class ArraysTest06 {
public static void main(String[] args) {
//1:聲明一個(gè)存放3個(gè)String字符串對(duì)象的數(shù)組
String[] strs = new String[3];
//2:修改數(shù)組的第一個(gè)元素的值
strs[0] = "老薛好帥";
//3:獲取strs數(shù)組中的第一個(gè)元素
System.out.println("strs數(shù)組中的第一個(gè)元素是:"+strs[0]);
}
}
PS:輸出內(nèi)容
strs數(shù)組中的第一個(gè)元素是:老薛好帥
2-4-2:數(shù)組添加元素和刪除元素的細(xì)節(jié)
這里我們暫且不細(xì)述添加和刪除元素的細(xì)節(jié)栈拖,在后續(xù)章節(jié)再做介紹连舍,但是這里我們給一點(diǎn)思路,刪除和添加元素時(shí)需要查看原來(lái)的數(shù)組的大小,一般情況下我們都會(huì)返回一個(gè)新的數(shù)組索赏。
2-5:數(shù)組的遍歷方式
2-5-1:數(shù)組的迭代第一種(普通for循環(huán))
public class ArraysTest04 {
public static void main(String[] args) {
//1:聲明一個(gè)數(shù)組
int[] arrs = new int[6];
//2:通過(guò)索引一次給數(shù)組中的元素填充值
int value = 10;
for(int i = 0;i<arrs.length;i++){
arrs[i] = value+(i*2);
}
System.out.println("當(dāng)前數(shù)組的長(zhǎng)度是:"+arrs.length);
//3:遍歷迭代當(dāng)前數(shù)組 依次獲取數(shù)組中的值
for(int i = 0;i<arrs.length;i++){
System.out.println("索引是"+i+"的元素的是:"+arrs[i]);
}
}
}
PS:通過(guò)普通for循環(huán)盼玄,通過(guò)索引填充以及迭代當(dāng)前數(shù)組的元素。
2-5-2:數(shù)組的迭代第二種(foreach)
public class ArraysTest04 {
public static void main(String[] args) {
//1:聲明一個(gè)數(shù)組
int[] arrs = new int[6];
//2:通過(guò)索引一次給數(shù)組中的元素填充值
int value = 10;
for(int i = 0;i<arrs.length;i++){
arrs[i] = value+(i*2);
}
//3:通過(guò)foreach迭代當(dāng)前數(shù)組
System.out.println("jdk1.5之后潜腻,提供了增強(qiáng)for循環(huán)埃儿,專門(mén)用來(lái)獲取集合中的元素");
for(int num:arrs){
System.out.println(num);
}
}
}
PS:通過(guò)foreach循環(huán)迭代數(shù)組中的元素。但是千萬(wàn)注意融涣,foreach循環(huán)對(duì)于和索引有關(guān)的操作無(wú)能為力童番,它只能做為迭代和遍歷使用。
三威鹿、多維數(shù)組
3-1:多維數(shù)組的聲明以及賦值
3-1-1:多維數(shù)組的聲明和初始化
這里我們通過(guò)二維數(shù)組類一探多維數(shù)組剃斧,其實(shí)本質(zhì)上而言,不管數(shù)組的維數(shù)忽你,它都是由一維數(shù)組慢慢拼湊起來(lái)的幼东。
3-1-1-1:二維數(shù)組的聲明第一種方式(靜態(tài)初始化)
這里我們將二維數(shù)組的聲明和初始化放在一起來(lái)演示
代碼演示1
public class Test01 {
public static void main(String[] args) {
//1:聲明多維數(shù)組
//聲明一個(gè)二維數(shù)組arrs 其實(shí)這里也可以理解為一個(gè)一維數(shù)組,只不過(guò)數(shù)組中包含的內(nèi)容是一個(gè)數(shù)組
int[][] arrs;
//2:數(shù)組的初始化:
//指定當(dāng)前數(shù)組的長(zhǎng)度科雳。聲明了一個(gè)一維數(shù)組根蟹,數(shù)組中包含三個(gè)元素 每個(gè)元素都是一個(gè)新的數(shù)組
arrs = new int[3][];
//3:初始化第二種方式:
//聲明一個(gè)數(shù)組,數(shù)組長(zhǎng)度是3糟秘,每個(gè)元素中存放的是一個(gè)長(zhǎng)度為4的新的數(shù)組 有點(diǎn)類似表格的展示
arrs = new int[3][4];
}
}
內(nèi)存分析1
第二行代碼初始化方式內(nèi)存分析简逮,及arrs = new int[3] [];
結(jié)論1
聲明二維數(shù)組時(shí),我們可以理解為聲明的是一個(gè)一維數(shù)組蚌堵,比如聲明的數(shù)組為new int[3] [],其實(shí)就是聲明了一個(gè)int[3]數(shù)組买决,這個(gè)數(shù)組的每個(gè)元素都是一個(gè)新的數(shù)組對(duì)象,而此時(shí)元素存儲(chǔ)的每個(gè)數(shù)組對(duì)象的長(zhǎng)度還沒(méi)有指定吼畏。所以在獲取元素時(shí)督赤,由于二維數(shù)組是int類型,所以訪問(wèn)二維數(shù)組的元素時(shí)獲取的值還是默認(rèn)值0泻蚊。這里之所以在數(shù)組的位置上寫(xiě)addr只是為了方便后期演示躲舌。
內(nèi)存分析2
第三行代碼初始化方式內(nèi)存分析,及arrs = new int[3] [4];
結(jié)論2
arrs = new int[3] [4],創(chuàng)建一個(gè)數(shù)組對(duì)象性雄,數(shù)組中的元素個(gè)數(shù)是3個(gè)没卸,每個(gè)元素的存儲(chǔ)是一個(gè)新的數(shù)組,新數(shù)組的長(zhǎng)度是4秒旋。其實(shí)也可以看成是一個(gè)三行4列的二維表格约计。而這個(gè)二維數(shù)組的索引就是通過(guò)索引依次去獲取以及修改等。
3-1-1-2:二維數(shù)組的聲明第一種方式(動(dòng)態(tài)初始化)
代碼演示1
public class Test01 {
public static void main(String[] args) {
//初始化的第三種方式:
//初始化一個(gè)數(shù)組迁筛,數(shù)組的長(zhǎng)度是3煤蚌,每個(gè)元素都是一個(gè)數(shù)組,數(shù)組索引是1的指向的新數(shù)組的長(zhǎng)度是1,以后以此類推
String[][] strs = new String[][]{{"你好","我好"},{"嘿嘿","呵呵"},{"碼歌","老薛"}};
}
}
內(nèi)存分析1
結(jié)論1
1:會(huì)在堆內(nèi)存中開(kāi)辟一個(gè)連續(xù)的存儲(chǔ)空間尉桩,存儲(chǔ)當(dāng)前的多維數(shù)組筒占。
2:數(shù)組的每個(gè)元素其實(shí)存儲(chǔ)的是一個(gè)新的一維數(shù)組。存儲(chǔ)的是一維數(shù)組的地址蜘犁。
3:根據(jù)聲明方式翰苫,新的一維數(shù)組中的元素個(gè)數(shù)是兩個(gè),所以會(huì)開(kāi)辟三個(gè)新的一維數(shù)組这橙,開(kāi)始給新的一維數(shù)組的每個(gè)元素賦值null奏窑。
4:通過(guò)靜態(tài)初始化的方式,新的一維數(shù)組的元素開(kāi)始正常賦值屈扎,比如:strs[0] 代表的是二維數(shù)組的第一個(gè)元素良哲,str[0][0]代表二維數(shù)組的第一個(gè)元素中存儲(chǔ)的一維數(shù)組的第一個(gè)元素,賦值你好助隧,str[0][1]代表二維數(shù)組的第一個(gè)元素中存儲(chǔ)的一維數(shù)組的第二個(gè)元素,賦值我好滑沧。然后依次類推
代碼演示2:(第二種靜態(tài)初始化)
public class Test01 {
public static void main(String[] args) {
//初始化的第三種方式:
//初始化一個(gè)數(shù)組并村,數(shù)組的長(zhǎng)度是3,每個(gè)元素都是一個(gè)數(shù)組滓技,數(shù)組索引是1的指向的新數(shù)組的長(zhǎng)度是1哩牍,以后以此類推
String[][] strs = {{"你好","我好"},{"嘿嘿","呵呵"},{"碼歌","老薛"}};
}
}
內(nèi)存分析2:
這里的內(nèi)存分析和上圖是一致的。
3-2:多維數(shù)組的CRUD以及遍歷
3-2-1:多維數(shù)組的填充值
3-2-1-1:簡(jiǎn)單的填充值
3-2-1-1-1:測(cè)試用例
public class Test02 {
public static void main(String[] args) {
//1:聲明一個(gè)二維數(shù)組令漂,存放3個(gè)一維數(shù)組
String[][] strs = new String[3][];
//2:獲取二維數(shù)組中的第一元素
System.out.println("獲取二維數(shù)組中的第一個(gè)元素:"+strs[0]);
//3:給二維數(shù)組中的每個(gè)元素指定意味數(shù)組的長(zhǎng)度
//3-1:指定二維數(shù)組的第一個(gè)元素是一個(gè)長(zhǎng)度為2的數(shù)組
strs[0] = new String[2];
//3-2:指定二維數(shù)組的第二個(gè)元素是一個(gè)長(zhǎng)度為2的數(shù)組
strs[1] = new String[2];
//3-3:指定二維數(shù)組的第三個(gè)元素是一個(gè)長(zhǎng)度為2的數(shù)組
strs[2] = new String[2];
//4:查看二維數(shù)組中的第一個(gè)元素膝昆,該元素的第一個(gè)位置上的元素值
System.out.println("查看二維數(shù)組中的第一個(gè)元素?cái)?shù)組中的第一個(gè)元素:"+strs[0][0]);
3-2-1-1-2:打印結(jié)果
獲取二維數(shù)組中的第一個(gè)元素:null
查看二維數(shù)組中的第一個(gè)元素?cái)?shù)組中的第一個(gè)元素:null
3-2-1-1-3:結(jié)論
- 聲明的二維數(shù)組本質(zhì)上就是一個(gè)一維數(shù)組中的每個(gè)元素存儲(chǔ)的還是一個(gè)數(shù)組
- 在第二步獲取二維數(shù)組的第一個(gè)元素,由于創(chuàng)建方式的問(wèn)題叠必,其實(shí)本質(zhì)上而言荚孵,二維數(shù)組的每個(gè)元素還不是一個(gè)一維數(shù)組的地址,而只是int的默認(rèn)值0,這個(gè)千萬(wàn)要注意
- 在第三步通過(guò)new 數(shù)組的方式給二維數(shù)組的每個(gè)元素填充值
- 通過(guò)strs[索引][索引]去獲取或者填充值
3-2-1-2:常見(jiàn)的錯(cuò)誤
很多人在這里犯錯(cuò)纬朝,覺(jué)得通過(guò)上述方式可以聲明的就是二維數(shù)組收叶,通過(guò) 索引訪問(wèn)二維數(shù)組中的一個(gè)元素:通過(guò)strs[0][0],那么結(jié)果是什么呢?
System.out.println("獲取二維數(shù)組中的第一個(gè)數(shù)組的第一個(gè)元素:"+strs[0][0]);
報(bào)錯(cuò):Exception in thread "main"java.lang.NullPointerException at com.mage.arrays.multi.Test02.main(Test02.java:16) 因?yàn)樵诼暶鞫S數(shù)組的時(shí)候共苛,只指定了二維數(shù)組中的存儲(chǔ)元素是3個(gè)判没,但是這三個(gè)元素中存儲(chǔ)的一維數(shù)組并沒(méi)有指定長(zhǎng)度,也就意味著隅茎,這里訪問(wèn)第一個(gè)元素時(shí)澄峰,第一個(gè)元素是0,并沒(méi)有可以通過(guò)索引訪問(wèn)的內(nèi)容辟犀,所以報(bào)錯(cuò)俏竞。所以訪問(wèn)時(shí),只能訪問(wèn)二維數(shù)組的第一個(gè)元素
3-2-1-3:通過(guò)循環(huán)填充值
3-2-1-3-1:測(cè)試用例
public class Test03 {
public static void main(String[] args) {
//1:聲明二維數(shù)組,且通過(guò)循環(huán)填充值
//聲明的二維數(shù)組胞此,包含三個(gè)元素臣咖,每個(gè)元素中存儲(chǔ)一個(gè)包含了4個(gè)元素的一維數(shù)組
int[][] arrs = new int[3][4];
//2:通過(guò)循環(huán)填充值
//循環(huán)二維數(shù)組的長(zhǎng)度
for(int i = 0;i<arrs.length;i++){
//循環(huán)二維數(shù)組的每個(gè)元素中的一維數(shù)組的長(zhǎng)度
for(int j = 0;j<arrs[i].length;j++){
////往指定的位置上填充值
arrs[i][j] = (int)(Math.random()*40);
}
}
//3:查看二維數(shù)組中的元素值
System.out.println("查看二維數(shù)組中第2個(gè)元素?cái)?shù)組中的第1個(gè)位置上的值是:"+arrs[1][0]);
}
}
3-2-1-3-2:打印結(jié)果
打印結(jié)果:查看二維數(shù)組中第2個(gè)元素?cái)?shù)組中的第1個(gè)位置上的值是:7
3-2-1-3-3:結(jié)論
其實(shí)二維數(shù)組我們可以看做一個(gè)二維表格,上述通過(guò)循環(huán)填充值的代碼漱牵,其實(shí)就是一個(gè)表格如下圖:
3-2-2:多維數(shù)組修改夺蛇、查看元素
我們通過(guò)索引去查看以及修改元素
測(cè)試代碼:
public class Test02 {
public static void main(String[] args) {
//1:聲明一個(gè)二維數(shù)組,存放3個(gè)一維數(shù)組 每個(gè)數(shù)組長(zhǎng)度為4
String[][] strs = new String[3][4];
//2:通過(guò)索引給二維數(shù)組的第一個(gè)元素?cái)?shù)組中的第二個(gè)元素填充值
strs[0][1] = "嘿嘿";
System.out.println("查看二維數(shù)組中的第一個(gè)元素位置上的數(shù)組的第二位位置上的值是:"+strs[0][1]);
}
}
3-2-3:多維數(shù)組的迭代
通過(guò)普通for循環(huán)和foreach循環(huán)依次迭代二維數(shù)組
3-2-3-1:測(cè)試用例
public class Test04 {
public static void main(String[] args) {
//1:聲明二維數(shù)組酣胀,且通過(guò)循環(huán)填充值
//聲明的二維數(shù)組刁赦,包含三個(gè)元素,每個(gè)元素中存儲(chǔ)一個(gè)包含了4個(gè)元素的一維數(shù)組
int[][] arrs = new int[4][5];
//2:通過(guò)循環(huán)填充值
for(int i = 0;i<arrs.length;i++){
for (int j = 0;j<arrs[i].length;j++){
arrs[i][j] = (int)(Math.random()*60);
}
}
//3:查看二維數(shù)組中的元素值
System.out.println("=======通過(guò)for循環(huán)迭代=======");
for(int i = 0;i<arrs.length;i++){
System.out.print((i+1)+"行\(zhòng)t");
for (int j = 0;j<arrs[i].length;j++){
System.out.print(arrs[i][j]+"\t");
}
System.out.println();
}
//4:查看二維數(shù)組中的元素值
System.out.println("=======通過(guò)foreach循環(huán)迭代=======");
for(int[] arr:arrs){
for (int value:arr){
System.out.print(value+"\t");
}
System.out.println();
}
}
}
3-2-3-2:打印結(jié)果
=======通過(guò)for循環(huán)迭代=======
1行 40 6 16 12 29
2行 12 50 22 48 16
3行 21 54 5 44 20
4行 6 34 28 3 16
=======通過(guò)foreach循環(huán)迭代=======
40 6 16 12 29
12 50 22 48 16
21 54 5 44 20
6 34 28 3 16
四闻镶、數(shù)組和可變參數(shù)作為形式參數(shù)的區(qū)別
4-1:為什么需要可變參數(shù)
4-1-1:可變參數(shù)的演變
需求:計(jì)算兩個(gè)數(shù)相加 計(jì)算三個(gè)數(shù)相加 計(jì)算四個(gè)數(shù)等等甚脉,此時(shí)我們需要定義多個(gè)重載方法完成功能,在jdk5之前铆农,我們可以通過(guò)數(shù)組完成該功能
4-1-1-1:測(cè)試用例
public class Test01 {
public static void main(String[] args) {
//1:定義調(diào)用方法實(shí)參
int num1 = 10;
int num2 = 20;
int num3 = 30;
//2:將多個(gè)值通過(guò)數(shù)組包裝
int[] arrs = new int[]{num1,num2,num3};
//3:調(diào)用相加的方法完成功能
add(arrs);
}
//定義方法完成該功能
public static void add(int[] arrs){
int totle = 0;
for(int num:arrs){
totle += num;
}
System.out.println("多個(gè)值相加結(jié)果是:"+totle);
}
}
打印結(jié)果:多個(gè)值相加結(jié)果是:60
4-1-1-2:?jiǎn)栴}
每次調(diào)用時(shí)牺氨,都需要對(duì)于實(shí)際參數(shù)進(jìn)行包裝,不夠簡(jiǎn)單墩剖。這種做法可以有效的達(dá)到“讓方法可以接受個(gè)數(shù)可變的參數(shù)”的目的猴凹,只是調(diào)用時(shí)的形式不夠簡(jiǎn)單
4-1-2:可變參數(shù)的定義
4-1-2-1:測(cè)試用例
定義可變參數(shù)方法完成功能
public class Test02 {
public static void main(String[] args) {
/**
* jdk5 之后支持可變參數(shù) 使得調(diào)用變得更加簡(jiǎn)單
*/
//1:定義調(diào)用方法實(shí)參
int num1 = 10;
int num2 = 20;
int num3 = 30;
//2:調(diào)用add方法
add(num1,num2,num3);
}
//定義可變參數(shù)的方法
public static void add(int... arrs){
int totle = 0;
for(int num:arrs){
totle += num;
}
System.out.println("多個(gè)值相加結(jié)果是:"+totle);
}
}
打印結(jié)果:多個(gè)值相加結(jié)果是:60
4-1-2-2:結(jié)論
調(diào)用變得更加簡(jiǎn)單一點(diǎn),而且實(shí)際方法中使用時(shí)也是通過(guò)數(shù)組的方式解析可變參數(shù)的值岭皂。
4-1-2:可變參數(shù)的使用規(guī)則
可變的使用郊霎,其實(shí)本質(zhì)上和使用數(shù)組是一致的。數(shù)組如何操作爷绘,可變參數(shù)的使用也如何操作即可书劝。
4-1-2-1:可變參數(shù)的定義規(guī)則
定義可變參數(shù)是,就是形參列表中通過(guò)<span style="color:red">type...type_names</span> 這樣的形式去定義土至。
4-1-2-2:可變參數(shù)定義時(shí)一些問(wèn)題
4-1-2-2-1:定義可變參數(shù)的方法不能包含多個(gè)可變參數(shù)
測(cè)試用例:
public static void function(String... strs,int ... arrs){}
問(wèn)題:
編譯時(shí)報(bào)錯(cuò)购对,Vararg parameter must be the last in the list。要確北凶眩可變參數(shù)在參數(shù)列表的最后一個(gè)位置洞斯。因?yàn)榭勺儏?shù)無(wú)法確保傳入的實(shí)參到底是多少個(gè),所以本質(zhì)上Java的方法調(diào)用還是要確保實(shí)參和形參的個(gè)數(shù)坑赡、順序烙如、類型要匹配到,不然無(wú)法調(diào)用
4-1-2-2-2:定義可變參數(shù)的方法的可變參數(shù)要在最后定義
測(cè)試用例:
public static void function(String... strs,int num){}
問(wèn)題:
這的問(wèn)題和上述問(wèn)題是一致的毅否,還是有序無(wú)法確定int類型的實(shí)際參數(shù)在調(diào)用時(shí)是具體是第幾個(gè)亚铁。
4-1-3:可變參數(shù)的優(yōu)勢(shì)
- 調(diào)用形式變得更加簡(jiǎn)單
- 調(diào)用者可以根據(jù)自己的需要傳入合適的參數(shù)即可
- 避免通過(guò)數(shù)組調(diào)用時(shí)需要構(gòu)造數(shù)組的過(guò)程
4-2:可變參數(shù)的一些坑
<h4 id="4.2.1">4-2-1:重載方法調(diào)用時(shí),可變參數(shù)的調(diào)用順序</h4>
4-2-1-1:測(cè)試用例:
public class Test03 {
public static void main(String[] args) {
short s = 20;
fun(10,s);
}
public static void fun(int num1,int num2){
System.out.println("我是兩個(gè)個(gè)參數(shù)的方法 int int");
}
public static void fun(int num1,long num2){
System.out.println("我是兩個(gè)個(gè)參數(shù)的方法 int long");
}
public static void fun(int ... num){
System.out.println("我是個(gè)可變參數(shù)的方法");
}
}
4-2-1-2:打印結(jié)果
我是兩個(gè)個(gè)參數(shù)的方法 int int
4-2-1-3:結(jié)論
- 方法調(diào)用時(shí)首先會(huì)進(jìn)行精確匹配,是參合形參完全匹配的
- 如果不存在這樣的方法螟加,則會(huì)采用最優(yōu)最近方式去匹配徘溢,上述例子因?yàn)橹剌d方法包含了兩個(gè)參數(shù)的方法吞琐,分別是(int,int)和(int,long)這里會(huì)調(diào)用離的最近的(int,int),這里的近可以理解為short 和 int的距離要近于short到long
- 可變參數(shù)的方法然爆,如果上述的兩個(gè)方法都不存在站粟,則會(huì)調(diào)用到,也就意味著可變參數(shù)的方法調(diào)用在最后才會(huì)被調(diào)用到曾雕。因?yàn)樵谡{(diào)用可變參數(shù)的時(shí)候奴烙,編譯器需要將實(shí)參編譯為對(duì)應(yīng)的數(shù)組,在進(jìn)行調(diào)用剖张。
4-2-2:數(shù)組和可變參數(shù)同時(shí)存在重寫(xiě)方法
4-2-2-1:重寫(xiě)方法的要求
- 一定要發(fā)生繼承關(guān)系
- 方法的修飾符子類的要大于或者等于父類的修飾符
- 方法的返回值子類的小于或者等于父類的返回值類型
- 方法的名稱和子類和父類必須保持一致
- 方法的參數(shù)簽名子類的要和父類的一致(包含個(gè)數(shù)切诀、順序、類型)
4-2-2-2:編寫(xiě)測(cè)試用例
需求:
提供商品打折功能搔弄,父類定義了打折的方法幅虑,子類根據(jù)需要重寫(xiě)父類的方法。不過(guò)這里父類的方法形參通過(guò)可變參數(shù)定義顾犹,子類重寫(xiě)的方法通過(guò)數(shù)組定義倒庵。
編寫(xiě)測(cè)試用例
class F{
void fun(int price,int ... discount){
System.out.println("F.fun");
}
}
class S extends F{
@Override
void fun(int price, int[] discount) {
System.out.println("S.fun");
}
}
問(wèn)題
注意這里并不會(huì)出現(xiàn)報(bào)錯(cuò),很多人好奇的原因是由于這里子類重寫(xiě)的方法的參數(shù)列表和父類的不一樣呀炫刷,為什么@Override難道不會(huì)報(bào)錯(cuò)嗎哄芜?這里注意確實(shí)不會(huì)報(bào)錯(cuò),我們通過(guò)反編譯工具可以看到F類反編譯的代碼如下柬唯,你看懂了嗎?
PS:本質(zhì)上最后編譯的.class文件中我們發(fā)現(xiàn)可變參數(shù)會(huì)變成一個(gè)與之對(duì)應(yīng)的數(shù)組圃庭。
4-2-2-3:?jiǎn)栴}
編寫(xiě)測(cè)試用例1:
public class Test04 {
public static void main(String[] args) {
F f = new S();
f.fun(10,10);
S s = new S()锄奢;
}
}
測(cè)試用例輸出結(jié)果
S.fun
結(jié)果分析
這里使用到了多態(tài),在真正運(yùn)行時(shí)剧腻,會(huì)執(zhí)行子類中的fun方法拘央,但是參數(shù)列表是根據(jù)父類確定的。而此時(shí)父類中的方法是可變參數(shù)书在,這時(shí)會(huì)把傳入的10編譯器會(huì)猜測(cè)為數(shù)組灰伟。因?yàn)榭勺儏?shù)可以接受多個(gè)值,所以根據(jù)傳入的參數(shù)儒旬,其實(shí)這里會(huì)對(duì)于輸入的10進(jìn)行包裝栏账,將其封裝為一個(gè)int數(shù)組,在進(jìn)行方法調(diào)用栈源。請(qǐng)看下圖反編譯之后的結(jié)果:
編寫(xiě)測(cè)試用例2:
public class Test04 {
public static void main(String[] args) {
F f = new S();
f.fun(10,10);
S s = new S()挡爵;
s.fun(10,10);
}
}
測(cè)試用例輸出結(jié)果
編譯報(bào)錯(cuò),Wrong 2nd argument type. Found: 'int', required: 'int[]'
結(jié)果分析
這里調(diào)用時(shí)甚垦,由于直接指定了子類調(diào)用該方法茶鹃,但是注意子類中的方法的參數(shù)列表是個(gè)數(shù)組涣雕,本身也是一種數(shù)據(jù)類型,編譯器無(wú)法將一個(gè)10直接轉(zhuǎn)為一個(gè)數(shù)組類型闭翩。本身Java的方法調(diào)用就嚴(yán)格要求類型匹配挣郭,所以這里就報(bào)錯(cuò),類型不匹配疗韵。
<span style="color:green">重點(diǎn):所以以后在去重寫(xiě)可變參數(shù)的方法時(shí)兑障,一定要慎重,盡量不要這么干伶棒。</span>
4-2-3:重載方法中定義可變參數(shù)問(wèn)題
這個(gè)問(wèn)題在<span style="color:green">[4-2-1]</span>中已經(jīng)提及過(guò)旺垒,就是當(dāng)出現(xiàn)可變參數(shù)方法的時(shí)候,千萬(wàn)小心肤无,因?yàn)楹笃诘木S護(hù)時(shí)先蒋,一不小心就會(huì)調(diào)入坑中。
4-2-4:可變參數(shù)+null值問(wèn)題的重載方法
這是原騰訊的一道筆試題宛渐,通過(guò)閱讀請(qǐng)找出問(wèn)題原因以及解決方案?
4-2-4-1:一下測(cè)試用例會(huì)不會(huì)出問(wèn)題?原因是什么?
public class Test05 {
public static void main(String[] args) {
//調(diào)用該方法會(huì)出現(xiàn)什么問(wèn)題
fun("",null);
}
public static void fun(String str,String ... strs){
System.out.println("str = [" + str + "], strs = [" + strs + "]");
}
public static void fun(String str,Integer ... ins){
System.out.println("str = [" + str + "], ins = [" + ins + "]");
}
}
4-2-4-2:原因描述
這里出現(xiàn)問(wèn)題的原因是由于null值是可以轉(zhuǎn)換為任意引用類型竞漾。導(dǎo)致這里方法調(diào)用就會(huì)出現(xiàn)二義性,編譯器不知道要調(diào)用那個(gè)方法窥翩。所以在調(diào)用時(shí)需要手動(dòng)繞開(kāi)业岁,比如想要調(diào)用String時(shí),需要編寫(xiě) String[] str = null 寇蚊。這樣程序編譯時(shí)就知道調(diào)用的是fun(String,String...)
4-2-4-3:增加難度 一下代碼會(huì)出問(wèn)題嗎?
public class Test06 {
public static void main(String[] args) {
invoke(null,1);//會(huì)調(diào)用哪個(gè)方法呢笔时?
}
static void invoke(Object obj,Object ... args ){
System.out.println("obj = [" + obj + "], args = [" + args + "]");
}
static void invoke(String str,Object obj,Object ... args){
System.out.println("str = [" + str + "], obj = [" + obj + "], args = [" + args + "]");
}
}
這里會(huì)選擇調(diào)用invoke(String,...),注意重載方法調(diào)用時(shí)由于null既可以作為Object類型,也可以作為String類型仗岸。那么這里遵守的規(guī)則就是查看繼承關(guān)系允耿,由于String是繼承Object,所以會(huì)選擇invoke(String,...)扒怖。
4-2-4-4:好奇害死貓
public class Test07 {
public static void main(String[] args) {
invoke(null,1);//會(huì)調(diào)用哪個(gè)方法呢较锡?
}
static void invoke(Integer obj,Object ... args ){
System.out.println("obj = [" + obj + "], args = [" + args + "]");
}
static void invoke(String str,Object obj,Object ... args){
System.out.println("str = [" + str + "], obj = [" + obj + "], args = [" + args + "]");
}
}
這里會(huì)就會(huì)報(bào)錯(cuò),存在二義性盗痒,導(dǎo)致JVM也不曉得要調(diào)用哪個(gè)方法蚂蕴。