在java里,我们是怎么写for循环的代码呢。一般都是用这两种写法:
第一种:最传统的for循环写法,for(代码段a;代码段b;代码段c),其中代码段a是初次进入for循环时执行的代码,代码段b是一个boolean的表达式,true则继续执行for循环内容,false则停止for循环,代码段c则是在for循环内部执行完后执行。
第二种:针对集合的遍历,for(类型 单个对象:集合对象)。第二种即是增强for循环。
本文的目的就是讲解增强for循环的原理。先看我的测试用例:
jdk版本:1.7.0_51
java 用例代码:
package com.onlyou.olyfinance.supply.web;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* 增强for循环编译测试
* Created by cd_huang on 2017/4/14.
*/
public class ForeachTest {
public static void superForeachTestArray(){
String[] args =new String[]{"1","2"};
for(String s:args) {
System.out.println(s);
}
}
public static void foreachTestArray(){
String[] args =new String[]{"1","2"};
String[] args2 = args;
int len = args.length;
for(int i = 0; i < len; ++i) {
String s= args2[i];
System.out.println(s);
}
}
public static void superForeachTestIterator(){
List<String> list = Arrays.asList(new String[]{"1","2"});
for(String s:list) {
System.out.println(s);
}
}
public static void foreachTestIterator(){
List<String> list = Arrays.asList(new String[]{"1","2"});
Iterator it = list.iterator();
while(it.hasNext()) {
String s = (String)it.next();
System.out.println(s);
}
}
}
命令行输入:C:\Users\hcd>javac c:\ForeachTest.java
生成StringTest.class文件。
命令行输入:C:\Users\hcd>javap -v c:\ForeachTest.class
生成java编译后的字节码指令。
Classfile /c:/ForeachTest.class
Last modified 2017-4-14; size 1307 bytes
MD5 checksum 5296942b41a468b4fa1f88e3123db02f
Compiled from "ForeachTest.java"
public class com.onlyou.olyfinance.supply.web.ForeachTest
SourceFile: "ForeachTest.java"
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #13.#28 // java/lang/Object."<init>":()V
#2 = Class #29 // java/lang/String
#3 = String #30 // 1
#4 = String #31 // 2
#5 = Fieldref #32.#33 // java/lang/System.out:Ljava/io/PrintStream;
#6 = Methodref #34.#35 // java/io/PrintStream.println:(Ljava/lang/String;)V
#7 = Methodref #36.#37 // java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
#8 = InterfaceMethodref #38.#39 // java/util/List.iterator:()Ljava/util/Iterator;
#9 = InterfaceMethodref #40.#41 // java/util/Iterator.hasNext:()Z
#10 = InterfaceMethodref #40.#42 // java/util/Iterator.next:()Ljava/lang/Object;
#11 = InterfaceMethodref #38.#39 // java/util/List.iterator:()Ljava/util/Iterator;
#12 = Class #43 // com/onlyou/olyfinance/supply/web/ForeachTest
#13 = Class #44 // java/lang/Object
#14 = Utf8 <init>
#15 = Utf8 ()V
#16 = Utf8 Code
#17 = Utf8 LineNumberTable
#18 = Utf8 superForeachTestArray
#19 = Utf8 StackMapTable
#20 = Class #45 // "[Ljava/lang/String;"
#21 = Utf8 foreachTestArray
#22 = Utf8 superForeachTestIterator
#23 = Class #46 // java/util/List
#24 = Class #47 // java/util/Iterator
#25 = Utf8 foreachTestIterator
#26 = Utf8 SourceFile
#27 = Utf8 ForeachTest.java
#28 = NameAndType #14:#15 // "<init>":()V
#29 = Utf8 java/lang/String
#30 = Utf8 1
#31 = Utf8 2
#32 = Class #48 // java/lang/System
#33 = NameAndType #49:#50 // out:Ljava/io/PrintStream;
#34 = Class #51 // java/io/PrintStream
#35 = NameAndType #52:#53 // println:(Ljava/lang/String;)V
#36 = Class #54 // java/util/Arrays
#37 = NameAndType #55:#56 // asList:([Ljava/lang/Object;)Ljava/util/List;
#38 = Class #46 // java/util/List
#39 = NameAndType #57:#58 // iterator:()Ljava/util/Iterator;
#40 = Class #47 // java/util/Iterator
#41 = NameAndType #59:#60 // hasNext:()Z
#42 = NameAndType #61:#62 // next:()Ljava/lang/Object;
#43 = Utf8 com/onlyou/olyfinance/supply/web/ForeachTest
#44 = Utf8 java/lang/Object
#45 = Utf8 [Ljava/lang/String;
#46 = Utf8 java/util/List
#47 = Utf8 java/util/Iterator
#48 = Utf8 java/lang/System
#49 = Utf8 out
#50 = Utf8 Ljava/io/PrintStream;
#51 = Utf8 java/io/PrintStream
#52 = Utf8 println
#53 = Utf8 (Ljava/lang/String;)V
#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;
{
public com.onlyou.olyfinance.supply.web.ForeachTest();
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 superForeachTestArray();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=5, args_size=0
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_0
15: aload_0
16: astore_1
17: aload_1
18: arraylength
19: istore_2
20: iconst_0
21: istore_3
22: iload_3
23: iload_2
24: if_icmpge 46
27: aload_1
28: iload_3
29: aaload
30: astore 4
32: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
35: aload 4
37: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
40: iinc 3, 1
43: goto 22
46: return
LineNumberTable:
line 14: 0
line 15: 15
line 16: 32
line 15: 40
line 18: 46
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 22
locals = [ class "[Ljava/lang/String;", class "[Ljava/lang/String;", int, int ]
stack = []
frame_type = 248 /* chop */
offset_delta = 23
public static void foreachTestArray();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=5, args_size=0
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_0
15: aload_0
16: astore_1
17: aload_0
18: arraylength
19: istore_2
20: iconst_0
21: istore_3
22: iload_3
23: iload_2
24: if_icmpge 46
27: aload_1
28: iload_3
29: aaload
30: astore 4
32: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
35: aload 4
37: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
40: iinc 3, 1
43: goto 22
46: return
LineNumberTable:
line 21: 0
line 22: 15
line 23: 17
line 24: 20
line 25: 27
line 26: 32
line 24: 40
line 28: 46
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 22
locals = [ class "[Ljava/lang/String;", class "[Ljava/lang/String;", int, int ]
stack = []
frame_type = 250 /* chop */
offset_delta = 23
public static void superForeachTestIterator();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=3, args_size=0
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: invokestatic #7 // Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
17: astore_0
18: aload_0
19: invokeinterface #8, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
24: astore_1
25: aload_1
26: invokeinterface #9, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
31: ifeq 54
34: aload_1
35: invokeinterface #10, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
40: checkcast #2 // class java/lang/String
43: astore_2
44: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
47: aload_2
48: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
51: goto 25
54: return
LineNumberTable:
line 31: 0
line 32: 18
line 33: 44
line 34: 51
line 35: 54
StackMapTable: number_of_entries = 2
frame_type = 253 /* append */
offset_delta = 25
locals = [ class java/util/List, class java/util/Iterator ]
frame_type = 250 /* chop */
offset_delta = 28
public static void foreachTestIterator();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=3, args_size=0
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: invokestatic #7 // Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
17: astore_0
18: aload_0
19: invokeinterface #11, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
24: astore_1
25: aload_1
26: invokeinterface #9, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
31: ifeq 54
34: aload_1
35: invokeinterface #10, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
40: checkcast #2 // class java/lang/String
43: astore_2
44: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
47: aload_2
48: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
51: goto 25
54: return
LineNumberTable:
line 38: 0
line 39: 18
line 40: 25
line 41: 34
line 42: 44
line 43: 51
line 44: 54
StackMapTable: number_of_entries = 2
frame_type = 253 /* append */
offset_delta = 25
locals = [ class java/util/List, class java/util/Iterator ]
frame_type = 28 /* same */
}
上面的是java代码编译过后的二进制指令。如果对这些指令是什么意思有兴趣的可以看这篇博客:http://www.blogjava.net/DLevin/archive/2011/09/13/358497.html。不过单单看这个肯定是不够的。需要对java的运行时内存模型有一定的了解,明白栈帧,操作数栈,局部变量表,常量池这些东西的概念,才能理解在执行这些指令时,对应的东西里会产生什么变化。当然,要理解我说的增强for循环原理,并不需要看懂这些指令。认真观察上面的二进制指令,superForeachTestArray()方法编译后的指令,即第86行到121行,和foreachTestArray() 方法编译后的指令,即第137行到172行,两个方法编译后的指令是一样的。
superForeachTestIterator()方法编译后的指令,即第191行到222行,和foreachTestIterator() 方法编译后的指令,即第237行到268行,两个方法编译后的指令是一样的。
这说明什么问题?说明了增强for循环是一个编译前的概念,在编译后编译器会对代码进行优化。有两种对象支持增强for循环:一种是数组,编译器对数组的优化只是写法上的优化,即superForeachTestArray()方法内的增强for循环在编译后会变成foreachTestArray() 方法里for循环的代码。一种是实现了Iterable接口的对象,对于这种,其实质是拿到对象的迭代器进行遍历,即superForeachTestIterator()方法内的增强for循环在编译后会变成foreachTestIterator() 方法内的迭代器遍历。迭代器遍历的好处是安全,且它是对遍历的一种抽象,有种解耦的意味在里面。