Java字節碼(class文件)解析

 Java編譯後生成的.class字節碼文件裏面的內容究竟是什麼呢?一直比較困擾,現在終於看到了廬山真面目,比如對於test.class使用javap -p -verbose test可以查看生成的字節碼裏面的內容。用一個簡單的test類來分析字節碼裏面的內容。

test.java:

/**

*

* @author :zhengrf1

* @date 創建時間:2017年7月19日 上午10:53:08

* @version 1.0

* @parameter 

* @since 

* @return 

*/

public class test {

              private int a = 99;

              public Long b = 88l;

              String d = "hello";

              Object e = new Object();

              static Object c = new Object();

              static String f ="world";

              {

                            a=999;

              }

              static{

                            f ="world2";

              }

             

              test(int param){

                            a = param;

              }

             

              public static void fun(Stringstr){

                            System.out.println(str);

              }

              /**

              *

              * @author : zhengrf1

              * @date 創建時間:2017年7月19日 上午10:53:08

              */

              public static void main(String[]args) {

                            // TODOAuto-generated method stub

                            test t= newtest(999);

                            test.fun(t.d);

              }

}

 

源碼很簡單,主要內容就是1.定義了幾個非靜態成員變量和靜態成員變量,2.並在非靜態語句塊{}和靜態語句塊static{}中重新賦值,3.重載了構造方法,4.創建了一個靜態成員方法fun,5.在main方法中創建了一個test對象並調用了靜態方法fun

 

執行了javap -verbose test命令後,生成test.class的詳細內容信息:

Classfile/D:/javadevelop/eclipse/user/workspace/RedisDao/bin/test.class

  Last modified 2017-7-19; size 1116 bytes

  MD5 checksum a211a3dc33eaac639dc28689a541fdf4

  Compiled from "test.java"

publicclass test

  minor version: 0

  major version: 52

  flags: ACC_PUBLIC, ACC_SUPER

Constantpool:

   #1 = Class              #2             // test

   #2 = Utf8               test

   #3 = Class              #4             // java/lang/Object

   #4 = Utf8               java/lang/Object

   #5 = Utf8               a

   #6 = Utf8               I

   #7 = Utf8               b

   #8 = Utf8               Ljava/lang/Long;

   #9 = Utf8               d

  #10 = Utf8               Ljava/lang/String;

  #11 = Utf8               e

  #12 = Utf8               Ljava/lang/Object;

  #13 = Utf8               c

  #14 = Utf8               f

  #15 = Utf8               <clinit>

  #16 = Utf8               ()V

  #17 = Utf8               Code

  #18 = Methodref          #3.#19         //java/lang/Object."<init>":()V

  #19 = NameAndType        #20:#16        // "<init>":()V

  #20 = Utf8               <init>

  #21 = Fieldref           #1.#22         // test.c:Ljava/lang/Object;

  #22 = NameAndType        #13:#12        // c:Ljava/lang/Object;

  #23 = String             #24            // world

  #24 = Utf8               world

  #25 = Fieldref           #1.#26         // test.f:Ljava/lang/String;

  #26 = NameAndType        #14:#10        // f:Ljava/lang/String;

  #27 = String             #28            // world2

  #28 = Utf8               world2

  #29 = Utf8               LineNumberTable

  #30 = Utf8               LocalVariableTable

  #31 = Utf8               (I)V

  #32 = Fieldref           #1.#33         // test.a:I

  #33 = NameAndType        #5:#6          // a:I

  #34 = Long               88l

  #36 = Methodref          #37.#39        // java/lang/Long.valueOf:(J)Ljava/lan

