深入理解Java Class文件格式(四)


前情回顧


在上一篇博客深入理解Java Class文件格式(三) 中, 介紹了常量池中的兩種類型的數據項, 分別是
  1. CONSTANT_Utf8_info
  2. CONSTANT_NameAndType_info 。
CONSTANT_Utf8_info中存儲了幾乎所有類型的字符串, 包括方法名, 字段名, 描述符等等。 而CONSTANT_NameAndType_info是方法符號引用或字段的符號引用的一部分, 也就是說, 如果在源文件中調用了一個方法, 或者引用了一個字段(不管是本類中的方法和字段, 還是引用其他類中的方法和字段), 那麼和這個方法或在字段相對應的CONSTANT_NameAndType_info 就會出現在常量池中。 注意, 只有引用了一個方法或字段, 常量池中才會存在和它對應的CONSTANT_NameAndType_info , 如果只在當前類中定義了一個字段而不訪問它, 或者定義了一個方法而不調用它, 那麼常量池中就不會出現對應的CONSTANT_NameAndType_info 數據項。 CONSTANT_NameAndType_info 中引用了兩個CONSTANT_Utf8_info, 一個叫做name_index, 存儲方法名或字段名, 一個叫做descriptor_index, 存儲方法描述符或字段描述符。 

關於這兩種常量池數據項的詳細介紹, 請參閱上一篇博客:深入理解Java Class文件格式(三) 。 關於虛擬機和class文件格式的前幾篇文章,已經收錄在我的博客專欄中, 如果想全面瞭解這些知識,建議按順序閱讀我的專欄中的博客, 專欄地址:


後續關於深入理解java的其他一些文章, 也會收錄在本專欄中, 歡迎關注和交流。 

下面我們繼續詳細講解常量池中的其他類型的數據項, 本文接着前幾篇文章寫的, 建議先讀前幾篇文章。   


常量池中各數據項類型詳解(續)


(3)CONSTANT_Integer_info


一個常量池中的CONSTANT_Integer_info數據項, 可以看做是CONSTANT_Integer類型的一個實例。 它存儲的是源文件中出現的int型數據的值。 同樣, 作爲常量池中的一種數據類型, 它的第一個字節也是一個tag值, 它的tag值爲3, 也就是說, 當虛擬機讀到一個tag值爲3的數據項時, 就知道這個數據項是一個CONSTANT_Integer_info, 它存儲的是int型數值的值。 緊挨着tag的下面4個字節叫做bytes, 就是int型數值的整型值。 它的內存佈局如下:


下面以示例代碼進行說明, 示例代碼如下:

package com.jg.zhang;

public class TestInt {
	
	void printInt(){
		System.out.println(65535);
	}
}

將上面的類生成的class文件反編譯:
D:\Workspace\AndroidWorkspace\BlogTest\bin>javap -v -c -classpath . com.jg.zhang.TestInt

下面列出反編譯的結果, 由於反編譯結果較長, 我們省略了大部分信息:

  ..................
  ..................


Constant pool:


   ..................
   ..................


  #21 = Integer            65535

   ..................
   ..................

{


     ..................
     ..................

  void printInt();
    flags:
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #21                 // int 65535
         5: invokevirtual #22                 // Method java/io/PrintStream.println:(I)V
         8: return
      LineNumberTable:
        line 6: 0
        line 7: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       9     0  this   Lcom/jg/zhang/TestInt;
}

上面的輸出結果中, 保留了printInt方法的反編譯結果, 並且保留了常量池中的第21項。 首先看printInt方法反編譯結果中的索引爲3 的字節碼指令:

3: ldc           #21                 // int 65535

這條ldc指令, 引用了常量池中的第21項, 而第21項是一個CONSTANT_Integer_info, 並且這個CONSTANT_Integer_info存儲的整型值爲65535 。 


(4)CONSTANT_Float_info


一個常量池中的CONSTANT_Float_info數據項, 可以看做是CONSTANT_Float類型的一個實例。 它存儲的是源文件中出現的float型數據的值。 同樣, 作爲常量池中的一種數據類型, 它的第一個字節也是一個tag值, 它的tag值爲4, 也就是說, 當虛擬機讀到一個tag值爲4的數據項時, 就知道這個數據項是一個CONSTANT_Float_info, 並且知道它存儲的是float型數值。 緊挨着tag的下面4個字節叫做bytes, 就是float型的數值。 它的內存佈局如下:


舉例說明, 如果源文件中的一句代碼使用了一個float值, 如下所示:
	void printFloat(){
		System.out.println(1234.5f);
	}

那麼在這個類的常量池中就會有一個CONSTANT_Float_info與之相對應, 這個CONSTANT_Float_info的形式如下:



代碼反編譯結果如下:
Constant pool:

.............
.............

  #29 = Float              1234.5f

............
............

{

............
............

  void printFloat();
    flags:
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #29                 // float 1234.5f
         5: invokevirtual #30                 // Method java/io/PrintStream.println:(F)V
         8: return
      LineNumberTable:
        line 10: 0
        line 11: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       9     0  this   Lcom/jg/zhang/TestInt;
}


(5)CONSTANT_Long_info


一個常量池中的CONSTANT_Long_info數據項, 可以看做是CONSTANT_Long類型的一個實例。 它存儲的是源文件中出現的long型數據的值。 同樣, 作爲常量池中的一種數據類型, 它的第一個字節也是一個tag值, 它的tag值爲5, 也就是說, 當虛擬機讀到一個tag值爲5的數據項時, 就知道這個數據項是一個CONSTANT_Long_info, 並且知道它存儲的是long型數值。 緊挨着tag的下面8個字節叫做bytes, 就是long型的數值。 它的內存佈局如下:


