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循环的简单分析就这样了,有误或不妥处欢迎指正
时间: 2024-10-11 07:00:12