g/Long;

  #37 = Class              #38            // java/lang/Long

  #38 = Utf8               java/lang/Long

  #39 = NameAndType        #40:#41        // valueOf:(J)Ljava/lang/Long;

  #40 = Utf8               valueOf

  #41 = Utf8               (J)Ljava/lang/Long;

  #42 = Fieldref           #1.#43         // test.b:Ljava/lang/Long;

  #43 = NameAndType        #7:#8          // b:Ljava/lang/Long;

  #44 = String             #45            // hello

  #45 = Utf8               hello

  #46 = Fieldref           #1.#47         // test.d:Ljava/lang/String;

  #47 = NameAndType        #9:#10         // d:Ljava/lang/String;

  #48 = Fieldref           #1.#49         // test.e:Ljava/lang/Object;

  #49 = NameAndType        #11:#12        // e:Ljava/lang/Object;

  #50 = Utf8               this

  #51 = Utf8               Ltest;

  #52 = Utf8               param

  #53 = Utf8               fun

  #54 = Utf8               (Ljava/lang/String;)V

  #55 = Fieldref           #56.#58        // java/lang/System.out:Ljava/io/Print

Stream;

  #56 = Class              #57            // java/lang/System

  #57 = Utf8               java/lang/System

  #58 = NameAndType        #59:#60        // out:Ljava/io/PrintStream;

  #59 = Utf8               out

  #60 = Utf8               Ljava/io/PrintStream;

  #61 = Methodref          #62.#64        // java/io/PrintStream.println:(Ljava/

lang/String;)V

  #62 = Class              #63            // java/io/PrintStream

  #63 = Utf8               java/io/PrintStream

  #64 = NameAndType        #65:#54        // println:(Ljava/lang/String;)V

  #65 = Utf8               println

  #66 = Utf8               str

  #67 = Utf8               main

  #68 = Utf8               ([Ljava/lang/String;)V

  #69 = Methodref          #1.#70         // test."<init>":(I)V

  #70 = NameAndType        #20:#31        // "<init>":(I)V

  #71 = Methodref          #1.#72         // test.fun:(Ljava/lang/String;)V

  #72 = NameAndType        #53:#54        // fun:(Ljava/lang/String;)V

  #73 = Utf8               args

  #74 = Utf8               [Ljava/lang/String;

  #75 = Utf8               t

  #76 = Utf8               SourceFile

  #77 = Utf8               test.java

{

  private int a;

    descriptor: I

    flags: ACC_PRIVATE

  publicjava.lang.Long b;

    descriptor: Ljava/lang/Long;

    flags: ACC_PUBLIC

 

  java.lang.String d;

    descriptor: Ljava/lang/String;

    flags:

 

  java.lang.Object e;

    descriptor: Ljava/lang/Object;

    flags:

 

  static java.lang.Object c;

    descriptor: Ljava/lang/Object;

    flags: ACC_STATIC

 

  static java.lang.String f;

    descriptor: Ljava/lang/String;

    flags: ACC_STATIC

 

  static {};

    descriptor: ()V

    flags: ACC_STATIC

    Code:

      stack=2, locals=0, args_size=0

        0: new           #3                  // class java/lang/Object

         3: dup

        4: invokespecial #18                // Method java/lang/Object."<init>

":()V

         7: putstatic     #21                 // Field c:Ljava/lang/Object;

        10: ldc           #23                 // String world

        12: putstatic     #25                 // Field f:Ljava/lang/String;

        15: ldc           #27                 // String world2

        17: putstatic     #25                 // Field f:Ljava/lang/String;

        20: return

      LineNumberTable:

        line 15: 0

        line 16: 10

        line 21: 15

        line 22: 20

      LocalVariableTable:

        Start Length  Slot  Name  Signature

 

  test(int);

    descriptor: (I)V

    flags:

    Code:

      stack=3, locals=2, args_size=2

        0: aload_0

         1: invokespecial #18                 // Methodjava/lang/Object."<init>

":()V

         4: aload_0

         5: bipush        99

         7: putfield      #32                 // Field a:I

        10: aload_0

        11: ldc2_w        #34                 // long 88l

        14: invokestatic  #36                 // Methodjava/lang/Long.valueOf:(

J)Ljava/lang/Long;

        17: putfield      #42                 // Field b:Ljava/lang/Long;

       20: aload_0

        21: ldc           #44                 // String hello

        23: putfield      #46                 // Field d:Ljava/lang/String;

        26: aload_0

       27: new           #3                  // class java/lang/Object

        30: dup

       31: invokespecial #18                // Method java/lang/Object."<init>

