今天给大家分享一下for-each循环的底层原理,故事还得从字节码反汇编和反编译说起...
首先看一下Java JDK1.5的新特性
泛型: ...
for-each. ...
自动拆箱和装箱功能 ...
枚举: ...
可变参数 ...
静态导入 ...
线程并发库
对于字节码反汇编,可以使用 JDK 自带的工具 javap
首先看一下 javap 有哪些指令以及用法
$ javap
用法: javap
其中, 可能的选项包括:
-help --help -? 输出此用法消息
-version 版本信息
-v -verbose 输出附加信息
-l 输出行号和本地变量表
-public 仅显示公共类和成员
-protected 显示受保护的/公共类和成员
-package 显示程序包/受保护的/公共类
和成员 (默认)
-p -private 显示所有类和成员
-c 对代码进行反汇编
-s 输出内部类型签名
-sysinfo 显示正在处理的类的
系统信息 (路径, 大小, 日期, MD5 散列)
-constants 显示最终常量
-classpath 指定查找用户类文件的位置
-cp 指定查找用户类文件的位置
-bootclasspath 覆盖引导类文件的位置
由此可见,在我们拿到字节码文件之后,使用javap -c 字节码文件位置,即可进行反汇编
其实 for-each 是 jdk 1.5 的语法糖,它可以迭代集合和数组
1.for-each 迭代集合
首先来看for-each 迭代集合
public static void main(String[] args) {
List a = new ArrayList<>();
a.add("1");
a.add("2");
a.add("3");
for (String temp : a) {
System.out.print(temp);
}
}
javap 反汇编这段代码
$ javap -c ForeachTest.class
Compiled from "ForeachTest.java"
public class cn.ixan.design.ForeachTest {
public cn.ixan.design.ForeachTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."":()V
7: astore_1
8: aload_1
9: ldc #4 // String 1
11: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
16: pop
17: aload_1
18: ldc #6 // String 2
20: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
25: pop
26: aload_1
27: ldc #7 // String 3
29: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
34: pop
35: aload_1
36: invokeinterface #8, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
41: astore_2
42: aload_2
43: invokeinterface #9, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
48: ifeq 71
51: aload_2
52: invokeinterface #10, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
57: checkcast #11 // class java/lang/String
60: astore_3
61: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream;
64: aload_3
65: invokevirtual #13 // Method java/io/PrintStream.print:(Ljava/lang/String;)V
68: goto 42
71: return
}
由第 36 行,第 43 行,第 52 行可知,迭代集合使用到了 Iterator
然后使用 idea 查看字节码文件,发现 foreach 遍历集合运用迭代器
public static void main(String[] args) {
List a = new ArrayList();
a.add("1");
a.add("2");
a.add("3");
Iterator var2 = a.iterator();
while(var2.hasNext()) {
String temp = (String)var2.next();
System.out.print(temp);
}
}
2.for-each 迭代数组
我们知道集合继承 Iterable 接口,使用使用 iterator 迭代集合,
那么数组没有继承 Iterable 接口,for-each 是如何迭代它呢?
首先来看 for-each 迭代数组
public static void main(String[] args) {
String[] arr = {"1","2"};
for(String e : arr){
System.out.println(e);
}
}
javap 反汇编这段代码
$ javap -c ForeachTest.class
Compiled from "ForeachTest.java"
public class cn.ixan.design.ForeachTest {
public cn.ixan.design.ForeachTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_2
1: anewarray #2 // class java/lang/String
4: dup
5: iconst_0
6: ldc #3 // String 1
8: aastore
9: dup
10: iconst_1
11: ldc #4 // String 2
13: aastore
14: astore_1
15: aload_1
16: astore_2
17: aload_2
18: arraylength
19: istore_3
20: iconst_0
21: istore 4
23: iload 4
25: iload_3
26: if_icmpge 49
29: aload_2
30: iload 4
32: aaload
33: astore 5
35: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
38: aload 5
40: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
43: iinc 4, 1
46: goto 23
49: return
}
和迭代集合不同,没有出现 Iterator,说明没有使用迭代器
第 18 行 arraylength,可能使用到了数组的 length 属性
使用 idea 反编译查看,发现 for-each 遍历数组是经典 for 循环
public static void main(String[] args) {
String[] arr = new String[]{"1", "2"};
String[] var2 = arr;
int var3 = arr.length;
for(int var4 = 0; var4 < var3; ++var4) {
String e = var2[var4];
System.out.println(e);
}
}
总结: 1.for-each 迭代集合时,使用的是迭代器迭代集合。
2.for-each 迭代数组时,使用的是经典for循环。