Fou循环常常用,但是在字节码层它是怎样执行的呢?出于兴趣驱使,就有了这篇短文了!
首先要分析字节码就得先写个类了,代码如下:
public class ForTest{
public static void main(String[] args) {
for (int i = 0; i < 10; ++i) {
System.out.println(i);
}
}
}
简单的for循环打印10个数,那么编译之后就该得到他的字节码了,使用命令
javap -v ForTest.class > ForTest.j
这里用到 -v 参数,导出了整个class文件的详情,如果只是想简单得到字节码,就只需换成 -c 参数就成了,这里得到的结果如下:
Classfile /E:/Develop/JavaTest/ForTest.class
Last modified 2015-1-2; size 437 bytes
MD5 checksum 21aad18f3a51ffa226ad2f49d7c3f5e0
Compiled from "ForTest.java"
public class ForTest
SourceFile: "ForTest.java"
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #5.#15 // java/lang/Object."<init>":()V
#2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Methodref #18.#19 // java/io/PrintStream.println:(I)V
#4 = Class #20 // ForTest
#5 = Class #21 // 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 = Utf8 SourceFile
#14 = Utf8 ForTest.java
#15 = NameAndType #6:#7 // "<init>":()V
#16 = Class #22 // java/lang/System
#17 = NameAndType #23:#24 // out:Ljava/io/PrintStream;
#18 = Class #25 // java/io/PrintStream
#19 = NameAndType #26:#27 // println:(I)V
#20 = Utf8 ForTest
#21 = Utf8 java/lang/Object
#22 = Utf8 java/lang/System
#23 = Utf8 out
#24 = Utf8 Ljava/io/PrintStream;
#25 = Utf8 java/io/PrintStream
#26 = Utf8 println
#27 = Utf8 (I)V
{
public ForTest();
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 1: 0
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: iconst_0
1: istore_1
2: iload_1
3: bipush 10
5: if_icmpge 21
8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
11: iload_1
12: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
15: iinc 1, 1
18: goto 2
21: return
LineNumberTable:
line 3: 0
line 4: 8
line 3: 15
line 6: 21
StackMapTable: number_of_entries = 2
frame_type = 252 /* append */
offset_delta = 2
locals = [ int ]
frame_type = 250 /* chop */
offset_delta = 18
}
其实重点还是下面这段代码,其他内容只是方便理解和分析class文件:
0: iconst_0
1: istore_1
2: iload_1
3: bipush 10
5: if_icmpge 21
8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
11: iload_1
12: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
15: iinc 1, 1
18: goto 2
21: return
现在就分析分析For循环是怎么执行的了
首先先初始化循环变量:
0: iconst_0
1: istore_1
这两行代码相当于 int i = 0 这句代码(iconst_0 是数字 0,istore_1 就是表示局部变量1,这里就是源码里的 i 了)
然后判断循环条件:
2: iload_1
3: bipush 10
5: if_icmpge 21
这三行意思就是 i 是否小于 10 ,小于则继续往下执行,否则就跳到 编号为 21 的 return那里也即跳出for循环了(iload_1 就是指读取局部变量1 即 源码里的 i)
条件判断成立了那么就执行For循环体了:
8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
11: iload_1
12: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
这三行结果就是执行了 System.out.println(i); 这句代码
这里的 #2 和 #3 代表的意思可以在 常数池 里找到了,所以在使用 javap命令的时候我用了 -v 参数来方便理解了
既然循环体执行完了,那么接着就得更新循环变量了:
15: iinc 1, 1
即 ++i; 语句
最后就是跳到判断语句的地方了:
18: goto 2
For循环的简单分析就这样了,有误或不妥处欢迎指正