":()V

        34: putfield      #48                 // Field e:Ljava/lang/Object;

        37: aload_0

        38: sipush        999

        41: putfield      #32                 // Field a:I

        44: aload_0

        45: iload_1

        46: putfield      #32                 // Field a:I

        49: return

      LineNumberTable:

        line 24: 0

        line 11: 4

        line 12: 10

        line 13: 20

        line 14: 26

        line 18: 37

        line 25: 44

        line 26: 49

      LocalVariableTable:

        Start Length  Slot  Name  Signature

            0      50    0  this   Ltest;

            0     50     1 param   I

 

  public static void fun(java.lang.String);

    descriptor: (Ljava/lang/String;)V

    flags: ACC_PUBLIC, ACC_STATIC

    Code:

      stack=2, locals=1, args_size=1

        0: getstatic     #55                 // Field java/lang/System.out:Ljav

a/io/PrintStream;

        3: aload_0

         4: invokevirtual #61                 // Methodjava/io/PrintStream.prin

tln:(Ljava/lang/String;)V

        7: return

      LineNumberTable:

        line 29: 0

        line 30: 7

      LocalVariableTable:

        Start Length  Slot  Name  Signature

            0       8    0   str   Ljava/lang/String;

 

  public static void main(java.lang.String[]);

    descriptor: ([Ljava/lang/String;)V

    flags: ACC_PUBLIC, ACC_STATIC

    Code:

      stack=3, locals=2, args_size=1

        0: new           #1                  // class test

         3: dup

         4: sipush        999

         7: invokespecial #69                 // Method"<init>":(I)V

        10: astore_1

        11: aload_1

        12: getfield      #46                 // Field d:Ljava/lang/String;

        15: invokestatic  #71                 // Methodfun:(Ljava/lang/String;)

V

        18: return

      LineNumberTable:

        line 38: 0

        line 39: 11

        line 40: 18

      LocalVariableTable:

        Start Length  Slot  Name  Signature

            0      19    0  args   [Ljava/lang/String;

           11       8    1     t   Ltest;

}

SourceFile: "test.java"

 

裏面的內容看似雜亂,其實稍微分析下就會很清楚了

 

第一部分深紅色模塊:

Classfile/D:/javadevelop/eclipse/user/workspace/RedisDao/bin/test.class

  Last modified 2017-7-19; size 1116 bytes

  MD5 checksum a211a3dc33eaac639dc28689a541fdf4

  Compiled from "test.java"

--就是關於.calss文件的文件本身的信息介紹,有最後修改時間,文件大小,MD5校驗碼,來自哪個.java文件

 

第二部分紅色模塊:

publicclass test

  minor version: 0

  major version: 52

  flags: ACC_PUBLIC, ACC_SUPER

--就是關於test這個類的信息,包括編譯版本,和flags:修飾符訪問權限,可見test是ACC_PUBLICpublic

 

第三部分藍色模塊:

Constantpool:

   #1 = Class              #2             // test

   #2 = Utf8               test

   #3 = Class              #4             // java/lang/Object

   #4 = Utf8               java/lang/Object

   #5 = Utf8               a

   #6 = Utf8               I

   #7 = Utf8               b

   #8 = Utf8               Ljava/lang/Long;

   #9 = Utf8               d

  #10 = Utf8               Ljava/lang/String;

  #11 = Utf8               e

  #12 = Utf8               Ljava/lang/Object;

  #13 = Utf8               c

  #14 = Utf8               f

  #15 = Utf8               <clinit>

  #16 = Utf8               ()V

  #17 = Utf8               Code

  #18 = Methodref          #3.#19         //java/lang/Object."<init>":()V

  #19 = NameAndType        #20:#16        // "<init>":()V

  #20 = Utf8               <init>

  #21 = Fieldref           #1.#22         // test.c:Ljava/lang/Object;

  #22 = NameAndType        #13:#12        // c:Ljava/lang/Object;

  #23 = String             #24            // world

  #24 = Utf8               world

  #25 = Fieldref           #1.#26         // test.f:Ljava/lang/String;

  #26 = NameAndType        #14:#10        // f:Ljava/lang/String;

  #27 = String             #28            // world2

  #28 = Utf8               world2

  #29 = Utf8               LineNumberTable

  #30 = Utf8               LocalVariableTable

  #31 = Utf8               (I)V

  #32 = Fieldref           #1.#33         // test.a:I

  #33 = NameAndType        #5:#6          // a:I

  #34 = Long               88l

  #36 = Methodref          #37.#39        // java/lang/Long.valueOf:(J)Ljava/lan

g/Long;

  #37 = Class              #38            // java/lang/Long

  #38 = Utf8               java/lang/Long

  #39 = NameAndType        #40:#41        // valueOf:(J)Ljava/lang/Long;

  #40 = Utf8               valueOf

  #41 = Utf8               (J)Ljava/lang/Long;

  #42 = Fieldref           #1.#43         // test.b:Ljava/lang/Long;

  #43 = NameAndType        #7:#8          // b:Ljava/lang/Long;

  #44 = String             #45            // hello

  #45 = Utf8               hello

  #46 = Fieldref           #1.#47         // test.d:Ljava/lang/String;

  #47 = NameAndType        #9:#10         // d:Ljava/lang/String;

  #48 = Fieldref           #1.#49         // test.e:Ljava/lang/Object;

  #49 = NameAndType        #11:#12        // e:Ljava/lang/Object;

  #50 = Utf8               this

  #51 = Utf8               Ltest;

  #52 = Utf8               param

  #53 = Utf8               fun

  #54 = Utf8               (Ljava/lang/String;)V

  #55 = Fieldref           #56.#58        // java/lang/System.out:Ljava/io/Print

Stream;

  #56 = Class              #57            // java/lang/System

  #57 = Utf8               java/lang/System

  #58 = NameAndType        #59:#60        // out:Ljava/io/PrintStream;

  #59 = Utf8               out

  #60 = Utf8               Ljava/io/PrintStream;

  #61 = Methodref          #62.#64        // java/io/PrintStream.println:(Ljava/

lang/String;)V

  #62 = Class              #63            // java/io/PrintStream

  #63 = Utf8               java/io/PrintStream

  #64 = NameAndType        #65:#54        // println:(Ljava/lang/String;)V

  #65 = Utf8               println

  #66 = Utf8               str

  #67 = Utf8               main

  #68 = Utf8               ([Ljava/lang/String;)V

  #69 = Methodref          #1.#70         // test."<init>":(I)V

  #70 = NameAndType        #20:#31        // "<init>":(I)V

  #71 = Methodref          #1.#72         // test.fun:(Ljava/lang/String;)V

  #72 = NameAndType        #53:#54        // fun:(Ljava/lang/String;)V

  #73 = Utf8               args

  #74 = Utf8               [Ljava/lang/String;

  #75 = Utf8               t

  #76 = Utf8               SourceFile

  #77 = Utf8               test.java

