Java语法糖1:可变长度参数以及foreach循环原理
語(yǔ)法糖?
接下來(lái)幾篇文章要開(kāi)啟一個(gè)Java語(yǔ)法糖系列,所以首先講講什么是語(yǔ)法糖。語(yǔ)法糖是一種幾乎每種語(yǔ)言或多或少都提供過(guò)的一些方便程序員開(kāi)發(fā)代碼的語(yǔ)法,它只是編譯器實(shí)現(xiàn)的一些小把戲罷了,編譯期間以特定的字節(jié)碼或者特定的方式對(duì)這些語(yǔ)法做一些處理,開(kāi)發(fā)者就可以直接方便地使用了。這些語(yǔ)法糖雖然不會(huì)提供實(shí)質(zhì)性的功能改進(jìn),但是它們或能提高性能、或能提升語(yǔ)法的嚴(yán)謹(jǐn)性、或能減少編碼出錯(cuò)的機(jī)會(huì)。Java提供給了用戶(hù)大量的語(yǔ)法糖,比如泛型、自動(dòng)裝箱、自動(dòng)拆箱、foreach循環(huán)、變長(zhǎng)參數(shù)、內(nèi)部類(lèi)、枚舉類(lè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ù)遍歷出來(lái)了,這說(shuō)明兩個(gè)問(wèn)題:
1、可以使用遍歷數(shù)組的方式去遍歷可變參數(shù)
2、可變參數(shù)是利用數(shù)組實(shí)現(xiàn)的
既然這樣,那我其實(shí)main函數(shù)也可以這么寫(xiě),完全可以:
String[] strs = {"000", "111", "222", "333"}; print(strs);那直接傳入一個(gè)數(shù)組不就好了?問(wèn)題是,數(shù)組是要指定長(zhǎng)度的,萬(wàn)一這次我想傳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è)月前,自己寫(xiě)了一個(gè)ArrayList,想用foreach循環(huán)遍歷一下看一下寫(xiě)的效果,結(jié)果報(bào)了空指針異常。本文就寫(xiě)寫(xiě)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é)果就不說(shuō)了,都知道。看一下Java是如何處理這個(gè)foreach循環(huán)的,javap反編譯一下:
F:\代碼\MyEclipse\TestArticle\bin\com\xrq\test21>javap -verbose TestMain.class反編譯出來(lái)的內(nèi)容很多,有類(lèi)信息、符號(hào)引用、字節(jié)碼信息,截取一段信息:
1 public static void main(java.lang.String[]); 2 flags: ACC_PUBLIC, ACC_STATIC 3 Code: 4 stack=2, locals=4, args_size=1 5 0: new #16 // class java/util/ArrayList 6 3: dup 7 4: invokespecial #18 // Method java/util/ArrayList."<in 8 it>":()V 9 7: astore_1 10 8: aload_1 11 9: ldc #19 // String 111 12 11: invokeinterface #21, 2 // InterfaceMethod java/util/List. 13 add:(Ljava/lang/Object;)Z 14 16: pop 15 17: aload_1 16 18: ldc #27 // String 222 17 20: invokeinterface #21, 2 // InterfaceMethod java/util/List. 18 add:(Ljava/lang/Object;)Z 19 25: pop 20 26: aload_1 21 27: invokeinterface #29, 1 // InterfaceMethod java/util/List. 22 iterator:()Ljava/util/Iterator;看不懂沒(méi)關(guān)系,new、dup、invokespecial這些本來(lái)就是字節(jié)碼指令表內(nèi)定義的指令,虛擬機(jī)會(huì)根據(jù)這些指令去執(zhí)行指定的C++代碼,完成每個(gè)指令的功能。關(guān)鍵看到21、22這兩行就可以了,看到了一個(gè)iterator,所以得出結(jié)論:在編譯的時(shí)候編譯器會(huì)自動(dòng)將對(duì)for這個(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的父類(lèi)AbstractList正確地實(shí)現(xiàn)了Iterable接口的iterator方法。之前我自己寫(xiě)的ArrayList用foreach循環(huán)直接報(bào)空指針異常是因?yàn)槲易约簩?xiě)的ArrayList并沒(méi)有實(shí)現(xiàn)Iterable接口
2、任何一個(gè)集合,無(wú)論是JDK提供的還是自己寫(xiě)的,只要想使用foreach循環(huán)遍歷,就必須正確地實(shí)現(xiàn)Iterable接口
實(shí)際上,這種做法就是23中設(shè)計(jì)模式中的迭代器模式。
?
數(shù)組呢?
上面的講完了,好理解,但是不知道大家有沒(méi)有疑問(wèn),至少我是有一個(gè)疑問(wèn)的:數(shù)組并沒(méi)有實(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)鍵的信息:
1 0: iconst_2 2 1: newarray int 3 3: dup 4 4: iconst_0 5 5: iconst_1 6 6: iastore 7 7: dup 8 8: iconst_1 9 9: iconst_2 10 10: iastore 11 11: astore_1 12 12: aload_1 13 13: dup 14 14: astore 5 15 16: arraylength 16 17: istore 4 17 19: iconst_0 18 20: istore_3 19 21: goto 39 20 24: aload 5 21 26: iload_3 22 27: iaload 23 28: istore_2 24 29: getstatic #16 // Field java/lang/System.out:Ljav 25 a/io/PrintStream; 26 32: iload_2 27 33: invokevirtual #22 // Method java/io/PrintStream.prin 28 tln:(I)V 29 36: iinc 3, 1 30 39: iload_3 31 40: iload 4 32 42: if_icmplt 24 33 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)引用。
?
總結(jié)
以上是生活随笔為你收集整理的Java语法糖1:可变长度参数以及foreach循环原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 神经网络学习(一)
- 下一篇: Node.js与Sails~Model和