1. 程式人生 > >如何查看.java文件的字節碼(原碼)

如何查看.java文件的字節碼(原碼)

數據 int new compile from auto 進行 java public

出自於:https://www.cnblogs.com/tomasman/p/6751751.html

直接了解foreach底層有些困難,我們需要從更簡單的例子著手.下面上一個簡單例子:

技術分享圖片 技術分享圖片
1 public class Simple {
2 
3     public static void main(String[] args) {
4         int i = 5;
5         System.out.println(i);
6     }
7 }
技術分享圖片

找到其字節碼文件所在目錄並在目錄下打開終端(Windows系統是在目錄下shift+鼠標右鍵選擇在此打開powershell窗口)

輸入指令:javac -Simple.class >SimpleRunc

目錄中得到SimpleRunc文件,打開它,會看到如下代碼(裏面有我的註釋):

技術分享圖片 技術分享圖片
 1 Compiled from "Simple.java"
 2 public class cn._012thDay._foreach.Simple {
 3   public cn._012thDay._foreach.Simple();
 4     Code:
 5        0: aload_0                將第一個引用型本地變量推送至棧頂;
 6        1: invokespecial #8                  // Method java/lang/Object."<init>":()V
 7                         調用超類構造方法;
 8        4: return
 9 
10   public static void main(java.lang.String[]);
11     Code:
12        0: iconst_5                將int型5推送至棧頂;
13        1: istore_1                將棧頂int型數據存入第二個本地變量;
14                         此處推測:第一個本地變量是超類;
15 
16 
17        2: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
18                         獲取本地靜態域並將其壓入棧頂;即獲取靜態屬性壓入棧頂
19        5: iload_1                將第二個int型本地變量推送至棧頂;
20        6: invokevirtual #22                 // Method java/io/PrintStream.println:(I)V
21                         調用實例方法;
22     獲取類屬性,加載入棧,打印棧頂,即5
23 
24        9: return
25 }
技術分享圖片

如果不懂指令意思,可查詢JVM指令表.這裏我說明一下步驟:

第一步:加載超類Object類

第二步:將int類型的5壓入棧頂,然後將5存入本地變量1

第三部:獲取靜態屬性

第四步:加載本地變量1,即將5推送至棧頂

第五步:打印

for循環

技術分享圖片 技術分享圖片
1 public class ForSimple {
2 
3     public static void main(String[] args) {
4         for (int i = 5; i > 0; i-=2) {
5             System.out.println(i);
6         }
7     }
8 }
技術分享圖片

其實這個例子foreach就做不了,因為foreach遍歷必須要有一個數組.

javap -c:

技術分享圖片
Compiled from "ForSimple.java"
public class cn._012thDay._foreach.ForSimple {
  public cn._012thDay._foreach.ForSimple();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_5
       1: istore_1
       2: goto          15
       5: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
       8: iload_1
       9: invokevirtual #22                 // Method java/io/PrintStream.println:(I)V
      12: iinc          1, -2
      15: iload_1
      16: ifgt          5
      19: return
}
技術分享圖片

main方法第2行goto 15代表無條件跳轉至第15行加載變量1,值是5

16行:ifgt 5 如果int型大於零則回到第5行,5>0

5~9行:打印,5

12行:變量1自增-2,即5變為3

15行:加載變量1,值是3

16行:ifgt 5 如果int型大於零則回到第5行,3>0

5~9行:打印,3

12行:變量1自增-2,即3變為1

15行:加載變量1,值是1

16行:ifgt 5 如果int型大於零則回到第5行,1>0

5~9行:打印,1

12行:變量1自增-2,即1變為-1

15行:加載變量1,值是-1

16行:ifgt 5 如果int型大於零則回到第5行,-1 <0

19行:return main方法結束

for循環打印結果為5,3,1

foreach遍歷

先new一個int[]數組,看看數據是如何存儲的:

技術分享圖片 技術分享圖片
 1 public class SimpleDemo {
 2 
 3     public static void main(String[] args) {
 4         // TODO Auto-generated method stub
 5         int[]arr = new int[3];
 6         arr[0] = 10;
 7         arr[1] = 20;
 8         arr[2] = 30;
 9     }
10 }
技術分享圖片 技術分享圖片 技術分享圖片
Compiled from "SimpleDemo.java"
public class cn._012thDay._foreach.SimpleDemo {
  public cn._012thDay._foreach.SimpleDemo();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_3
       1: newarray       int
       3: astore_1
       4: aload_1
       5: iconst_0
       6: bipush        10
       8: iastore
       9: aload_1
      10: iconst_1
      11: bipush        20
      13: iastore
      14: aload_1
      15: iconst_2
      16: bipush        30
      18: iastore
      19: return
}
技術分享圖片

前3行創建基本類型(int)數組,長度為3,存入本地引用型變量1

將索引為0的位置壓入10並存儲

將索引為1的位置壓入20並存儲

將索引為2的位置壓入30並存儲

接下來開始遍歷,加入for循環:

for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}