---這就是大名鼎鼎的常量池了,裏面包含了很多類型的常量,比如

#1 =Class              #2             // test 

#2 =Utf8               test

--這個意思是在代碼區用到的#1這個索引買就是用到了一個Class對象,這個Class對象就是test,簡單來說就是#1就是Class,對一個類或接口的符號引用,而這個Class的名就是#2,而#2就是test

再比如後面的代碼內容中有一行是:

4: invokespecial #18                 // Methodjava/lang/Object."<init>":()V

--來我們分析下是什麼回事,首先看命令invokespecial(調用需要特殊處理的實例方法:invokespecial),說明這行命令是調用一個實例方法,調用哪個方法呢?調用常量池中的#18,再去看看#18的內容:

#18 = Methodref          #3.#19         //java/lang/Object."<init>":()V

--#18是一個Methodref類型常量,意思這個常量是對一個類中方法的符號引用,而#18由#3.#19組成,那看看#3和#19

#3 =Class              #4             // java/lang/Object

#4 =Utf8               java/lang/Object

#19 =NameAndType        #20:#16        // "<init>":()V

#20 =Utf8               <init>

#16 =Utf8               ()V

--#3是Class,對一個類或接口的符號引用,那個類名由#4確定,#4對應java/lang/Object,說明#3全名就是java/lang/Object這個類型,而#19是NameAndType, 對一個字段或方法的部分符號引用,而引用的方法由

