Class文件詳解(二)

前情回顧

在上一章我們介紹了jvm、class文件和class文件中的魔數、版本號和常量池計數值(見Class文件結構(一))。本章我們來具體介紹常量池。

 

class文件特殊字符串

在介紹常量池之前,我們得先介紹一下在class文件中出場率較高的一些特殊字符串。

1、簡單名稱

沒有類型和參數修飾的方法或字段名稱

2、全限定名

假設一個類的全名是com.example.demo,那麼它在class文件中的全限定名就是將"."替換爲"/"。例如Object的類全名爲java.lang.Object,那麼它的全限定名就是java/lang/Object

3、描述符

描述符得分爲3種類型的描述符

(1)基本類型的描述符

                      基本數據類型和void類型               類型對應字符
                                 byte                      B
                                 char                      C
                                double                      D
                                 float                      F
                                  int                      I
                                  long                      J
                                 short                      S
                               boolean                      Z
                                  void                      V

只要記住除了long對應J和boolean對應Z以外,其他都是對應自己的大寫字母

(2)引用類型(類和接口)的描述符

"L"+類型低的全限定名+";"

比如Object的描述符是

Ljava/lang/Object;

com.example.demo對應的就是

Lcom/example/demo;

(3)數組類型的描述符

數組類型中的每一個維度都用一個"["表示,因此整個類型對應字符串就是

若干個"["+數組中元素類型對應的字符串

比如Object[][]的描述符是

