溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

java可變長(zhǎng)度參數(shù)和foreach循環(huán)原理是什么?

發(fā)布時(shí)間:2020-07-14 09:52:43 來源:億速云 閱讀:127 作者:Leah 欄目:編程語言

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)碛嘘P(guān)java可變長(zhǎng)度參數(shù)和foreach循環(huán)原理是什么?,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

一、語法糖

語法糖是一種幾乎每種語言或多或少都提供過的一些方便程序員開發(fā)代碼的語法,它只是編譯器實(shí)現(xiàn)的一些小把戲罷了,編譯期間以特定的字節(jié)碼或者特定的方式對(duì)這些語法做一些處理,開發(fā)者就可以直接方便地使用了。這些語法糖雖然不會(huì)提供實(shí)質(zhì)性的功能改進(jìn),但是它們或能提高性能、或能提升語法的嚴(yán)謹(jǐn)性、或能減少編碼出錯(cuò)的機(jī)會(huì)。Java提供給了用戶大量的語法糖,比如泛型、自動(dòng)裝箱、自動(dòng)拆箱、foreach循環(huán)、變長(zhǎng)參數(shù)、內(nèi)部類、枚舉類、斷言(assert)等。

二、可變長(zhǎng)參數(shù)

先講可變長(zhǎng)度參數(shù),看一段代碼:

public static void main(String[] args)
{
    print("000", "111", "222", "333");
}
    
public static void print(String... strs)
{
    for (int i = 0; i < strs.length; i++)
    {
        System.out.println(strs[i]);
    }
}

print方法的參數(shù)的意思是表示傳入的String個(gè)數(shù)是不定的,看一下代碼的運(yùn)行結(jié)果:

000
111
222
333

我用數(shù)組遍歷的方式成功地將輸入的參數(shù)遍歷出來了,這說明兩個(gè)問題:

1、可以使用遍歷數(shù)組的方式去遍歷可變長(zhǎng)參數(shù)

2、可變參數(shù)是利用數(shù)組實(shí)現(xiàn)的

既然這樣,那我其實(shí)main函數(shù)也可以這么寫,完全可以:

String[] strs = {"000", "111", "222", "333"};
print(strs);

那直接傳入一個(gè)數(shù)組不就好了?問題是,數(shù)組是要指定長(zhǎng)度的,萬一這次我想傳2個(gè)String,下次我想傳3個(gè)String怎么辦呢?

最后,注意一點(diǎn),可變長(zhǎng)度參數(shù)必須作為方法參數(shù)列表中的的最后一個(gè)參數(shù)且方法參數(shù)列表中只能有一個(gè)可變長(zhǎng)度參數(shù)。

三、foreach循環(huán)原理

以前對(duì)foreach循環(huán)就是這么用著,觸動(dòng)我去研究foreach循環(huán)的原理的原因是大概兩個(gè)月前,自己寫了一個(gè)ArrayList,想用foreach循環(huán)遍歷一下看一下寫的效果,結(jié)果報(bào)了空指針異常。本文就寫寫foreach循環(huán)的原理,先看一下這么一段代碼:

public static void main(String[] args)
{
    List<String> list = new ArrayList<String>();
    list.add("111");
    list.add("222");
    
    for (String str : list)
    {
        System.out.println(str);
    }
}

用foreach循環(huán)去遍歷這個(gè)list,結(jié)果就不說了,都知道。看一下Java是如何處理這個(gè)foreach循環(huán)的,javap反編譯一下:

F:\代碼\MyEclipse\TestArticle\bin\com\xrq\test21>javap -verbose TestMain.class

反編譯出來的內(nèi)容很多,有類信息、符號(hào)引用、字節(jié)碼信息,截取一段信息:

public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=4, args_size=1
         0: new           #16                 // class java/util/ArrayList
         3: dup
         4: invokespecial #18                 // Method java/util/ArrayList."<in
it>":()V
         7: astore_1
         8: aload_1
         9: ldc           #19                 // String 111
        11: invokeinterface #21,  2           // InterfaceMethod java/util/List.