#20:#16組成,而#20和#16是Utf8,utf-8編碼的字符串。分別是<init>和()V,其中<init>是構造方法的方法名,而()V是方法的簽名,表示方法是個沒有參數返回類型爲void。所以#18的最後的全名是java/lang/Object."<init>":()V,表示這是個Object類的默認構造方法。這是查找的過程,其實javap生成的結果中#18常量池這行已經在//後面幫我們拼湊出了最後的全名

--說到這裏,所謂的神祕的常量池就被我們扒的差不多了,但是常量池的作用是什麼呢?就以上面4: invokespecial #18例子看,當我們加載Class文件到JVM中,常量池的內容會存放在方法區的常量池區域,當我們執行代碼到invokespecial #18這行時,JVM就知道要去找Object類的構造方法init,如果Object類還沒加載,那就加載Object.class進jvm,加載Object.class後,很明顯JVM將在方法區中存儲Object類的所有方法,包括構造方法init,這樣這個方法的物理內存地址就確定了,假設這個地址是ff0809,那麼這個ff0809這個值就會回寫到常量池中,假設是#18 = Methodref         #3.#19         ff0809,那麼後續只要其他代碼執行到invokespecial #18,就直接調用ff0809這個方法入口就行,不需要再次去解析。這個就是常量池的符號引用解析成內存直接引用的一步。

 

第四部分綠色部分:

  private int a;

    descriptor: I

    flags: ACC_PRIVATE

publicjava.lang.Long b;

    descriptor: Ljava/lang/Long;

    flags: ACC_PUBLIC

 

  java.lang.String d;

    descriptor: Ljava/lang/String;

    flags:

 

  java.lang.Object e;

    descriptor: Ljava/lang/Object;

    flags:

 

  static java.lang.Object c;

    descriptor: Ljava/lang/Object;

    flags: ACC_STATIC

 

  static java.lang.String f;

    descriptor: Ljava/lang/String;

    flags: ACC_STATIC

--這個就是類的靜態成員和非靜態成員的信息描述,其中以static String f爲例子,可見,生產的字節碼中是下面描述:

staticjava.lang.String f;

    descriptor: Ljava/lang/String;

    flags: ACC_STATIC

-- descriptor表示成員變量的簽名,而flags: ACC_STATIC表示成員變量的訪問權限和靜態屬性。其中可以看到訪問權限是空,也就是使用了默認的包訪問權限。

 

第五部分紫色部分:

static{};

    descriptor: ()V

    flags: ACC_STATIC

    Code:

      stack=2, locals=0, args_size=0

        0: new           #3                  // class java/lang/Object

         3: dup

        4: invokespecial #18                // Method java/lang/Object."<init>

":()V

         7: putstatic     #21                 // Field c:Ljava/lang/Object;

        10: ldc           #23                 // String world

        12: putstatic     #25                 // Field f:Ljava/lang/String;

        15: ldc           #27                 // String world2

        17: putstatic     #25                 // Field f:Ljava/lang/String;

        20: return

      LineNumberTable:

        line 15: 0

        line 16: 10

        line 21: 15

        line 22: 20

      LocalVariableTable:

        Start Length  Slot  Name  Signature

 

  test(int);

    descriptor: (I)V

    flags:

    Code:

      stack=3, locals=2, args_size=2

        0: aload_0

         1: invokespecial #18                 // Methodjava/lang/Object."<init>