技術分享圖片 技術分享圖片
Compiled from "SimpleDemo.java"
public class cn._012thDay._foreach.SimpleDemo {
  public cn._012thDay._foreach.SimpleDemo();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_3
       1: newarray       int
       3: astore_1
       4: aload_1
       5: iconst_0
       6: bipush        10
       8: iastore
       9: aload_1
      10: iconst_1
      11: bipush        20
      13: iastore
      14: aload_1
      15: iconst_2
      16: bipush        30
      18: iastore
      19: iconst_0
      20: istore_2
      21: goto          36
      24: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
      27: aload_1
      28: iload_2
      29: iaload
      30: invokevirtual #22                 // Method java/io/PrintStream.println:(I)V
      33: iinc          2, 1
      36: iload_2
      37: aload_1
      38: arraylength
      39: if_icmplt     24
      42: return
}
技術分享圖片

上面代碼大家應該不那麽陌生了,前面18行存入數組,第19行開始創建了一個新的變量int型值為0,存入變量2.然後用變量2和數組長度作比較,小於數組長度就回到第24行打印,這是一個典型的for循環.

整個遍歷中不考慮超類的加載總共創建了兩個本地變量,即arr[3]和int i,用arr[3]的長度3和i進行比較,符合條件輸出arr[i].輸出結果為10,20,30

下面終於輪到我們的主角foreach登場了,刪除for循環,新增foreach叠代:

for (int item : arr) {
System.out.println(item);
}

技術分享圖片 技術分享圖片
Compiled from "SimpleDemo.java"
public class cn._012thDay._foreach.SimpleDemo {
  public cn._012thDay._foreach.SimpleDemo();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_3
       1: newarray       int
       3: astore_1
       4: aload_1
       5: iconst_0
       6: bipush        10
       8: iastore
       9: aload_1
      10: iconst_1
      11: bipush        20
      13: iastore
      14: aload_1
      15: iconst_2
      16: bipush        30
      18: iastore
      19: aload_1            load local1 :0
      20: dup                copy
      21: astore        5        int[] local5 = local1
      23: arraylength            3
      24: istore        4        int local4 = 3
      26: iconst_0            0
      27: istore_3            int local3 = 0
      28: goto          46
      31: aload         5        load local5 : int[3]
      33: iload_3            load local3 : 0..
      34: iaload            arr[0..]進棧
      35: istore_2            int local2 = arr[0..]
      36: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
      39: iload_2            load local2 :arr[0..]
      40: invokevirtual #22                 // Method java/io/PrintStream.println:(I)V
      43: iinc          3, 1        local3 +=1
      46: iload_3            load local3 :0..
      47: iload         4        load local4 :3
      49: if_icmplt     31        local3 < local4 ? go line31:next line
      52: return
}
技術分享圖片

以上代碼我加入了註釋,這裏大家應該可以看懂了,不考慮超類加載,foreach總共創建了5個本地變量:

local1是原始數組,引用類型

local5是原始數組副本,引用類型

local4是副本數組長度,int類型

local3是0,int類型

local2是arr[i]的副本,int類型

總結:

1.for循環和foreach循環底層創建變量數不同,對於遍歷int[]類型數組,for循環底層創建2個本地變量,而foreach底層創建5個本地變量;

2.for循環直接對數組進行操作,foreach對數組副本進行操作;

由於foreach是對數組副本操作,開發中可能導致的問題:

附上java代碼和javap -c代碼

技術分享圖片 技術分享圖片
 1 public class ForeachDemo {
 2 
 3     public static void main(String[] args) {
 4         // TODO Auto-generated method stub
 5 
 6         String[] s1 = new String[3];
 7         for (String item : s1) {//這裏的s1實際是s1副本
 8             item = new String("b");
 9             System.out.println(item);//這裏可以把副本中的每項打印出來
10         }
11         print(s1);//打印s1是null,因為s1在內存地址中沒有任何變化
12 
13     }
14 
15     private static void print(String[] s) {
16         // TODO Auto-generated method stub
17         for (int i = 0; i < s.length; i++) {
18             System.out.print(s[i]+" ");
19         }
20     }
21     
22 }
技術分享圖片 技術分享圖片 技術分享圖片
Compiled from "ForeachDemo.java"
public class cn._012thDay._foreach.ForeachDemo {
  public cn._012thDay._foreach.ForeachDemo();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_3
       1: anewarray     #16                 // class java/lang/String
       4: astore_1
       5: aload_1
       6: dup
       7: astore        5
       9: arraylength
      10: istore        4
      12: iconst_0
      13: istore_3
      14: goto          42
      17: aload         5
      19: iload_3
      20: aaload
      21: astore_2
      22: new           #16                 // class java/lang/String
      25: dup
      26: ldc           #18                 // String b
      28: invokespecial #20                 // Method java/lang/String."<init>":(Ljava/lang/String;)V
      31: astore_2
      32: getstatic     #23                 // Field java/lang/System.out:Ljava/io/PrintStream;
      35: aload_2
      36: invokevirtual #29                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      39: iinc          3, 1
      42: iload_3
      43: iload         4
      45: if_icmplt     17
      48: aload_1
      49: invokestatic  #34                 // Method print:([Ljava/lang/String;)V
      52: return
}
技術分享圖片

javap -c代碼第7行新建了String[]數組副本變量5,之後一直在對副本進行操作,直到48行aload_1,然後打印,此時不難看出foreach中進行的所有操作都沒有對本地變量1(即原數組)的值產生任何影響.

所以main方法最後一行打印數組s1,其結果一定是3個null

如何查看.java文件的字節碼(原碼)