[[Ljava/lang/Object;

(4)方法描述符

格式:

(參數類型1參數類型2...)返回值類型

                 方法描述符                             方法聲明                 
                 ()I                       int getSize()
                 ()Ljava/lang/String;                    String toString()
                 ([Ljava/lang/String;)V                    void  main(String[] args)
                 ([BII)I                     int  read(byte[]  b,int  off,int  len)    

常量池

常量池是Class文件中的資源倉庫,主要存放兩大類常量,分別是字面量符號引用。字面量大家可以類比爲Java語言層面的常量,而符號引用則包括下面三類常量:

  • 類和接口的全限定名
  • 字段的名稱和描述符
  • 方法的名稱和描述符

                                         

           類型                           名稱                              數量
            u2             constant_pool_count                                 1
          cp_info                  constant_pool              constant_pool_count-1

如上表所示,常量池由一個無符號數和一張表組成。無符號數constant_pool_count代表常量池容量計數值,是常量池入口參數,由於該數是從1開始計數,因此常量池實際數量是該數-1。接下來的就是常量池中的具體數據項了。

常量池的項目類型
                                                    類型 標誌 描述
                                        CONSTANT_Utf8_info 1 UTF-8編碼的字符串
                                        CONSTANT_Integer_info 3 整型字面量
                                        CONSTANT_Float_info 4 浮點型字面量
                                        CONSTANT_Long_info 5 長整型字面量
                                        CONSTANT_Double_info 6 雙精度浮點型字面量
                                        CONSTANT_Class_info 7 類或接口的符號引用
                                        CONSTANT_String_info 8 字符串類型字面量
                                        CONSTANT_Fieldref_info 9 字段的符號引用
                                        CONSTANT_Methodref_info 10 類中方法的的符號引用
                                        CONSTANT_InterfaceMethodref_info 11 接口中方法的符號引用
                                        CONSTANT_NameAndType_info 12 字段或方法的部分符號引用
                                        CONSTANT_MethodHandle_info 15 表示方法句柄
                                        CONSTANT_MethodType_info 16 標識方法句柄
                                        CONSTANT_InvokeDynamic_info 18 表示一個動態方法調用點

上表中表示了常量池中的14種不同類型的常量,這十四種常量各自都有自己的結構。


CONSTANT_Utf8_info的結構:

CONSTANT_Utf8_info的結構
                   類型               名稱        數量
                    u1                 tag              1
                    u2                length              1
                    u1                 bytes           length

tag是標誌位,用於標誌該常量的類型,比如該常量爲CONSTANT_Utf8_info,它的tag值就爲1,CONSTANT_Class_info就爲7。

length表明該字符串的長度是多少字節,而length字節後的連續數據是一個字符串。下面給出一個實例說明該常量的結構。

Comments.java

package com.zust.bean;

public class Comments {
    private String comments_no;
    private String comments_name;
    private String comments_context;
    private String news_no;

    public String getComments_name() {
        return comments_name;
    }

    public void setComments_name(String comments_name) {
        this.comments_name = comments_name;
    }

    public String getComments_context() {
        return comments_context;
    }

    public void setComments_context(String comments_context) {
        this.comments_context = comments_context;
    }

    public String getNews_no() {
        return news_no;
    }

    public void setNews_no(String news_no) {
        this.news_no = news_no;
    }

    public String getComments_no() {
        return comments_no;
    }

    public void setComments_no(String comments_no) {
        this.comments_no = comments_no;
    }
}

用winhex打開Comments.class

第9個和第10個字節說明常量池中有38個常量,我們從第11個字節開始,這個字節是07,表明這個常量是CONSTANT_Class_info,這個類型的常量有3個字節,我們先跳過這個常量(因爲還沒介紹過它的結構QAQ),我們來到第14個字節,它是01,表明這個常量是CONSTANT_Utf8_info,接下來的2個字節是00 16說明它的長度是22,於是我們可以確定從第17個字節開始到第38個字節中儲存的是我們想要的字符串。

這裏用藍色字符標記了第17-38的字節,我們在右側可以看到這個字符串是com/zust/bean/Comments,是這個類的全限定名,可以驗證上述推論是正確的。


下面我們介紹一個符號引用的常量:

CONSTANT_Class_info結構:

CONSTANT_Class_info型常量的結構
              類型                  名稱             數量
                u1                    tag               1
                u2                  name_index               1

tag和CONSTANT_Utf8_info中的一樣都是標誌位。name_index是一個索引值,表示指向常量池中的第幾個常量,由於當前常量是類或接口的符號引用,所以name_index是指向一個字符串,而常量池中存放字符串的就是CONSTANT_Utf8_info,所以這裏的name_index實際上就是指向CONSTANT_Utf8_info的。

下面我們用一個專門分析Class文件字節碼的工具javap來分析上文中的Comments.class。

我們敲入javap -verbose Comments.class命令,得到以下內容,下面只截取了部分需要用到的。

 Last modified 2019-7-4; size 1166 bytes
  MD5 checksum f7b9e128edcf563d8216d36602fda343
  Compiled from "Comments.java"
public class com.zust.bean.Comments
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Class              #2             // com/zust/bean/Comments
   #2 = Utf8               com/zust/bean/Comments
   #3 = Class              #4             // java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               comments_no
   #6 = Utf8               Ljava/lang/String;
   #7 = Utf8               comments_name
   #8 = Utf8               comments_context
   #9 = Utf8               news_no
  #10 = Utf8               <init>
  #11 = Utf8               ()V
  #12 = Utf8               Code
  #13 = Methodref          #3.#14         // java/lang/Object."<init>":()V
  #14 = NameAndType        #10:#11        // "<init>":()V
  #15 = Utf8               LineNumberTable
  #16 = Utf8               LocalVariableTable
  #17 = Utf8               this
  #18 = Utf8               Lcom/zust/bean/Comments;
  #19 = Utf8               getComments_name
  #20 = Utf8               ()Ljava/lang/String;
  #21 = Fieldref           #1.#22         // com/zust/bean/Comments.comments_name:Ljava/lang/String;
  #22 = NameAndType        #7:#6          // comments_name:Ljava/lang/String;
  #23 = Utf8               setComments_name
  #24 = Utf8               (Ljava/lang/String;)V
  #25 = Utf8               getComments_context
  #26 = Fieldref           #1.#27         // com/zust/bean/Comments.comments_context:Ljava/lang/String;
  #27 = NameAndType        #8:#6          // comments_context:Ljava/lang/String;
  #28 = Utf8               setComments_context
  #29 = Utf8               getNews_no
  #30 = Fieldref           #1.#31         // com/zust/bean/Comments.news_no:Ljava/lang/String;
  #31 = NameAndType        #9:#6          // news_no:Ljava/lang/String;
  #32 = Utf8               setNews_no
  #33 = Utf8               getComments_no
  #34 = Fieldref           #1.#35         // com/zust/bean/Comments.comments_no:Ljava/lang/String;
  #35 = NameAndType        #5:#6          // comments_no:Ljava/lang/String;
  #36 = Utf8               setComments_no
  #37 = Utf8               SourceFile
  #38 = Utf8               Comments.java

前情回顧

在上一章我們介紹了jvm、class文件和class文件中的魔數、版本號和常量池計數值。本章我們來具體介紹常量池。

 

class文件特殊字符串

在介紹常量池之前,我們得先介紹一下在class文件中出場率較高的一些特殊字符串。

1、簡單名稱

沒有類型和參水修飾的方法或字段名稱

2、全限定名

假設一個類的全名是com.example.demo,那麼它在class文件中的全限定名就是將"."替換爲"/"。例如Object的類全名爲java.lang.Object,那麼它的全限定名就是java/lang/Object

3、描述符

描述符得分爲3種類型的描述符

(1)基本類型的描述符

                      基本數據類型和void類型               類型對應字符
                                 byte                      B
                                 char                      C
                                double                      D
                                 float                      F
                                  int                      I
                                  long                      J
                                 short                      S
                               boolean                      Z
                                  void                      V

只要記住除了long對應J和boolean對應Z以外,其他都是對應自己的大寫字母

(2)引用類型(類和接口)的描述符

"L"+類型低的全限定名+";"

比如Object的描述符是

Ljava/lang/Object;

com.example.demo對應的就是

Lcom/example/demo;

(3)數組類型的描述符

數組類型中的每一個維度都用一個"["表示,因此整個類型對應字符串就是

若干個"["+數組中元素類型對應的字符串

比如Object[][]的描述符是

[[Ljava/lang/Object;

(4)方法描述符

格式:

(參數類型1參數類型2...)返回值類型

                 方法描述符                             方法聲明                 
                 ()I                       int getSize()
                 ()Ljava/lang/String;                    String toString()
                 ([Ljava/lang/String;)V                    void  main(String[] args)
                 ([BII)I                     int  read(byte[]  b,int  off,int  len)    

常量池

常量池是Class文件中的資源倉庫,主要存放兩大類常量,分別是字面量符號引用。字面量大家可以類比爲Java語言層面的常量,而符號引用則包括下面三類常量:

  • 類和接口的全限定名
  • 字段的名稱和描述符
  • 方法的名稱和描述符

                                         

           類型                           名稱                              數量
            u2             constant_pool_count                                 1
          cp_info                  constant_pool              constant_pool_count-1

如上表所示,常量池由一個無符號數和一張表組成。無符號數constant_pool_count代表常量池容量計數值,是常量池入口參數,由於該數是從1開始計數,因此常量池實際數量是該數-1。接下來的就是常量池中的具體數據項了。

常量池的項目類型
                                                    類型 標誌 描述
                                        CONSTANT_Utf8_info 1 UTF-8編碼的字符串
                                        CONSTANT_Integer_info 3 整型字面量
                                        CONSTANT_Float_info 4 浮點型字面量
                                        CONSTANT_Long_info 5 長整型字面量
                                        CONSTANT_Double_info 6 雙精度浮點型字面量
                                        CONSTANT_Class_info 7 類或接口的符號引用
                                        CONSTANT_String_info 8 字符串類型字面量
                                        CONSTANT_Fieldref_info 9 字段的符號引用
                                        CONSTANT_Methodref_info 10 類中方法的的符號引用
                                        CONSTANT_InterfaceMethodref_info 11 接口中方法的符號引用
                                        CONSTANT_NameAndType_info 12 字段或方法的部分符號引用
                                        CONSTANT_MethodHandle_info 15 表示方法句柄
                                        CONSTANT_MethodType_info 16 標識方法句柄
                                        CONSTANT_InvokeDynamic_info 18 表示一個動態方法調用點

上表中表示了常量池中的14種不同類型的常量,這十四種常量各自都有自己的結構。


CONSTANT_Utf8_info的結構:

CONSTANT_Utf8_info的結構
                   類型               名稱        數量
                    u1                 tag              1
                    u2                length              1
                    u1                 bytes           length

tag是標誌位,用於標誌該常量的類型,比如該常量爲CONSTANT_Utf8_info,它的tag值就爲1,CONSTANT_Class_info就爲7。

length表明該字符串的長度是多少字節,而length字節後的連續數據是一個字符串。下面給出一個實例說明該常量的結構。

Comments.java

package com.zust.bean;

public class Comments {
    private String comments_no;
    private String comments_name;
    private String comments_context;
    private String news_no;

    public String getComments_name() {
        return comments_name;
    }

    public void setComments_name(String comments_name) {
        this.comments_name = comments_name;
    }

    public String getComments_context() {
        return comments_context;
    }

    public void setComments_context(String comments_context) {
        this.comments_context = comments_context;
    }

    public String getNews_no() {
        return news_no;
    }

    public void setNews_no(String news_no) {
        this.news_no = news_no;
    }

    public String getComments_no() {
        return comments_no;
    }

    public void setComments_no(String comments_no) {
        this.comments_no = comments_no;
    }
}

用winhex打開Comments.class

第9個和第10個字節說明常量池中有38個常量,我們從第11個字節開始,這個字節是07,表明這個常量是CONSTANT_Class_info,這個類型的常量有3個字節,我們先跳過這個常量(因爲還沒介紹過它的結構QAQ),我們來到第14個字節,它是01,表明這個常量是CONSTANT_Utf8_info,接下來的2個字節是00 16說明它的長度是22,於是我們可以確定從第17個字節開始到第38個字節中儲存的是我們想要的字符串。

這裏用藍色字符標記了第17-38的字節,我們在右側可以看到這個字符串是com/zust/bean/Comments,是這個類的全限定名,可以驗證上述推論是正確的。


下面我們介紹一個符號引用的常量:

CONSTANT_Class_info結構:

CONSTANT_Class_info型常量的結構
              類型                  名稱             數量
                u1                    tag               1
                u2                  name_index               1

tag和CONSTANT_Utf8_info中的一樣都是標誌位。name_index是一個索引值,表示指向常量池中的第幾個常量,由於當前常量是類或接口的符號引用,所以name_index是指向一個字符串,而常量池中存放字符串的就是CONSTANT_Utf8_info,所以這裏的name_index實際上就是指向CONSTANT_Utf8_info的。

下面我們用一個專門分析Class文件字節碼的工具javap來分析上文中的Comments.class。

我們敲入javap -verbose Comments.class命令,得到以下內容,下面只截取了部分需要用到的。

 

 Last modified 2019-7-4; size 1166 bytes
  MD5 checksum f7b9e128edcf563d8216d36602fda343
  Compiled from "Comments.java"
public class com.zust.bean.Comments
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Class              #2             // com/zust/bean/Comments
   #2 = Utf8               com/zust/bean/Comments
   #3 = Class              #4             // java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               comments_no
   #6 = Utf8               Ljava/lang/String;
   #7 = Utf8               comments_name
   #8 = Utf8               comments_context
   #9 = Utf8               news_no
  #10 = Utf8               <init>
  #11 = Utf8               ()V
  #12 = Utf8               Code
  #13 = Methodref          #3.#14         // java/lang/Object."<init>":()V
  #14 = NameAndType        #10:#11        // "<init>":()V
  #15 = Utf8               LineNumberTable
  #16 = Utf8               LocalVariableTable
  #17 = Utf8               this
  #18 = Utf8               Lcom/zust/bean/Comments;
  #19 = Utf8               getComments_name
  #20 = Utf8               ()Ljava/lang/String;
  #21 = Fieldref           #1.#22         // com/zust/bean/Comments.comments_name:Ljava/lang/String;
  #22 = NameAndType        #7:#6          // comments_name:Ljava/lang/String;
  #23 = Utf8               setComments_name
  #24 = Utf8               (Ljava/lang/String;)V
  #25 = Utf8               getComments_context
  #26 = Fieldref           #1.#27         // com/zust/bean/Comments.comments_context:Ljava/lang/String;
  #27 = NameAndType        #8:#6          // comments_context:Ljava/lang/String;
  #28 = Utf8               setComments_context
  #29 = Utf8               getNews_no
  #30 = Fieldref           #1.#31         // com/zust/bean/Comments.news_no:Ljava/lang/String;
  #31 = NameAndType        #9:#6          // news_no:Ljava/lang/String;
  #32 = Utf8               setNews_no
  #33 = Utf8               getComments_no
  #34 = Fieldref           #1.#35         // com/zust/bean/Comments.comments_no:Ljava/lang/String;
  #35 = NameAndType        #5:#6          // comments_no:Ljava/lang/String;
  #36 = Utf8               setComments_no
  #37 = Utf8               SourceFile
  #38 = Utf8               Comments.java

                                                                                          圖(一)

 

                                                                             圖(二)

圖(一)和圖(二)中的#+數字代表第幾個常量,我們可以看到第一個常量是Class,是類的符號引用,字節是 07 00 02,意爲指向第二個常量,因此在圖(一)中後方會跟上一個#2,後面的註釋就是它指向的字符串的字面量。第二個常量是utf8類型,圖二中用#2標註了出來。第三個依然是Class類型,07 00 04,指向第四個常量,第四個是utf8...,依此類推即可。


常量池中的常量類型我們已經介紹了兩個,其餘的類型我們皆可通過類似的方法推算,不再詳述,下面給出常量池中的14種常量項的結構總表。

 

 

總結

本章詳述了Class文件中常用的特殊字符串、常量池中的兩類數據項CONSTANT_utf8_info和CONSTANT_Class_info。下篇博客我們將講述Class文件的訪問標誌,類索引、父類索引和接口索引。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章