":()V

         4: aload_0

         5: bipush        99

         7: putfield      #32                 // Field a:I

        10: aload_0

        11: ldc2_w        #34                 // long 88l

        14: invokestatic  #36                 // Methodjava/lang/Long.valueOf:(

J)Ljava/lang/Long;

        17: putfield      #42                 // Field b:Ljava/lang/Long;

       20: aload_0

        21: ldc           #44                 // String hello

        23: putfield      #46                 // Field d:Ljava/lang/String;

        26: aload_0

       27: new           #3                  // class java/lang/Object

        30: dup

       31: invokespecial #18                // Method java/lang/Object."<init>

":()V

        34: putfield      #48                 // Field e:Ljava/lang/Object;

        37: aload_0

        38: sipush        999

        41: putfield      #32                 // Field a:I

        44: aload_0

        45: iload_1

        46: putfield      #32                 // Field a:I

        49: return

      LineNumberTable:

        line 24: 0

        line 11: 4

        line 12: 10

        line 13: 20

        line 14: 26

        line 18: 37

        line 25: 44

        line 26: 49

      LocalVariableTable:

        Start Length  Slot  Name  Signature

            0      50    0 this   Ltest;

            0      50    1 param   I

 

  public static void fun(java.lang.String);

    descriptor: (Ljava/lang/String;)V

    flags: ACC_PUBLIC, ACC_STATIC

    Code:

      stack=2, locals=1, args_size=1

        0: getstatic     #55                 // Field java/lang/System.out:Ljav

a/io/PrintStream;

        3: aload_0

         4: invokevirtual #61                 // Methodjava/io/PrintStream.prin

tln:(Ljava/lang/String;)V

        7: return

      LineNumberTable:

        line 29: 0

        line 30: 7

      LocalVariableTable:

        Start Length  Slot  Name  Signature

            0       8    0   str   Ljava/lang/String;

 

  public static void main(java.lang.String[]);

    descriptor: ([Ljava/lang/String;)V

    flags: ACC_PUBLIC, ACC_STATIC

    Code:

      stack=3, locals=2, args_size=1

        0: new           #1                  // class test

         3: dup

         4: sipush        999

         7: invokespecial #69                 // Method"<init>":(I)V

        10: astore_1

        11: aload_1

        12: getfield      #46                 // Field d:Ljava/lang/String;

        15: invokestatic  #71                 // Methodfun:(Ljava/lang/String;)

V

        18: return

      LineNumberTable:

        line 38: 0

        line 39: 11

        line 40: 18

      LocalVariableTable:

        Start Length  Slot  Name  Signature

            0      19    0  args   [Ljava/lang/String;

           11       8    1     t   Ltest;

--這就是test類的方法代碼模塊,拿構造方法爲例子:

  test(int);

    descriptor: (I)V

    flags:

    Code:

      stack=3, locals=2, args_size=2

         0: aload_0

         1: invokespecial #18                 // Methodjava/lang/Object."<init>

":()V

         4: aload_0

         5: bipush        99

         7: putfield      #32                 // Field a:I

        10: aload_0

        11: ldc2_w        #34                 // long 88l

        14: invokestatic  #36                 // Methodjava/lang/Long.valueOf:(

J)Ljava/lang/Long;

        17: putfield      #42                 // Field b:Ljava/lang/Long;

       20: aload_0

        21: ldc           #44                 // String hello

        23: putfield      #46                 // Field d:Ljava/lang/String;

        26: aload_0

       27: new           #3                  // class java/lang/Object

        30: dup

       31: invokespecial #18                // Method java/lang/Object."<init>

":()V

        34: putfield      #48                 // Field e:Ljava/lang/Object;

        37: aload_0

        38: sipush        999

        41: putfield      #32                 // Field a:I

        44: aload_0

        45: iload_1

        46: putfield      #32                 // Field a:I

        49: return

      LineNumberTable:

        line 24: 0

        line 11: 4

        line 12: 10

        line 13: 20

        line 14: 26

        line 18: 37

        line 25: 44

        line 26: 49

      LocalVariableTable:

        Start Length  Slot  Name  Signature

            0      50    0  this   Ltest;

            0      50    1 param   I

--可以看到descriptor: (I)V是方法的簽名,表示有一個int類型的參數,返回類型是void的方法,flags:表示訪問權限和靜態屬性,可見改方法是默認的包訪問權限和非靜態,Code:

      stack=3, locals=2, args_size=2

         0: aload_0

         1: invokespecial #18                 // Methodjava/lang/Object."<init>

":()V

         4: aload_0

         5: bipush        99

         7: putfield      #32                 // Field a:I

        10: aload_0

        11: ldc2_w        #34                 // long 88l

        14: invokestatic  #36                 // Methodjava/lang/Long.valueOf:(

J)Ljava/lang/Long;

        17: putfield      #42                 // Field b:Ljava/lang/Long;

       20: aload_0

        21: ldc           #44                 // String hello

        23: putfield      #46                 // Field d:Ljava/lang/String;

        26: aload_0

       27: new           #3                  // class java/lang/Object

        30: dup

       31: invokespecial #18                // Method java/lang/Object."<init>

