<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆-8  評論-67  文章-0  trackbacks-0
      從上一篇博客(Java Class文件解析)的分析可以看出,Class文件中的各項是按照一定的包含關系和次序關系存儲的,因此Class文件可以從頭到尾地被解析為各個項。下面請看一個解析實例:

    這里我們以非常著名的HelloWorld為實例來分析Java Class文件。HelloWorld的源代碼如下:

    package bytecodeResearch;

    public class HelloWorld {

        
    //定義兩個靜態變量
        private static String str_1 = "Hello";
        
    private static String str_2 = "World";
        
        
    /**
         * 靜態方法
         * 
    @param str
         
    */

        
    private static void Hello(String str)
        
    {
            System.out.println(str);
        }
        
        
    /**
         * 靜態方法
         * 
    @param str
         
    */

        
    private static void World(String str)
        
    {
            System.out.println(str);
        }

        
        
    /**
         * 程序入口方法
         * 
    @param args
         
    */

        
    public static void main(String[] args)
        
    {
            Hello(str_1);
            World(str_2);
        }

    }

        編譯單元HelloWorld.java文件經過編譯器編譯之后,將得到一個HelloWorld.class文件。需要說明的是,經過不同的編譯器編譯之后得到的HelloWorld.class文件可能不一樣,本人選用的開發工具是Eclipse3.3-europa版本,Eclipse SDK自帶的JDT工具內置了增量式Java編譯器,這個編譯器與javac完全兼容。本人以下的分析都是基于Eclipse自帶的編譯器編譯得到的Class文件,如果有人用了Jikes編譯器、GNU的編譯器或者其它版本的javac編譯器的話,得到的Class文件跟我的可能不完全一樣的話,但是Class文件的格式肯定是一樣的,因此分析的原理也都是一樣的,即都是基于上一篇博客(Java Class文件解析)給出的ClassFile結構圖示。

        經過Eclipse3.3編譯得到的Class文件內容如下

      

     

    HelloWorld.class文件的內容

    00000000h: CA FE BA BE 00 00 00 31  00 31 07 00 02  01 00 1B (該類共有48個常量池表項)

    00000010h: 62 79 74 65 63 6F 64 65 52 65 73 65 61 72 63 68 

    00000020h: 2F 48 65 6C 6C 6F 57 6F 72 6C 64  07 00 04 01 00 

    00000030h: 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 

    00000040h: 74 01 00 05 73 74 72 5F 31 01 00 12 4C 6A 61 76 

    00000050h: 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B  01 00  

    00000060h: 05 73 74 72 5F 32 01 00 08 3C 63 6C 69 6E 69 74 

    00000070h: 3E  01 00 03 28 29 56  01 00 04 43 6F 64 65  08 00   (10號常量池表項)

    00000080h: 0C  01 00 05 48 65 6C 6C 6F  09 00 01 00 0E 0C 00 

    00000090h: 05 00 06 08 00 10  01 00 05 57 6F 72 6C 64 09 00  

    000000a0h: 01 00 12 0C 00 07 00 06 01 00 0F 4C 69 6E 65 4E 

    000000b0h: 75 6D 62 65 72 54 61 62 6C 65 01 00 12 4C 6F 63  

    000000c0h: 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65 01   (20號常量池表項)

    000000d0h: 00 06 3C 69 6E 69 74  3E  0A 00 03 00 17  0C 00 15 

    000000e0h: 00 09 01 00 04 74 68 69 73 01 00 1D 4C 62 79 74 

    000000f0h: 65 63 6F 64 65 52 65 73 65 61 72 63 68 2F 48 65 

    00000100h: 6C 6C 6F 57 6F 72 6C 64 3B  01 00 15 28 4C 6A 61  

    00000110h: 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29  

    00000120h: 56  09 00 1C 00 1E 07 00 1D 01 00 10 6A 61 76 61 

    00000130h: 2F 6C 61 6E 67 2F 53 79 73 74 65 6D  0C 00 1F 00   (30號常量池表項)

    00000140h: 20 01 00 03 6F 75 74 01 00 15 4C 6A 61 76 61 2F  

    00000150h: 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D 3B 0A 

    00000160h: 00 22 00 24 07 00 23 01 00 13 6A 61 76 61 2F 69 

    00000170h: 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D  0C 00 25  

    00000180h: 00 1A 01 00 07 70 72 69 6E 74 6C 6E 01 00 03 73 

    00000190h: 74 72  01 00 04 6D 61 69 6E 01 00 16  28 5B 4C 6A  

    000001a0h: 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B   (40號常量池表項)

    000001b0h: 29 56  0A 00 01 00 2A 0C 00 0C 00 1A 0A 00 01 00 

    000001c0h: 2C  0C 00 10 00 1A 01 00 04 61 72 67 73 01 00 13 

    000001d0h: 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 

    000001e0h: 6E 67 3B 01 00 0A 53 6F 75 72 63 65 46 69 6C 65  

    000001f0h: 01 00 0F 48 65 6C 6C 6F 57 6F 72 6C 64 2E 6A 61  

    00000200h: 76 61  00 21 00 01 00 03 00 00  00 02 00 0A 00 05    (該類有0x0002field)

    00000210h: 00 06  00 00  00 0A 00 07 00 06 00 00 00 05  00 08    (該類有0x0005method)

    00000220h: 00 08 00 09 00 01  00 0A 00 00 00 33 00 01 00 00        (1method_info)

    00000230h: 00 00 00 0B 12 0B B3 00 0D 12 0F B3 00 11 B1 00 

    00000240h: 00 00 02 00 13 00 00 00 0E 00 03 00 00 00 06 00 

    00000250h: 05 00 07 00 0A 00 03 00 14 00 00 00 02 00 00 00        (2method_info)

    00000260h: 01  00 15 00 09  00 01  00 0A  00 00 00 2F 00 01 00 

    00000270h: 01 00 00 00 05 2A B7 00 16 B1 00 00 00 02 00 13 

    00000280h: 00 00 00 06 00 01 00 00 00 03 00 14 00 00 00 0C  

    00000290h: 00 01 00 00 00 05 00 18 00 19 00 00  00 0A 00 0C    (3method_info)

    000002a0h: 00 1A  00 01  00 0A 00 00 00 36  00 02 00 01 00 00 

    000002b0h: 00 08 B2 00 1B 2A B6 00 21 B1 00 00 00 02 00 13 

    000002c0h: 00 00 00 0A 00 02 00 00 00 0F 00 07 00 10 00 14  

    000002d0h: 00 00 00 0C 00 01 00 00 00 08 00 26 00 06 00 00  

    000002e0h: 00 0A 00 10 00 1A 00 01 00 0A 00 00 00 36 00 02     (4method_info)

    000002f0h: 00 01 00 00 00 08 B2 00 1B 2A B6 00 21 B1 00 00  

    00000300h: 00 02 00 13 00 00 00 0A 00 02 00 00 00 18 00 07 

    00000310h: 00 19 00 14 00 00 00 0C 00 01 00 00 00 08 00 26  

    00000320h: 00 06  00 00 00 09 00 27 00 28 00 01 00 0A 00 00    (5method_info)

    00000330h: 00 3F  00 01 00 01 00 00 00 0D B2 00 0D B8 00 29 

    00000340h: B2 00 11 B8 00 2B B1 00 00 00 02 00 13 00 00 00 

    00000350h: 0E 00 03 00 00 00 21 00 06 00 22 00 0C 00 23 00 

    00000360h: 14 00 00 00 0C 00 01 00 00 00 0D 00 2D 00 2E 00 

    00000370h: 00 00 01 00 2F 00 00 00 02 00 30                              (該類有0x0001attribute)

     

     

         注:該Class的內容是經過解析的,即將.class文件的二進制字節流轉換為16進制的數字字符流形式。由于UE查看Class文件時,用一個8位的16進制數來表示每一行起始字節對應于Class文件中的字節號,為了不造成視覺差異,本人寫了一段程序來讀寫Class文件內容,其輸出結果就是上面這段內容,每16個字節占一行,每個字節均拆解成216進制整數字符,每一行左端的8位的16進制整數就表示該行的起始字節對應于原Class文件中的字節號。這個程序貼在了我第一篇blog里--個讀取Class文件的示例程序

        好了,閑言少敘!下面開始正式解析這個Class文件。

     

    按照上一篇博客(Java Class文件解析)介紹的ClassFile結構圖示以及對ClassFile結構的詳細分析,可以將HelloWorld.class文件的各個項順序地解析如下:

    (1) 4個字節0xCAFEBABEmagic項的內容。

    (2) 接下來的2個字節0x0000minor_version項的內容,即該Class文件的次版本號0

    (3) 接下來的2個字節0x0031major_version項的內容,即該Class文件的主版本號為49。這個主版本號對應于J2SE5.0的編譯器的編譯結果。如果你用J2SE6.0的編譯器來編譯此HelloWorld.java程序的話,得到的主版本號應該是0x0032,即50

    (4) 接下來的2個字節0x0031constant_pool_count項的內容,它表示接下來共有連續的49-1=48constant_pool表項。

    (5) 緊接著constant_pool_count項后面的是常量池列表項。常量池列表項的長度是可變的,這是因為不同的常量池表項的格式是不一樣的。

    5.1)先來分析第一個常量池表項。上一篇博客(Java Class文件解析)中提到,每個常量池表項的具體格式是要根據其tag項(即該常量池表項的第一個字節)來決定。因此,constant_pool_count項后面的第一字節就是第一個常量池表項的tag項的內容,在這里為0x07cp_type表可知,tag值為0x07的對應CONSTANT_Class結構的常量池表項。再查閱CONSTANT_Class_info表,該表結構如下:

    CONSTANT_Class_info {

                u1 tag;

                u2 name_index;

    }

           根據《JVM Spec(2nded)中對此表的說明可知,1個字節的tag項就是剛才讀取的值0x07,而后的2個字節的name_index項表示對一個CONSTANT_Utf8_info表的索引,該索引項包含了類或者接口的完全限定名稱。對于HelloWorld.class這個類來說,第一個常量池表項就是一個CONSTANT_Class_info表的結構,其tag項的值是0x07,其name_index項的值是0x0002,即該常量池表項指向索引為0x0002的常量池表項。

    5.2)再來分析第二個常量池表項。其tag項值為0x01,查閱cp_type表可知,該常量池表項是一個CONSTANT_ Utf8_info表的結構,然后我們查閱CONSTANT_ Utf8_info表,該表結構如下:

    CONSTANT_Utf8_info {
             u1 tag;
             u2 length;
             u1 bytes[length];
     }

           根據《JVM Spec(2nded)中對此表的說明可知,1個字節的tag項的值就是0x012個字節的length項給出了后續的bytes數組的長度(字節數),在這里為0x001B,即其后續的27個字節均是該bytes數組的內容,它包含了按照變體UTF-8格式存儲(不是標準的UTF-8格式啊,具體的差別請查閱那兩個參考資料,都有說明)的字符串中的字符。按照此變體格式可以將這27個字節解析為“bytecodeResearch/HelloWorld”,這27個字符實際上就是該ClassFile的完全限定名稱,這是為什么呢?因為第一個常量池表項是CONSTANT_Class_info結構的,其name_index項指向的常量池表項包含了類或者接口的完全限定名稱,第二個常量池表項正是第一個常量池表項中name_index項所指向的常量池表項,因此“bytecodeResearch/HelloWorld”這27個字符就是該ClassFile的完全限定名稱(注:是完全限定名稱的內部形式,下同)

           5.3)再來分析第三個常量池表項。第二個常量池表項后的第一個字節為0x07,由cp_type表可知,tag值為0x07的對應CONSTANT_Class結構的常量池表項。類似于對第一個常量池表項的分析,0x07后面的兩個字節為name_index項,其值為0x0004,即該常量池表項指向索引為0x0004的常量池表項,且該常量池是CONSTANT_Utf8_info表的結構

           5.4)下面來分析第四個常量池表項。該常量池表項的tag項值又是0x01,類似地,同第二個常量池表項的分析,該常量池表項也是一個CONSTANT_ Utf8_info表的結構,其length項值為0x0010,即其后續的bytes數組的長度為0x0010個字節。按照《JVM Spec(2nded)中關于變體UTF-8格式的定義,可以將這16個字節解析為“java/lang/Object”,這是Java類層次結構的根類Object類的完全限定名稱。

           類似地,可以分析第5678910都是一個CONSTANT_ Utf8_info結構,分別表示“str_1”,“Ljava/lang/String”,“str_2”,“<clinit>”,“()V”,“Code

           5.5…….其他常量池表項的分析原理是類似的,這里就不贅述了。

           5.6)第四十八個常量池表項的解析。經過分析,第48個常量池起始于第000001f0h字節,終止于第00000202h字節。該常量池表項又是一個CONSTANT_Utf8_info表的結構,其15字節的bytes數組項的值可解析為“HelloWorld.java”。由后面的分析說明該常量池表項存儲的是該類文件的SourceFile屬性的信息。

           好了,該Class文件的常量池部分已經解析結束了!下面開始其它部分的解析:

           (6)   在常量池列表項后面的兩個字節是該Java類型的access_flags,這里為0x0021。根據access_flags表可以查到,該值是0x00200x0001兩者的和,即該類的修飾符為ACC_PUBLIC+ACC_SUPER,前者表示該類是public類型,后者表示采用invokespecial指令特殊處理對超類的調用。具體可以查閱兩本參考資料中關于JVM指令集的描述J

           7)接下來的兩個字節是this_class項,它是一個對常量池表項的索引,在這里值為0x0001,即它指向1號常量池表項,而1號常量池表項是一個CONSTANT_Class_info結構,它指向2號常量池表項,2號常量池表項的值為bytecodeResearch/HelloWorld,前面提到這是該Class文件的完全路徑名稱的內部形式,因此this_class即指bytecodeResearch/HelloWorld

           (8) 接下來的兩個字節是super_class項,它是一個對常量池表項的索引,在這里值為0x0003, 即它指向3號常量池表項,查一下上面對3號常量池表項的分析,它指向4號常量池表項,而4號常量池表項包含的值為“java/lang/Object”,即super_class的實際值為“java/lang/Object”。說明我們分析的這個Class文件的超類是java.lang.Object

    (9) 下面的兩個字節是interfaces_count項,在這里的值為0x0000,這表示由該類直接實現或者由該接口所擴展的超接口的數量為0,因此該Class文件中的interfaces列表項也就不存在了。

    (10)接下來的字節應該是field項的內容了。首先的兩個字節是fields_count項,這里的值為0x0002,即該類聲明了兩個字段(變量),亦即該項之后的fields列表項的元素個數為2。由于fields列表項的類型為field_info,所以在fields_count項下面的字節是兩個連續的field_info結構,下面來詳細分析這兩個具體的field_info結構;

    10.1)第一個field_info,即第一個字段的相關信息。

    10.1.1)首先的兩個字節是第一個fieldaccess_flags項,在這里的值為0x000A,查閱field_access_flags表可知該access_flags項表示的是ACC_PRIVATE+ACC_STATIC,即該字段是由privatestatic修飾的。

    10.1.2)接下來的兩個字節是name_index項,在這里的值為0x0005,即該字段的簡單名稱由第5個常量池表項描述的,根據上一篇博客(Java Class文件解析)的分析可知,該常量池包含的信息為str_1,即該字段的名稱為str_1

    10.1.3)接下來的兩個字節是descriptor_index項,在這里的值為0x0006,即該字段的描述符存儲在6個常量池表項,根據上一篇博客(Java Class文件解析)的分析可知,這個字段的類型為“Ljava/lang/String”。在Class文件中,“L<classname>”表示一個類的實例,其中<classname>是這個內部形式的完全限定類名。

    10.1.4)接下來的兩個字節是attributes_count項,在這里的值為0x0000,即該字段沒有附加的屬性列表。因而也就不用討論attributes[]項了。

    10.2)第二個field_info,即第二個字段的相關信息。類似地,參照第一個字段信息的分析,我們很快就可以知道該字段的access_flags項為ACC_PRIVATE+ACC_STATIC,名字為str_2,類型描述符為“Ljava/lang/String”attributes_count的值為0x0000

    (11)接下來的字節應該是method項的內容了。首先的兩個字節是methods_count項,這里的值為0x0005,即該類聲明了5個方法,亦即該項之后的methods列表項的元素個數為5。由于methods列表項的類型為method_info,所以在methods_count項下面的字節是5個連續的method_info結構,下面來詳細分析這5個具體的method_info結構:

    11.1)第1method_info結構,即第一個方法的相關信息,如方法名、描述符(即方法的返回值及參數類型)以及一些其它信息。根據method_info表分析接下來的字節碼可以得到:

    11.1.1access_flags項,值為0x0008,即給方法的訪問修飾符為ACC_STATIC,它表示這是一個static方法。

    11.1.2name_index項,值為0x0008,8號常量池表項存儲的信息為<clinit>即該方法的名稱為<clinit>。這是一個類與接口初始化方法,這個方法是由Java編譯器編譯源代碼的時候產生的,Java編譯器將該類的所有類變量初始化語句和所有類型的靜態初始化器收集到一起,放到<clinit>方法中,該方法只能被JVM隱式地調用,專門用于把類型的靜態變量設置為它們正確的初始值。

    11.1.3descriptor_index項,值為0x0009,9號常量池表項存儲的信息為()V,這表示該方法的沒有參數,返回值為void

    11.1.4attributes_count項,值為0x0001,即該方法有一個屬性。查閱屬性信息表的結構,如下所示:

    attribute_info {
             u2 attribute_name_index;
             u4 attribute_length;
             u1 info[attribute_length];
        }

    由這個表,我們可以知道attributes_count項后面的是這個屬性的attribute_name_index項,該項的值為0x000A,該屬性的名字信息存儲在第10號常量池表項里。查閱第10號常量池表項可知,該屬性的名字為“Code”,然后我們查閱Code_attribute表,結構如下:

    Code_attribute {
             u2 attribute_name_index;
             u4 attribute_length;
             u2 max_stack;
             u2 max_locals;
             u4 code_length;
             u1 code[code_length];
             u2 exception_table_length;
             {       u2 start_pc;
                    u2 end_pc;
                    u2 handler_pc;
                    u2 catch_type;
             }       exception_table[exception_table_length];
             u2 attributes_count;
             attribute_info attributes[attributes_count];
        }

    11.1.5Code_attribute項,該項包含了一個Java方法,或者實例初始化方法,或者類或接口初始化方法的JVM指令和輔助信息。每個JVM的實現都必須要識別Code屬性,在每個method_info結構也必須確切地有一個Code屬性。下面來具體分析這個屬性;

    11.1.5.1) attribute_name_index項,2個字節,該項的值為0x000A,查閱第10號常量池表型包含的信息后知該屬性的名字為“Code”。

    11.1.5.2attribute_length項,4個字節,值為0x00000033,這說明該屬性的長度,出去初始的6個字節,還有0x33=51個字節。如果不愿意討論接下去的51字節的話,可以直接跳過這51字節(00000226h-0000025eh字節),討論下一個方法。

    11.1.5.3max_stack項,2個字節,值為0x0001,這表示該方法執行中任何點操作數棧上字的最大個數為1

    11.1.5.4max_locals項,2個字節,值為0x0000,這表示該方法使用的局部變量個數為0

    11.1.5.5code_length項,4個字節,值為0x0000000B,這表示該方法的code數組中字節的總個數為11

    11.1.5.6code[]項,由11.1.5.5)知,該方法的code數組共占11個字節。該code[]項給出了實現該方法的JVM代碼的實際字節。例如第一個指令是0x12,這是ldc指令,這個指令表示將一個常量池表項壓入棧,它需要一個操作數,而它后面的一個字節是0x0B,因此這條指令加上其操作數就表示將常量池中的第0x0B號表項壓入棧。接下來的一個指令是0xB3,這是putstatic指令,這條指令表示設置類中靜態變量的值。它需要兩個操作數indexbyte1indexbyte2,這兩個操作數均占一個字節,JVM執行putstatic執行時,會通過計算(indexbyte1<<8)|indexbyte2生成一個對常量池表項的索引,這里的參數為0x000x0D,運算結果是0x0D,因此這條指令的意思就是將操作數棧的當前棧頂元素賦值給0x0D號常量池表項所存儲的字段(str_1),即完成對字段str_1的賦值。。同樣,下面的五個字節的意思,就是將索引為0x0F的常量池表項壓入操作數棧,并賦值給(0x00<<)|0x11=0x11號常量池表項中所存儲的字段(str_2),即完成對字段str_2的賦值。該Code數組的最后一個字節是0xB1,這是一條不帶操作數的指令return,它表示從方法中返回,返回值為void

    11.1.5.7exception_table_length項,2個字節,值為0x0000,這表示該方法的異常處理器的個數為0。因此exception_table[ ]就沒有必要討論了。

    11.1.5.8attributes_count項,2個字節,值為0x0002,這表示該方法Code屬性具有兩個屬性。當前由Code屬性定義和使用的兩個屬性是LineNumberTaleLocalVariableTable屬性。

    11.1.5.9attributes[ ]項,由于LineNumberTaleLocalVariableTable兩個屬性都包含了一些調試信息,但是兩者都是可選屬性,因此這里就不多討論了。

    11.2)第2method_info結構,即第2個方法的相關信息。第2個方法是實例初始化方法<init>,這段方法在Class文件中的字節編號為:0000025fh-0000029bh字節,感興趣的朋友請繼續分析下去,原理和第一個方法的分析是一樣的。

    11.3)第3method_info結構,即第3個方法的相關信息。第3個方法是該類的靜態方法Hello,這段方法在Class文件中的字節編號為:0000029ch-000002dfh字節,感興趣的朋友請繼續分析下去,原理和第一個方法的分析是一樣的。

    11.4)第4method_info結構,即第4個方法的相關信息。第4個方法是該類的靜態方法World,這段方法在Class文件中的字節編號為:000002e0h-00000323h字節,感興趣的朋友請繼續分析下去,原理和第一個方法的分析是一樣的。

    11.5)第5method_info結構,即第5個方法的相關信息。第5個方法是該類文件的入口main方法,這段方法在Class文件中的字節編號為:00000324h-00000370h字節,感興趣的朋友請繼續分析下去,原理和第一個方法的分析是一樣的。

    (12) attributes_count項,2個字節,該ClassFile的屬性計數項,它的值為0x0001,表示在后續的attributes列表中的attributes_info表的總個數為1

    (13) attributes[ ]項,該ClassFile的屬性列表項,這是Class文件的最后一項了!由(12)知,該列表項只有一個表項。由attribute_info表結構

    attribute_info {
                       u2 attribute_name_index;
                       u4 attribute_length;
                       u1 info[attribute_length];
        }

    可知,attributes_count項后面的兩個字節是attribute_name_index項,它的值為0x002F,它表示對常量池編號為0x002F的表項的一個索引。這個索引表項存儲的信息為”SourceFile”,即該ClassFile屬性的名稱為SourceFile,該屬性是一個可選的定長屬性,對于給定的ClassFile結構的attributes列表中不能有多于一個的SourceFile屬性;查閱SourceFile_attribute表可知,下面的4個字節為attribute_length項,其值為0x00000002,它表示在該項后面還有2個字節的信息。根據SourceFile_attribute表,最后的這兩個字節是sourcefile_index項,該項的值是一個對CONSTANT_Utf8_info結構的常量池表項的索引,其信息表示的是該Class文件的源文件名稱。在這里值為0x0030,根據上一篇博客(Java Class文件解析)的分析,第48號常量池表項存儲的信息可解析為“HelloWorld.java”,這是該Class文件的源文件名稱(不包括路徑)

           好了,到此為止,該Class文件的實例已經全部解析完畢,大功告成:)


     

    posted on 2008-02-03 13:03 獨孤求敗 閱讀(4864) 評論(33)  編輯  收藏 所屬分類: Java ByteCode

    評論:
    # re: 一個解析Java Class文件的實例 2008-02-03 13:55 | 小白鼠兒
    很暈很深奧  回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-02-03 14:14 | dennis
    很好的文章,有興趣研究這個的都會自己去找spec或者《inside jvm》
    不過現在能沉下心來讀你這篇文章的,我怕沒幾個  回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-02-03 15:22 | 久城
    很暈很迷糊......

    看來我還需要看很多東西才能消化它.....  回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-02-03 21:11 | 獨孤求敗
    @dennis

    呵呵...當然,我也是參考了這兩本書,在上一篇blog里已經說了,我只是整理了一下,并且加進去一些自己的理解而已:)
      回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-02-03 21:12 | 獨孤求敗
    @小白鼠兒

    找那兩本參考資料看看:)  回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-02-03 21:12 | 獨孤求敗
    @久城

    找那兩本參考資料看看:)  回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-02-12 14:43 | loocky
    值得研究一下看看,我相信不超過1%的人能都讀完  回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-02-14 14:18 | zhyiwww
    我覺得,解析了class文件,然后能更深入的引導我們去理解那些東西,是值得我們去深入思考的問題。  回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-02-15 02:14 | 獨孤求敗
    呵呵...真正需要的人會看完的:)
    @loocky
      回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-02-15 02:16 | 獨孤求敗
    你說得很對,這只是入門級的技術blog
    @zhyiwww
      回復  更多評論
      
    # re: 一個解析Java Class文件的實例[未登錄] 2008-02-15 10:00 | 屹礫
    如果要做JVM或者研究JVM原理的話,做這個研究還是蠻有意義的。  回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-02-15 10:11 | 獨孤求敗
    贊同啊~@屹礫
      回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-02-17 12:36 | 83bbb
    有點意思,  回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-03-21 21:33 | 王躍峰
    我就是仔細研究這個文章的人啊!
    很好,很有用。
    我最近也在解析class文件,就是不知道怎樣獲取類中的方法的參數名字。  回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-03-25 17:06 | 王躍峰
    不知道博主還在寫嗎?我研究了下,寫了程序要獲取到class中的method的參數名稱,目前只能得到方法描述.
    一般是這樣的
    create(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
    我現在想知道參數的名字啊,常量池中有這個名字的
    怎樣通過method_info聯系到哪個參數名稱?
    求老大賜教.mail:wang.yue.feng@163.com   回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-03-29 15:15 | 獨孤求敗
    可以查閱Java System API
    能解決的,Method,MethodDescriptor,ParameterDescriptor這三個類提供的方法應該就能解決你的問題:)
    @王躍峰
      回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-03-30 12:30 | 王躍峰
    謝謝大哥幫忙啊.

    我試了下,好象也不行啊,ParameterDescriptor類里面根本沒有表示參數名字的屬性和方法.

    另外,我也嘗試用了兩個分析class的工具jcfe和classediter,確實最后是按照classfile的文件結構解析的.但是在method_info中沒有參數名稱的跡象啊.參數類型這個是很明顯的.

    求教方法和它的參數是怎么對應的?

    最后,考慮method_info中的code屬性,這個里面不知能得到什么東西?  回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-03-30 16:27 | 王躍峰
    import java.beans.MethodDescriptor;
    import java.beans.ParameterDescriptor;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.lang.reflect.Method;
    public class FileClassLoader extends ClassLoader {
    public static final String drive = "d:/";
    public static final String fileType = ".class";

    public FileClassLoader() {
    super();
    }
    public FileClassLoader(ClassLoader arg0) {
    super(arg0);
    }
    public Class findClass(String name) {
    byte[] data = loadClassData(name);
    return defineClass(name, data, 0, data.length);
    }
    public byte[] loadClassData(String name) {
    FileInputStream fis = null;
    byte[] data = null;
    try {
    fis = new FileInputStream(new File(drive + name + fileType));

    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    int ch = 0;
    while ((ch = fis.read()) != -1) {
    baos.write(ch);
    }
    data = baos.toByteArray();
    } catch (IOException e) {
    e.printStackTrace();
    }
    return data;
    }
    public static void main(String[] args) throws Exception {
    FileClassLoader loader = new FileClassLoader();
    Class objClass = loader.loadClass("HelloWorld", true);

    Object obj = objClass.newInstance();

    Method[] methods=objClass.getMethods();
    for(Method m :methods)
    {
    System.out.println(m.getName());
    MethodDescriptor md = new MethodDescriptor(m);
    System.out.println(md.getParameterDescriptors());
    //這里得到的參數描述都是null
    //ParameterDescriptor[] PDS = md.getParameterDescriptors();
    System.out.println("************************");
    ///
    }
    }
    }

      回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-03-30 19:26 | 王躍峰
    上面代碼用的反射肯定是不行了,目前就是看對應class的反編譯結果,主要是不知道方法怎么和常量池里的參數名字聯系起來.  回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-04-03 00:22 | 獨孤求敗
    我寫了個程序驗證了下,是不能獲得方法的參數名字的...
    后來,我查看了常量池,里面好像沒有參數的名字啊,只有方法的參數類型描述,沒有名字。不知道你是用什么工具看到的?我是看不到:)
    我猜想,保存方法參數的名字也沒有意義啊,只需保存參數的類型就可以了,因為調用一個方法時,只需驗證其參數類型是否滿足即可!
    不值得,閣下想從class文中獲得方法的參數名字做什么用?@王躍峰
      回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-04-03 18:16 | 王躍峰
    謝謝老兄關注這個問題.
    我想先反編譯出原函數的樣子,然后考慮反編譯源代碼.
    常量池只要全部枚舉,有參數名字的,就是順序不一定是參數的順序了,我考慮了很久,目前主要思路還是停留在考慮method_info的code屬性的code里,我企圖從這著字節碼得到一些信息.
    我看過jad反編譯的代碼,只有這個工具可以看到反編譯函數的參數名字,我想在classfile里應該有個對應關系.

    @獨孤求敗
      回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-04-03 18:22 | 王躍峰
    關于枚舉常量池,我這里有網上一位大哥開發的一個源碼可以做到這一點.我也是企圖在這個源代碼上進一步研究.
    @獨孤求敗
      回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-04-03 22:27 | 獨孤求敗
    我用JAD試了一下,的確是你說的那樣的,但是方法的形參名字是存在了該方法的局部變量表里了:)

    現在,我知道怎么做了,方法的形參被看作局部變量了,所以你需要去查看該方法的局部變量表,如果該方法有形參的話,則局部變量表中的第一個元素就是第一個形參,第二個元素就是第二個形參,其他類推

    你從局部變量表中可以查到這個局部變量的名字是存在哪個常量池表項里的,直接從常量池中獲取即可!你沒有必要枚舉常量池的:)
    祝順利:)
    @王躍峰
      回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-04-07 13:58 | 王躍峰
    非常感謝!感覺這回應該很接近目的了.

    class文件結構中的method_info里面沒有局部變量表呀?

    怎樣得到它?@獨孤求敗
      回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-04-07 14:11 | 王躍峰
    每個method_info表都包含了與方法相關的一些信息,包括方法名和描述符(即方法的返回值及參數類型)。如果一個方法既非abstract也非native,那么method_info表則包含方法局部變量所需的棧空間長度、為方法所捕獲的異常表、字節碼序列以及可選的行號表和局部變量表等信息
    _______________________________________________________

    上面有些東西難理解,比如method_info表包含的棧空間長度、為方法所捕獲的異常表、字節碼序列以及可選的行號表和局部變量表等信息.

    我們知道method_info的結構是這樣的:
    method_info{
    u2 acces_flags;
    u2 name_index;
    u2 descriptor_index;
    u2 attributes_count;
    attribute_info attributes[attribute_count]
    };

    "方法所捕獲的異常表" 是對應異常屬性
    "字節碼序列" 是對應code屬性

    但是"棧空間長度"\"可選的行號表"\"局部變量表"沒有對應的結構啊.

    在  回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-04-08 12:43 |

    我已經知道了,局部變量表在code屬性的屬性里,謝謝你啊,有空一定常來看你的博客,非常感謝!

    @獨孤求敗
      回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-04-09 22:23 | 獨孤求敗
    呵呵...不客氣:)
    @王
      回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-05-07 18:09 | FutureBoy
    請教一下: 兩個字符串常量"Hello"和"World"存放在什么地方? CLASS文件中哪里有對它們的表述?   回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2008-05-14 16:53 | 獨孤求敗
    @FutureBoy
    在常量池里啊:)
    關于常量池的表述,請參考上一篇博客“Class文件格式解析 ”  回復  更多評論
      
    # re: 一個解析Java Class文件的實例[未登錄] 2009-04-13 17:32 | 阿飛
    太厲害了!  回復  更多評論
      
    # re: 一個解析Java Class文件的實例[未登錄] 2010-12-21 15:17 | lynn
    寫得太好了!高手。有機會的話希望能請教你些問題。  回復  更多評論
      
    # re: 一個解析Java Class文件的實例[未登錄] 2012-05-10 16:21 | wei
    寫的真好  回復  更多評論
      
    # re: 一個解析Java Class文件的實例 2013-02-03 10:07 | dereky
    好強大啊  回復  更多評論
      

    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    主站蜘蛛池模板: 亚洲乱码日产一区三区| 国产午夜无码视频免费网站| 亚洲精品影院久久久久久| 亚洲国产中文v高清在线观看| 国产高清免费视频| 免费国产成人α片| 在线观看肉片AV网站免费| 国产福利免费视频| 免费人成毛片动漫在线播放| 99国产精品免费观看视频| 精品久久久久久亚洲综合网| 亚洲国产女人aaa毛片在线| 久久久久亚洲AV无码专区首| 精品香蕉在线观看免费| 免费观看无遮挡www的小视频| 亚洲人成在线免费观看| 精品国产日韩亚洲一区91| 一区二区三区精品高清视频免费在线播放| 青草青草视频2免费观看| 久久成人免费播放网站| 8090在线观看免费观看| 和老外3p爽粗大免费视频| 亚洲aⅴ无码专区在线观看| 亚洲经典在线中文字幕| 精品久久8x国产免费观看| xxxx日本在线播放免费不卡| 精品亚洲成A人无码成A在线观看 | 精品国产呦系列在线观看免费| 亚洲国产成人精品久久| 亚洲精品成人无码中文毛片不卡| 免费无码黄动漫在线观看| 蜜臀98精品国产免费观看| 大地资源网高清在线观看免费| 在线观看免费亚洲| 亚洲久悠悠色悠在线播放| 色噜噜综合亚洲av中文无码| 亚洲中文字幕无码不卡电影| 国产精品成人免费综合| 成年轻人网站色免费看| 青娱乐免费视频在线观看| 91香焦国产线观看看免费|