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

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值