":()V

        34: putfield      #48                 // Field e:Ljava/lang/Object;

        37: aload_0

        38: sipush        999

        41: putfield      #32                 // Field a:I

        44: aload_0

        45: iload_1

        46: putfield      #32                 // Field a:I

        49: return

--其實就是代碼命令了,stack=3,locals=2, args_size=2,stack應該表示棧深,locals應該表示局部變量個數,args_size表示參數個數,分別是this和param。

0: aload_0

1:invokespecial #18                 //Method java/lang/Object."<init>":()V

--這兩行是啥意思呢?其實這是編譯器自己添加的代碼,調用父類的構造方法,而test的父類就是上帝類Object。這說明,初始化之類之前必須初始化父類,如果你不寫,編譯器也會幫你寫。

4:aload_0

5:bipush        99

7:putfield      #32                 // Field a:I

--這個意思是把99壓入棧中並且賦值給成員變量#32,而#32在常量池中就是a。

10:aload_0

11:ldc2_w        #34                 // long 88l

14:invokestatic  #36                 // Methodjava/lang/Long.valueOf:(

J)Ljava/lang/Long;

17:putfield      #42                 // Field b:Ljava/lang/Long;

--這幾行意思就是把88l作爲參數傳入Long.valueOf(long)方法中,生成一個Long類型對象並且賦值給成員變量b,其實就是Long b = 88l;這行代碼,只不過編譯器使用了語法糖自動裝載幫我們增加了調用Long.valueOf的方法生成了Long類型對象。

20:aload_0

21:ldc           #44                 // String hello

23:putfield      #46                 // Field d:Ljava/lang/String;

--這幾行意思就是把#44所表示的“hello”字符串賦值給成員變量d,其實就是d = "hello";

26: aload_0

27:new           #3                  // class java/lang/Object

30: dup

31:invokespecial #18                 //Method java/lang/Object."<init>":()V

34:putfield      #48                 // Field e:Ljava/lang/Object;

37:aload_0

38:sipush        999

41:putfield      #32                 // Field a:I

44:aload_0

45: iload_1

46:putfield      #32                 // Field a:I

49:return

--這幾行就不細說了,跟上面大同小異,分別表示代碼:

Object e = new Object();

a=999;

a = param;

--從上面的構造方法的解析可以看到,雖然我們在源碼中寫的構造方法是test(intparam){a = param;}

但是實際上編譯器生成的構造方法可不這麼簡單,1.首先是調用父類Object的構造方法。2.把成員變量定義時的初始化操作也添加進了構造方法中。3.把{}語句塊中的賦值操作也添加進構造方法中。4.最後一步纔是源碼中的代碼。知道這個步驟,我們會對初始化有更深的瞭解。