add:(Ljava/lang/Object;)Z
        16: pop
        17: aload_1
        18: ldc           #27                 // String 222
        20: invokeinterface #21,  2           // InterfaceMethod java/util/List.
add:(Ljava/lang/Object;)Z
        25: pop
        26: aload_1
        27: invokeinterface #29,  1           // InterfaceMethod java/util/List.
iterator:()Ljava/util/Iterator;

看不懂沒關(guān)系,new、dup、invokespecial這些本來就是字節(jié)碼指令表內(nèi)定義的指令,虛擬機(jī)會(huì)根據(jù)這些指令去執(zhí)行指定的C++代碼,完成每個(gè)指令的功能。關(guān)鍵看到21、22這兩行就可以了,看到了一個(gè)iterator,所以得出結(jié)論:在編譯的時(shí)候編譯器會(huì)自動(dòng)將對(duì)foreach這個(gè)關(guān)鍵字的使用轉(zhuǎn)化為對(duì)目標(biāo)的迭代器的使用,這就是foreach循環(huán)的原理。進(jìn)而,我們?cè)俚贸鰞蓚€(gè)結(jié)論:

1、ArrayList之所以能使用foreach循環(huán)遍歷,是因?yàn)锳rrayList所有的List都是Collection的子接口,而Collection是Iterable的子接口,ArrayList的父類AbstractList正確地實(shí)現(xiàn)了Iterable接口的iterator方法。之前我自己寫的ArrayList用foreach循環(huán)直接報(bào)空指針異常是因?yàn)槲易约簩懙腁rrayList并沒有實(shí)現(xiàn)Iterable接口

2、任何一個(gè)集合,無論是JDK提供的還是自己寫的,只要想使用foreach循環(huán)遍歷,就必須正確地實(shí)現(xiàn)Iterable接口實(shí)際上,這種做法就是23中設(shè)計(jì)模式中的迭代器模式。

數(shù)組呢?

上面的講完了,好理解,但是不知道大家有沒有疑問,至少我是有一個(gè)疑問的:數(shù)組并沒有實(shí)現(xiàn)Iterable接口啊,為什么數(shù)組也可以用foreach循環(huán)遍歷呢?先給一段代碼,再反編譯:

public static void main(String[] args)
{
    int[] ints = {1,2,3,4,5};
        
    for (int i : ints)
        System.out.println(i);
}

同樣反編譯一下,看一下關(guān)鍵的信息:

0: iconst_2
         1: newarray       int
         3: dup
         4: iconst_0
         5: iconst_1
         6: iastore
         7: dup
         8: iconst_1
         9: iconst_2
        10: iastore
        11: astore_1
        12: aload_1
        13: dup
        14: astore        5
        16: arraylength
        17: istore        4
        19: iconst_0
        20: istore_3
        21: goto          39
        24: aload         5
        26: iload_3
        27: iaload
        28: istore_2
        29: getstatic     #16                 // Field java/lang/System.out:Ljav
a/io/PrintStream;
        32: iload_2
        33: invokevirtual #22                 // Method java/io/PrintStream.prin
tln:(I)V
        36: iinc          3, 1
        39: iload_3
        40: iload         4
        42: if_icmplt     24
        45: return

這是完整的這段main函數(shù)對(duì)應(yīng)的45個(gè)字節(jié)碼指令,因?yàn)檫@涉及一些壓棧、出棧、推送等一些計(jì)算機(jī)原理性的內(nèi)容且對(duì)于這些字節(jié)碼指令的知識(shí)的理解需要一些C++的知識(shí),所以就不解釋了。簡(jiǎn)單對(duì)照字節(jié)碼指令表之后,我個(gè)人對(duì)于這45個(gè)字節(jié)碼的理解是Java將對(duì)于數(shù)組的foreach循環(huán)轉(zhuǎn)換為對(duì)于這個(gè)數(shù)組每一個(gè)的循環(huán)引用。

上述就是小編為大家分享的java可變長(zhǎng)度參數(shù)和foreach循環(huán)原理是什么?了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI