Java語法糖之foreach
阿新 • • 發佈:2018-12-12
JAVA集合foreach
for-each其實只是java提供的語法糖。語法糖是程式語言提供的一些便於程式設計師書寫程式碼的語法,是編譯器提供給程式設計師的糖衣,編譯時會對這些語法特殊處理。語法糖雖然不會帶來實質性的改進,但是在提高程式碼可讀性,提高語法嚴謹性,減少編碼錯誤機會上確實做出了很大貢獻; Java要求集合必須實現Iterable介面,才能使用for-each語法糖遍歷該集合的例項; 先看個簡單的例子:
public static void main(String[] args) { List<Integer> list = Arrays.asList(12, 30, 88); for (Integer num : list) { System.err.println(num); } }
使用javap -v 反編譯
Classfile /Users/Robert/gitlab/learn/target/classes/com/quancheng/SockerServer.class Last modified 2018-9-29; size 1126 bytes MD5 checksum d6c68f72bdc08b156eda0315c1bdb0c9 Compiled from "SockerServer.java" public class com.quancheng.SockerServer minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #11.#34 // java/lang/Object."<init>":()V #2 = Class #35 // java/lang/Integer #3 = Methodref #2.#36 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer; #4 = Methodref #37.#38 // java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List; #5 = InterfaceMethodref #39.#40 // java/util/List.iterator:()Ljava/util/Iterator; #6 = InterfaceMethodref #41.#42 // java/util/Iterator.hasNext:()Z #7 = InterfaceMethodref #41.#43 // java/util/Iterator.next:()Ljava/lang/Object; #8 = Fieldref #44.#45 // java/lang/System.err:Ljava/io/PrintStream; #9 = Methodref #46.#47 // java/io/PrintStream.println:(Ljava/lang/Object;)V #10 = Class #48 // com/quancheng/SockerServer #11 = Class #49 // java/lang/Object #12 = Utf8 <init> #13 = Utf8 ()V #14 = Utf8 Code #15 = Utf8 LineNumberTable #16 = Utf8 LocalVariableTable #17 = Utf8 this #18 = Utf8 Lcom/quancheng/SockerServer; #19 = Utf8 main #20 = Utf8 ([Ljava/lang/String;)V #21 = Utf8 num #22 = Utf8 Ljava/lang/Integer; #23 = Utf8 args #24 = Utf8 [Ljava/lang/String; #25 = Utf8 list #26 = Utf8 Ljava/util/List; #27 = Utf8 LocalVariableTypeTable #28 = Utf8 Ljava/util/List<Ljava/lang/Integer;>; #29 = Utf8 StackMapTable #30 = Class #50 // java/util/List #31 = Class #51 // java/util/Iterator #32 = Utf8 SourceFile #33 = Utf8 SockerServer.java #34 = NameAndType #12:#13 // "<init>":()V #35 = Utf8 java/lang/Integer #36 = NameAndType #52:#53 // valueOf:(I)Ljava/lang/Integer; #37 = Class #54 // java/util/Arrays #38 = NameAndType #55:#56 // asList:([Ljava/lang/Object;)Ljava/util/List; #39 = Class #50 // java/util/List #40 = NameAndType #57:#58 // iterator:()Ljava/util/Iterator; #41 = Class #51 // java/util/Iterator #42 = NameAndType #59:#60 // hasNext:()Z #43 = NameAndType #61:#62 // next:()Ljava/lang/Object; #44 = Class #63 // java/lang/System #45 = NameAndType #64:#65 // err:Ljava/io/PrintStream; #46 = Class #66 // java/io/PrintStream #47 = NameAndType #67:#68 // println:(Ljava/lang/Object;)V #48 = Utf8 com/quancheng/SockerServer #49 = Utf8 java/lang/Object #50 = Utf8 java/util/List #51 = Utf8 java/util/Iterator #52 = Utf8 valueOf #53 = Utf8 (I)Ljava/lang/Integer; #54 = Utf8 java/util/Arrays #55 = Utf8 asList #56 = Utf8 ([Ljava/lang/Object;)Ljava/util/List; #57 = Utf8 iterator #58 = Utf8 ()Ljava/util/Iterator; #59 = Utf8 hasNext #60 = Utf8 ()Z #61 = Utf8 next #62 = Utf8 ()Ljava/lang/Object; #63 = Utf8 java/lang/System #64 = Utf8 err #65 = Utf8 Ljava/io/PrintStream; #66 = Utf8 java/io/PrintStream #67 = Utf8 println #68 = Utf8 (Ljava/lang/Object;)V { public com.quancheng.SockerServer(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 11: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/quancheng/SockerServer; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=4, locals=4, args_size=1 0: iconst_3 1: anewarray #2 // class java/lang/Integer 4: dup 5: iconst_0 6: bipush 12 8: invokestatic #3 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 11: aastore 12: dup 13: iconst_1 14: bipush 30 16: invokestatic #3 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 19: aastore 20: dup 21: iconst_2 22: bipush 88 24: invokestatic #3 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 27: aastore 28: invokestatic #4 // Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List; 31: astore_1 32: aload_1 33: invokeinterface #5, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator; 38: astore_2 39: aload_2 40: invokeinterface #6, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z 45: ifeq 68 48: aload_2 49: invokeinterface #7, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object; 54: checkcast #2 // class java/lang/Integer 57: astore_3 58: getstatic #8 // Field java/lang/System.err:Ljava/io/PrintStream; 61: aload_3 62: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 65: goto 39 68: return LineNumberTable: line 13: 0 line 14: 32 line 15: 58 line 16: 65 line 17: 68 LocalVariableTable: Start Length Slot Name Signature 58 7 3 num Ljava/lang/Integer; 0 69 0 args [Ljava/lang/String; 32 37 1 list Ljava/util/List; LocalVariableTypeTable: Start Length Slot Name Signature 32 37 1 list Ljava/util/List<Ljava/lang/Integer;>; StackMapTable: number_of_entries = 2 frame_type = 253 /* append */ offset_delta = 39 locals = [ class java/util/List, class java/util/Iterator ] frame_type = 250 /* chop */ offset_delta = 28 }
注意看這行JVM指令:
33: invokeinterface #5, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
從上面的位元組碼指令可以看出foreach語法實際呼叫的還是List.iterator()方法然後使用迭代器進行迭代,這就是集合foreach的語法糖;
for (Integer num : list) {
System.err.println(num);
}
由Jvm編譯期處理後等效於:
for (I #i = Expression.iterator(); #i.hasNext(); ) { {VariableModifier} TargetType Identifier = (TargetType) #i.next(); Statement }
java陣列foreach
陣列的實現機制跟List完全不一樣,因為陣列並沒有實現Iterator介面,並沒有採用Iterable實現轉換。真正的解析結果如下所示:
T[] #a = Expression;
L1: L2: ... Lm:
for (int #i = 0; #i < #a.length; #i++) {
{VariableModifier} TargetType Identifier = #a[#i];
Statement
}
說明:javap是JDK自帶的反彙編器,可以檢視java編譯器為我們生成的位元組碼
【知識點】
- for-each遍歷的集合物件不能為null 既然對集合的for-each遍歷實際上是使用迭代器,會呼叫集合物件的iterator()方法獲得迭代器,那麼,對null集合的for-each遍歷,就會在null集合物件上呼叫方法,勢必會丟擲空指標異常;
- for-each遍歷時不能改變正在遍歷的集合 因為在使用迭代器遍歷集合時,不能夠改變集合,所以for-each遍歷時改變集合,同樣會引發ConcurrentModificationException異常;