舉例說明, 如果源文件中的一句代碼使用了一個long型的數值, 如下所示:
	void printLong(){
		System.out.println(123456L);
	}

那麼在這個類的常量池中就會有一個CONSTANT_Long_info與之相對應, 這個CONSTANT_Long_info的形式如下:


代碼反編譯結果爲:
Constant pool:

..............
..............

  #21 = Long               123456l

..............
..............

{

..............
..............

  void printLong();
    flags:
    Code:
      stack=3, locals=1, args_size=1
         0: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc2_w        #21                 // long 123456l
         6: invokevirtual #23                 // Method java/io/PrintStream.println:(J)V
         9: return
      LineNumberTable:
        line 7: 0
        line 8: 9
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      10     0  this   Lcom/jg/zhang/TestInt;
}


(6)CONSTANT_Double_info


一個常量池中的CONSTANT_Double_info數據項, 可以看做是CONSTANT_Double類型的一個實例。 它存儲的是源文件中出現的double型數據的值。 同樣, 作爲常量池中的一種數據類型, 它的第一個字節也是一個tag值, 它的tag值爲6, 也就是說, 當虛擬機讀到一個tag值爲6的數據項時, 就知道這個數據項是一個CONSTANT_Double_info, 並且知道它存儲的是double型數值。 緊挨着tag的下面8個字節叫做bytes, 就是double型的數值。 它的內存佈局如下:


舉例說明, 如果源文件中的一句代碼使用了一個double型的數值, 如下所示:
	void printDouble(){
		System.out.println(123456D);
	}


那麼在這個類的常量池中就會有一個CONSTANT_Double_info與之相對應, 這個CONSTANT_Double_info的形式如下:


代碼反編譯結果爲:
Constant pool:

..............
..............

  #21 = Double             123456.0d

..............
..............

{

..............
..............

  void printDouble();
    flags:
    Code:
      stack=3, locals=1, args_size=1
         0: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc2_w        #21                 // double 123456.0d
         6: invokevirtual #23                 // Method java/io/PrintStream.println:(D)V
         9: return
      LineNumberTable:
        line 7: 0
        line 8: 9
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      10     0  this   Lcom/jg/zhang/TestInt;
}


(7) CONSTANT_String_info


在常量池中, 一個CONSTANT_String_info數據項, 是CONSTANT_String類型的一個實例。 它的作用是存儲文字字符串, 可以把他看做是一個存在於class文件中的字符串對象。 同樣, 它的第一個字節是tag值, 值爲8 , 也就是說, 虛擬機訪問一個數據項時, 判斷tag值爲8 , 就說明訪問的數據項是一個CONSTANT_String_info 。 緊挨着tag的後兩個字節是一個叫做string_index的常量池引用, 它指向一個CONSTANT_Utf8_info, 這個CONSTANT_Utf8_info存放的纔是字符串的字面量。 它的內存佈局如下:



舉例說明, 如果源文件中的一句代碼使用了一個字符串常量, 如下所示:
	void printStrng(){
		System.out.println("abcdef");
	}

那麼在這個類的常量池中就會有一個CONSTANT_String_info與之相對應, 反編譯結果如下:
Constant pool:

..............
..............
  
  #21 = String             #22            //  abcdef
  #22 = Utf8               abcdef

..............
..............

{

..............
..............

  void printStrng();
    flags:
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #21                 // String abcdef
         5: invokevirtual #23                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 7: 0
        line 8: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       9     0  this   Lcom/jg/zhang/TestInt;
}

其中printString方法中索引爲3的字節碼指令ldc引用常量池中的第21項, 第21項是一個CONSTANT_String_info, 這個位於第21項的CONSTANT_String_info又引用了常量池的第22項, 第22項是一個CONSTANT_Utf8_info, 這個CONSTANT_Utf8_info中存儲的字符串是 abcdef 。 引用關係的內存佈局如下:




總結


本文就到此爲止。 最後總結一下, 本文主要講解了常量池中的五中數據項, 分別爲CONSTANT_Integer_info, CONSTANT_Float_info, CONSTANT_Long_info, CONSTANT_Double_info 和CONSTANT_String_info 。 這幾種常量池數據項都是直接存儲的常量值,而不是符號引用。 這裏又一次出現了符號引用的概念, 這個概念將會在下一篇博客中詳細講解, 因爲下一篇博客要介紹的剩下的四種常量池數據項, 都是符號引用, 這四種表示符號引用的數據項又會直接或間接引用上篇文章中介紹的CONSTANT_NameAndType_info和CONSTANT_Utf8_info, 所以說CONSTANT_NameAndType_info是符號引用的一部分。 

從本文中我們還可以知道。 雖然說CONSTANT_String_info是直接存儲值的數據項, 但是CONSTANT_String_info有點特別, 因爲它不是直接存儲字符串, 而是引用了一個CONSTANT_Utf8_info, 這個被引用的CONSTANT_Utf8_info中存儲了字符串。 

最後, 列出下一篇博文要介紹的剩下的四種常量池數據項:
  1. CONSTANT_Class_info
  2. CONSTANT_Fieldref_info
  3. CONSTANT_Methodref_info
  4. CONSTANT_InterfaceMethodref_info



更多關於深入理解Java的文章, 請關注我的專欄 : http://blog.csdn.net/column/details/zhangjg-java-blog.html

更多關於Java和Android等其他技術的文章, 請關注我的博客: http://blog.csdn.net/zhangjg_blog



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