用字節碼分析Java的For循環

Fou循環經常用,可是在字節碼層它是怎樣執行的呢?出於興趣驅使,就有了這篇短文了!java

首先要分析字節碼就得先寫個類了,代碼以下:shell

public class ForTest{
	public static void main(String[] args) {
		for (int i = 0; i < 10; ++i) {
			System.out.println(i);
		}
	}
}

簡單的for循環打印10個數,那麼編譯以後就該獲得他的字節碼了,使用命令app

javap -v ForTest.class > ForTest.j

這裏用到 -v 參數,導出了整個class文件的詳情,若是隻是想簡單獲得字節碼,就只需換成 -c 參數就成了,這裏獲得的結果以下:
spa

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文件:code

         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循環是怎麼執行的了ip

首先先初始化循環變量:ci

         0: iconst_0      
         1: istore_1

這兩行代碼至關於   int i = 0  這句代碼(iconst_0 是數字 0,istore_1 就是表示局部變量1,這裏就是源碼裏的 i 了)get

而後判斷循環條件:源碼

         2: iload_1       
         3: bipush        10
         5: if_icmpge     21

這三行意思就是 i 是否小於 10 ,小於則繼續往下執行,不然就跳到 編號爲 21 的 return那裏也即跳出for循環了(iload_1 就是指讀取局部變量1 即 源碼裏的 i)it

條件判斷成立了那麼就執行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循環的簡單分析就這樣了,有誤或不妥處歡迎指正

相關文章
相關標籤/搜索