數(shù)組中你可能不知道的一些事凡伊,數(shù)組和可變參數(shù)和重載方法的坑

本文總共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ù)組章節(jié)概括

二、數(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)存分析
內(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];

image
結(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
動(dòng)態(tài)創(chuàng)建
結(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類反編譯的代碼如下柬唯,你看懂了嗎?

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é)果:

測(cè)試類的反編譯工具
編寫(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è)方法蚂蕴。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市俯邓,隨后出現(xiàn)的幾起案子骡楼,更是在濱河造成了極大的恐慌,老刑警劉巖稽鞭,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件君编,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡川慌,警方通過(guò)查閱死者的電腦和手機(jī)吃嘿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)祠乃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人兑燥,你說(shuō)我怎么就攤上這事亮瓷。” “怎么了降瞳?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵嘱支,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我挣饥,道長(zhǎng)除师,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任扔枫,我火速辦了婚禮汛聚,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘短荐。我一直安慰自己倚舀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布忍宋。 她就那樣靜靜地躺著痕貌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪糠排。 梳的紋絲不亂的頭發(fā)上舵稠,一...
    開(kāi)封第一講書(shū)人閱讀 52,682評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音入宦,去河邊找鬼柱查。 笑死,一個(gè)胖子當(dāng)著我的面吹牛云石,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播研乒,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼汹忠,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了雹熬?” 一聲冷哼從身側(cè)響起宽菜,我...
    開(kāi)封第一講書(shū)人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎竿报,沒(méi)想到半個(gè)月后铅乡,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡烈菌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年阵幸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了花履。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡挚赊,死狀恐怖诡壁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情荠割,我是刑警寧澤妹卿,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站蔑鹦,受9級(jí)特大地震影響夺克,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嚎朽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一铺纽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧火鼻,春花似錦室囊、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至粗蔚,卻和暖如春尝偎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鹏控。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工致扯, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人当辐。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓抖僵,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親缘揪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子耍群,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361