for-each其实只是java提供的语法糖。语法糖是编程语言提供的一些便于程序员书写代码的语法,是编译器提供给程序员的糖衣,编译时会对这些语法特殊处理。语法糖虽然不会带来实质性的改进,但是在提高代码可读性,提高语法严谨性,减少编码错误机会上确实做出了很大贡献;
Java要求集合必须实现Iterable接口,才能使用for-each语法糖遍历该集合的实例;
先看个简单的例子:
public static void main(String[] args) {
List 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."":()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
#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;
#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 // "":()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."":()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;
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
}
数组的实现机制跟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编译器为我们生成的字节码