除了舉例子說明的構造方法外,可以看到還有其他方法,其中比較奇怪的是

static{};

    descriptor: ()V

    flags: ACC_STATIC

    Code:

      stack=2, locals=0, args_size=0

        0: new           #3                  // class java/lang/Object

         3: dup

        4: invokespecial #18                // Method java/lang/Object."<init>

":()V

         7: putstatic     #21                 // Field c:Ljava/lang/Object;

        10: ldc           #23                 // String world

        12: putstatic     #25                 // Field f:Ljava/lang/String;

        15: ldc           #27                 // String world2

        17: putstatic     #25                 // Field f:Ljava/lang/String;

        20: return

--這個其實就是大名鼎鼎的clinit方法(不過奇怪的是爲什麼方法名不是直接寫clinit),由編譯器生成,作用就是初始化靜態成員變量,如上面代碼翻譯過來就是

static Object c = new Object();

static String f = "world";

f = "world2";

--剩下的其他方法就沒什麼好說了,和源碼一一對應

補充:

1.       LineNumberTable:

        line 24: 0

        line 11: 4

        line 12: 10

        line 13: 20

        line 14: 26

        line 18: 37

        line 25: 44

        line 26: 49

     LocalVariableTable:

        Start  Length Slot  Name   Signature

            0      50    0  this   Ltest;

            0      50    1 param   I

 

--這些是什麼?其實這些不是很重要,只是附加信息,其中LineNumberTable:是方法裏面的命令行數和.java源碼文件代碼行數的對應關係,比如line 12: 10就是源碼中的第12行代碼和字節碼中構造方法中CODE段的第10行命令。應該是爲了檢查排除bug使用。

 

2.        LocalVariableTable:

        Start Length  Slot  Name  Signature

            0      50    0  this   Ltest;

            0      50     1 param  I

--這個又是什麼,也是附加信息,描述的是方法內部局部變量(包括參數)的信息,比如上面就是對應構造方法,一共有兩個局部變量,分別是隱藏的this和參數param,並且它們的作用域都是構造方法中命令行的0到50行,其實就是整個構造方法內有效。

 

3.       如果把static{}註釋掉,只要test內部有static變量,那麼編譯器還是會自動添加clinit方法,如果把所有static變量和語句塊註釋掉,那麼編譯器將不再生成clinit方法。只要存在一個static成員變量,則編譯器都會自動生成clinit方法,該方法的方法簽名和默認構造方法一模一樣,只是flag屬性是ACC_STATIC(靜態)

 

4.       如果再創建一個子類ChildTest繼承Test類,那麼字節碼有什麼變化嗎?應該說變化不大

public final classChildTest extends test

  minor version: 0

  major version: 52

  flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER

--只是在對Class描述段,裏面顯示了ChildTest繼承於test,並且是final屬性,flags的內容也做相應修改。還有就是因爲test重載了構造方法,創造了一個帶參數的構造方法,所以父類test已經不存在默認構造方法,這就使子類必須手工聲明和調用super(int)來初始化父類。如果父類存在默認構造方法(也就是無參構造方法),則子類不必手工調用super(),編譯器會自動添加,有一點需注意:子類的static成員初始化在父類初始化之前,簡單來說clinit方法在init方法前執行,並且只在加載時執行一次。

 

總結:通過對class文件的分析,我們能更深入地去了解java

另外附三個擴展連接:裏面有對命令和名詞的更詳細說明,就是有個不好的地方,沒有總體的概念

http://blog.csdn.net/lisulong1/article/details/53001211

http://baike.baidu.com/link?url=qJIIGWuC_EV-MmgebWE08fxVgYYbh_j-xuRDnrokIaO23zn-ogJ5Q5Kp40vbi6OrmrejF4BwtigOKzOWdtdXmIt1whTsMITcVMO_mP_mIXeoPAk3KiG-QRaScJxf5XJO

http://blog.csdn.net/xieyuooo/article/details/17452383

 

發佈了80 篇原創文章 · 獲贊 57 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章