反编译解析数组使用foreach
一、说明
- foreach 循环遍历的原理是通过迭代器 Iterator,不断获取 next 元素,所以使用 foreach,编译的时候编译器会自动将对 for 这个关键字的使用转化为对目标的迭代器的使用
- 我们常常认为,要想使用 foreach 循环遍历,就必须正确地实现 Iterable 接口,但是,你有没有想过,数组没有实现 Iterable 接口,为什么可以使用 foreach 语句循环遍历呢?这里我使用几个例子,以及进行Java反编译来说明数组这个特殊情况
二、集合使用foreach
- 首先,我们基于对照试验,使用实现 Iterable 接口的 Collection 集合(ArrayList)进行 foreach 使用
import java.util.ArrayList;
/**
*
* @author 吕泽江
* @version 创建日期: 2021-3-15 14:39:11
*/
public class TestCollection {
/**
* @param args
*/
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
arrayList.add(4);
arrayList.add(5);
for (Integer integer : arrayList) {
System.out.println(integer);
}
}
}
三、数组使用foreach
- 然后,我们使用没有实现 Iterable 接口的数组来进行 foreach 对比
/**
*
* @author 吕泽江
* @version 创建日期: 2021-3-15 14:42:28
*/
public class TestArray {
public static void main(String[] args) {
int[] arr = {6,7,8,9,10};
for (int i : arr) {
System.out.println(i);
}
}
}
- 以上两个程序十分简单,可以看到输出结果均为预期结果,但是单单这样,我们还不至于探究数组与 foreach 的特殊关系
四、数组使用for
- 这里举例数组使用 for 的目的在于下述对于Java反编译的对照
/**
*
* @author 吕泽江
* @version 创建日期: 2021-3-15 14:45:42
*/
public class TestFor {
public static void main(String[] args) {
int[] arr = {6,7,8,9,10};
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
五、javap反编译程序
- 我们使用以下命令来对上述几个程序进行Java反编译处理
javac -encoding utf-8 TestCollection.java
javap -v TestCollection.class
javac -encoding utf-8 TestArray.java
javap -v TestArray.class
javac -encoding utf-8 TestFor.java
javap -v TestFor.class
5.1 TestCollection结果
Classfile /D:/workspace/Test/src/Test0315/TestCollection.class
Last modified 2021-3-15; size 852 bytes
MD5 checksum abb0ab7ce240eb4590733fa75e5448a5
Compiled from "TestCollection.java"
public class Test0315.TestCollection
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #13.#25 // java/lang/Object."<init>":()V
#2 = Class #26 // java/util/ArrayList
#3 = Methodref #2.#25 // java/util/ArrayList."<init>":()V
#4 = Methodref #9.#27 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
#5 = Methodref #2.#28 // java/util/ArrayList.add:(Ljava/lang/Object;)Z
#6 = Methodref #2.#29 // java/util/ArrayList.iterator:()Ljava/util/Iterator;
#7 = InterfaceMethodref #30.#31 // java/util/Iterator.hasNext:()Z
#8 = InterfaceMethodref #30.#32 // java/util/Iterator.next:()Ljava/lang/Object;
#9 = Class #33 // java/lang/Integer
#10 = Fieldref #34.#35 // java/lang/System.out:Ljava/io/PrintStream;
#11 = Methodref #36.#37 // java/io/PrintStream.println:(Ljava/lang/Object;)V
#12 = Class #38 // Test0315/TestCollection
#13 = Class #39 // java/lang/Object
#14 = Utf8 <init>
#15 = Utf8 ()V
#16 = Utf8 Code
#17 = Utf8 LineNumberTable
#18 = Utf8 main
#19 = Utf8 ([Ljava/lang/String;)V
#20 = Utf8 StackMapTable
#21 = Class #26 // java/util/ArrayList
#22 = Class #40 // java/util/Iterator
#23 = Utf8 SourceFile
#24 = Utf8 TestCollection.java
#25 = NameAndType #14:#15 // "<init>":()V
#26 = Utf8 java/util/ArrayList
#27 = NameAndType #41:#42 // valueOf:(I)Ljava/lang/Integer;
#28 = NameAndType #43:#44 // add:(Ljava/lang/Object;)Z
#29 = NameAndType #45:#46 // iterator:()Ljava/util/Iterator;
#30 = Class #40 // java/util/Iterator
#31 = NameAndType #47:#48 // hasNext:()Z
#32 = NameAndType #49:#50 // next:()Ljava/lang/Object;
#33 = Utf8 java/lang/Integer
#34 = Class #51 // java/lang/System
#35 = NameAndType #52:#53 // out:Ljava/io/PrintStream;
#36 = Class #54 // java/io/PrintStream
#37 = NameAndType #55:#56 // println:(Ljava/lang/Object;)V
#38 = Utf8 Test0315/TestCollection
#39 = Utf8 java/lang/Object
#40 = Utf8 java/util/Iterator
#41 = Utf8 valueOf
#42 = Utf8 (I)Ljava/lang/Integer;
#43 = Utf8 add
#44 = Utf8 (Ljava/lang/Object;)Z
#45 = Utf8 iterator
#46 = Utf8 ()Ljava/util/Iterator;
#47 = Utf8 hasNext
#48 = Utf8 ()Z
#49 = Utf8 next
#50 = Utf8 ()Ljava/lang/Object;
#51 = Utf8 java/lang/System
#52 = Utf8 out
#53 = Utf8 Ljava/io/PrintStream;
#54 = Utf8 java/io/PrintStream
#55 = Utf8 println
#56 = Utf8 (Ljava/lang/Object;)V
{
public Test0315.TestCollection();
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 13: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: iconst_1
10: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
13: invokevirtual #5 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
16: pop
17: aload_1
18: iconst_2
19: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
22: invokevirtual #5 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
25: pop
26: aload_1
27: iconst_3
28: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
31: invokevirtual #5 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
34: pop
35: aload_1
36: iconst_4
37: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
40: invokevirtual #5 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
43: pop
44: aload_1
45: iconst_5
46: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
49: invokevirtual #5 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
52: pop
53: aload_1
54: invokevirtual #6 // Method java/util/ArrayList.iterator:()Ljava/util/Iterator;
57: astore_2
58: aload_2
59: invokeinterface #7, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
64: ifeq 87
67: aload_2
68: invokeinterface #8, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
73: checkcast #9 // class java/lang/Integer
76: astore_3
77: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
80: aload_3
81: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
84: goto 58
87: return
LineNumberTable:
line 19: 0
line 20: 8
line 21: 17
line 22: 26
line 23: 35
line 24: 44
line 26: 53
line 27: 77
line 28: 84
line 29: 87
StackMapTable: number_of_entries = 2
frame_type = 253 /* append */
offset_delta = 58
locals = [ class java/util/ArrayList, class java/util/Iterator ]
frame_type = 250 /* chop */
offset_delta = 28
}
SourceFile: "TestCollection.java"
5.2 TestArray结果
Classfile /D:/workspace/Test/src/Test0315/TestArray.class
Last modified 2021-3-15; size 543 bytes
MD5 checksum 0f5e743a1a6a5a08bf3ecb086eba6ed4
Compiled from "TestArray.java"
public class Test0315.TestArray
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #5.#17 // java/lang/Object."<init>":()V
#2 = Fieldref #18.#19 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Methodref #20.#21 // java/io/PrintStream.println:(I)V
#4 = Class #22 // Test0315/TestArray
#5 = Class #23 // java/lang/Object
#6 = Utf8 <init>
#7 = Utf8 ()V
#8 = Utf8 Code
#9 = Utf8 LineNumberTable
#10 = Utf8 main
#11 = Utf8 ([Ljava/lang/String;)V
#12 = Utf8 StackMapTable
#13 = Class #24 // "[Ljava/lang/String;"
#14 = Class #25 // "[I"
#15 = Utf8 SourceFile
#16 = Utf8 TestArray.java
#17 = NameAndType #6:#7 // "<init>":()V
#18 = Class #26 // java/lang/System
#19 = NameAndType #27:#28 // out:Ljava/io/PrintStream;
#20 = Class #29 // java/io/PrintStream
#21 = NameAndType #30:#31 // println:(I)V
#22 = Utf8 Test0315/TestArray
#23 = Utf8 java/lang/Object
#24 = Utf8 [Ljava/lang/String;
#25 = Utf8 [I
#26 = Utf8 java/lang/System
#27 = Utf8 out
#28 = Utf8 Ljava/io/PrintStream;
#29 = Utf8 java/io/PrintStream
#30 = Utf8 println
#31 = Utf8 (I)V
{
public Test0315.TestArray();
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
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=6, args_size=1
0: iconst_5
1: newarray int
3: dup
4: iconst_0
5: bipush 6
7: iastore
8: dup
9: iconst_1
10: bipush 7
12: iastore
13: dup
14: iconst_2
15: bipush 8
17: iastore
18: dup
19: iconst_3
20: bipush 9
22: iastore
23: dup
24: iconst_4
25: bipush 10
27: iastore
28: astore_1
29: aload_1
30: astore_2
31: aload_2
32: arraylength
33: istore_3
34: iconst_0
35: istore 4
37: iload 4
39: iload_3
40: if_icmpge 63
43: aload_2
44: iload 4
46: iaload
47: istore 5
49: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
52: iload 5
54: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
57: iinc 4, 1
60: goto 37
63: return
LineNumberTable:
line 13: 0
line 14: 29
line 15: 49
line 14: 57
line 17: 63
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 37
locals = [ class "[Ljava/lang/String;", class "[I", class "[I", int, int ]
stack = []
frame_type = 248 /* chop */
offset_delta = 25
}
SourceFile: "TestArray.java"
5.3 TestFor结果
Classfile /D:/workspace/Test/src/Test0315/TestFor.class
Last modified 2021-3-15; size 492 bytes
MD5 checksum 40766f0529a4d099ff82983b65b42540
Compiled from "TestFor.java"
public class Test0315.TestFor
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #5.#16 // java/lang/Object."<init>":()V
#2 = Fieldref #17.#18 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Methodref #19.#20 // java/io/PrintStream.println:(I)V
#4 = Class #21 // Test0315/TestFor
#5 = Class #22 // java/lang/Object
#6 = Utf8 <init>
#7 = Utf8 ()V
#8 = Utf8 Code
#9 = Utf8 LineNumberTable
#10 = Utf8 main
#11 = Utf8 ([Ljava/lang/String;)V
#12 = Utf8 StackMapTable
#13 = Class #23 // "[I"
#14 = Utf8 SourceFile
#15 = Utf8 TestFor.java
#16 = NameAndType #6:#7 // "<init>":()V
#17 = Class #24 // java/lang/System
#18 = NameAndType #25:#26 // out:Ljava/io/PrintStream;
#19 = Class #27 // java/io/PrintStream
#20 = NameAndType #28:#29 // println:(I)V
#21 = Utf8 Test0315/TestFor
#22 = Utf8 java/lang/Object
#23 = Utf8 [I
#24 = Utf8 java/lang/System
#25 = Utf8 out
#26 = Utf8 Ljava/io/PrintStream;
#27 = Utf8 java/io/PrintStream
#28 = Utf8 println
#29 = Utf8 (I)V
{
public Test0315.TestFor();
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
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=3, args_size=1
0: iconst_5
1: newarray int
3: dup
4: iconst_0
5: bipush 6
7: iastore
8: dup
9: iconst_1
10: bipush 7
12: iastore
13: dup
14: iconst_2
15: bipush 8
17: iastore
18: dup
19: iconst_3
20: bipush 9
22: iastore
23: dup
24: iconst_4
25: bipush 10
27: iastore
28: astore_1
29: iconst_0
30: istore_2
31: iload_2
32: aload_1
33: arraylength
34: if_icmpge 52
37: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
40: aload_1
41: iload_2
42: iaload
43: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
46: iinc 2, 1
49: goto 31
52: return
LineNumberTable:
line 13: 0
line 14: 29
line 15: 37
line 14: 46
line 17: 52
StackMapTable: number_of_entries = 2
frame_type = 253 /* append */
offset_delta = 31
locals = [ class "[I", int ]
frame_type = 250 /* chop */
offset_delta = 20
}
SourceFile: "TestFor.java"
六、结果分析
- 我们从反编译出来的结果中可以很清楚的看到,Collection集合(ArrayList)在使用 foreach 时,是通过迭代器 Iterator,不断获取 next 元素,而数组没有这样做,在我们观察数组在使用普通 for 与 foreach 两个反编译结果时发现,数组的 foreach 结果与 for 几乎可以说是一模一样,可以看到网上一些说法描述道:“数组也可以使用 foreach 循环遍历,是因为Java将对于数组的 foreach 循环转换为对于这个数组每一个的循环引用”,我想这句话更直观或者更准确的表达应该是:数组使用 foreach 时,是把 foreach 转换为普通 for 来循环处理的,与 Iterable 无关
- 所以我们需要记住:除了实现 Iterable 接口可以使用 foreach 外,数组也是可以使用 foreach 的,在数组使用 foreach 时,是把 foreach 转换为普通 for 来循环处理