首页 > 代码库 > 用字节码分析Java的For循环

用字节码分析Java的For循环

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循环的简单分析就这样了,有误或不妥处欢迎指正

用字节码分析Java的For循环