小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

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

 看風(fēng)景D人 2016-10-07


前情回顧


本專欄的前幾篇博文, 對class文件中的常量池進(jìn)行了詳細(xì)的解釋。 前文講解了常量池中的7種數(shù)據(jù)項(xiàng), 它們分別是:

  1. CONSTANT_Utf8_info
  2. CONSTANT_NameAndType_info
  3. CONSTANT_Integer_info
  4. CONSTANT_Float_info
  5. CONSTANT_Long_info
  6. CONSTANT_Double_info
  7. CONSTANT_String_info

關(guān)于這七種數(shù)據(jù)項(xiàng), 前面的文章已經(jīng)講得很詳細(xì)了, 不了解的同學(xué)請先參閱前面的博文。 此外, 如果想要全面的了解JVM和Class文件格式, 建議按順序閱讀我專欄中的博客。此外,本文是建立在前幾篇博客的基礎(chǔ)之上的, 是接著前面的的博客寫的,所以, 同樣建議先閱讀專欄前面的博客, 以保證知識的完整性。 專欄地址:


 更多關(guān)于深入理解java的文章會陸續(xù)收錄到該專欄中, 歡迎關(guān)注, 歡迎交流。 


常量池中各數(shù)據(jù)項(xiàng)類型詳解(續(xù))



(8) CONSTANT_Class_info


常量池中的一個CONSTANT_Class_info, 可以看做是CONSTANT_Class數(shù)據(jù)類型的一個實(shí)例。 他是對類或者接口的符號引用。 它描述的可以是當(dāng)前類型的信息, 也可以描述對當(dāng)前類的引用, 還可以描述對其他類的引用。 也就是說, 如果訪問了一個類字段, 或者調(diào)用了一個類的方法, 對這些字段或方法的符號引用, 必須包含它們所在的類型的信息, CONSTANT_Class_info就是對字段或方法符號引用中類型信息的描述。 


CONSTANT_Class_info的第一個字節(jié)是tag, 值為7, 也就是說, 當(dāng)虛擬機(jī)訪問到一個常量池中的數(shù)據(jù)項(xiàng), 如果發(fā)現(xiàn)它的tag值為7, 就可以判斷這是一個CONSTANT_Class_info 。 tag下面的兩個字節(jié)是一個叫做name_index的索引值, 它指向一個CONSTANT_Utf8_info, 這個CONSTANT_Utf8_info中存儲了CONSTANT_Class_info要描述的類型的全限定名。 全限定名的概念在前面的博文 深入理解Java Class文件格式(二) 中將結(jié)果, 不熟悉的同學(xué)可以先閱讀這篇文章。  

此外要說明的是, java中數(shù)組變量也是對象, 那么數(shù)組也就有相應(yīng)的類型, 并且數(shù)組的類型也是使用CONSTANT_Class_info描述的, 并且數(shù)組類型和普通類型的描述有些區(qū)別。 普通類型的CONSTANT_Class_info中存儲的是全限定名, 而數(shù)組類型對應(yīng)的CONSTANT_Class_info中存儲的是數(shù)組類型相對應(yīng)的描述符字符串。 舉例說明:

與Object類型對應(yīng)的CONSTANT_Class_info中存儲的是: java/lang/Object 
與Object[]類型對應(yīng)的CONSTANT_Class_info中存儲的是: [Ljava/lang/Object; 


下面看CONSTANT_Class_info的存儲布局:



例如, 如果在一個類中引用了System這個類, 那么就會在這個類的常量池中出現(xiàn)以下信息:

 




(9) CONSTANT_Fieldref_info


常量池中的一個CONSTANT_Fieldref_info, 可以看做是CONSTANT_Field數(shù)據(jù)類型的一個實(shí)例。 該數(shù)據(jù)項(xiàng)表示對一個字段的符號引用, 可以是對本類中的字段的符號引用, 也可以是對其他類中的字段的符號引用, 可以是對成員變量字段的符號引用, 也可以是對靜態(tài)變量的符號引用, 其中ref三個字母就是reference的簡寫。 之前的文章中, “符號引用”這個名詞出現(xiàn)了很多次, 可能有的同學(xué)一直不是很明白, 等介紹完CONSTANT_Fieldref_info, 就可以很清晰的了解什么是符號引用。 下面分析CONSTANT_Fieldref_info中的內(nèi)容都存放了什么信息。 

和其他類型的常量池?cái)?shù)據(jù)項(xiàng)一樣, 它的第一個字節(jié)也必然是tag, 它的tag值為9 。 也就是說, 當(dāng)虛擬機(jī)訪問到一個常量池中的一項(xiàng)數(shù)據(jù), 如果發(fā)現(xiàn)這個數(shù)據(jù)的tag值為9, 就可以確定這個被訪問的數(shù)據(jù)項(xiàng)是一個CONSTANT_Fieldref_info, 并且知道這個數(shù)據(jù)項(xiàng)表示對一個字段的符號引用。 

tag值下面的兩個字節(jié)是一個叫做class_index的索引值, 它指向一個CONSTANT_Class_info數(shù)據(jù)項(xiàng), 這個數(shù)據(jù)項(xiàng)表示被引用的字段所在的類型, 包括接口。 所以說, CONSTANT_Class_info可以作為字段符號引用的一部分。 

class_index以下的兩個字節(jié)是一個叫做name_and_type_index的索引, 它指向一個CONSTANT_NameAndType_info, 這個CONSTANT_NameAndType_info前面的博客中已經(jīng)解釋過了, 不明白的朋友可以先看前面的博客:深入理解Java Class文件格式(三) 。 這個CONSTANT_NameAndType_info描述的是被引用的字段的名稱和描述符。 我們在前面的博客中也提到過, CONSTANT_NameAndType_info可以作為字段符號引用的一部分。

到此, 我們可以說, CONSTANT_Fieldref_info就是對一個字段的符號引用, 這個符號引用包括兩部分, 一部分是該字段所在的類, 另一部分是該字段的字段名和描述符。 這就是所謂的 “對字段的符號引用” 。

下面結(jié)合實(shí)際代碼來說明, 代碼如下:

  1. package com.jg.zhang;  
  2.   
  3. public class TestInt {  
  4.     int a = 10;  
  5.     void print(){  
  6.         System.out.println(a);  
  7.     }  
  8. }  

在print方法中, 引用了本類中的字段a。 代碼很簡單, 我們一眼就可以看到print方法中是如何引用本類中定義的字段a的。 那么在class文件中, 對字段a的引用是如何描述的呢? 下面我們將這段代碼使用javap反編譯, 給出簡化后的反編譯結(jié)果:

[plain] view plain copy
在CODE上查看代碼片派生到我的代碼片
  1. Constant pool:  
  2.    #1 = Class              #2             //  com/jg/zhang/TestInt  
  3.    #2 = Utf8               com/jg/zhang/TestInt  
  4.   
  5.    ......  
  6.   
  7.    #5 = Utf8               a  
  8.    #6 = Utf8               I  
  9.   
  10.    ......  
  11.   
  12.   #12 = Fieldref           #1.#13         //  com/jg/zhang/TestInt.a:I  
  13.   #13 = NameAndType        #5:#6          //  a:I  
  14.   
  15.   ......  
  16.   
  17. {  
  18.   
  19.   void print();  
  20.     flags:  
  21.     Code:  
  22.       stack=2, locals=1, args_size=1  
  23.          0: getstatic     #19                 // Field java/lang/System.out:Ljava/io/PrintStream;  
  24.          3: aload_0  
  25.          4: getfield      #12                 // Field a:I  
  26.          7: invokevirtual #25                 // Method java/io/PrintStream.println:(I)V  
  27.         10: return  
  28. }  



可以看到, print方法的位置為4的字節(jié)碼指令getfield引用了索引為12的常量池?cái)?shù)據(jù)項(xiàng), 常量池中索引為12的數(shù)據(jù)項(xiàng)是一個CONSTANT_Fieldref_info, 這個CONSTANT_Fieldref_info又引用了索引為1和13的兩個數(shù)據(jù)項(xiàng), 索引為1的數(shù)據(jù)項(xiàng)是一個CONSTANT_Class_info, 這個CONSTANT_Class_info數(shù)據(jù)項(xiàng)又引用了索引為2的數(shù)據(jù)項(xiàng), 索引為2的數(shù)據(jù)項(xiàng)是一個CONSTANT_Utf8_info , 他存儲了字段a所在的類的全限定名com/jg/zhang/TestInt 。 而CONSTANT_Fieldref_info所引用的索引為13的數(shù)據(jù)項(xiàng)是一個CONSTANT_NameAndType_info, 它又引用了兩個數(shù)據(jù)項(xiàng), 分別為第5項(xiàng)和第6項(xiàng), 這是兩個CONSTANT_Utf8_info , 分別存儲了字段a的字段名a, 和字段a的描述符I 。 

下面給出內(nèi)存布局圖, 這個圖中涉及的東西有點(diǎn)多, 因?yàn)镃ONSTANT_Fieldref_info引用了CONSTANT_Class_info和CONSTANT_NameAndType_info, CONSTANT_Class_info又引用了一個CONSTANT_Utf8_info , 而CONSTANT_NameAndType_info又引用了兩個CONSTANT_Utf8_info 。 





(10) CONSTANT_Methodref_info


常量池中的一個CONSTANT_Methodref_info, 可以看做是CONSTANT_Methodref數(shù)據(jù)類型的一個實(shí)例。 該數(shù)據(jù)項(xiàng)表示對一個類中方法的符號引用, 可以是對本類中的方法的符號引用, 也可以是對其他類中的方法的符號引用, 可以是對成員方法字段的符號引用, 也可以是對靜態(tài)方法的符號引用,但是不會是對接口中的方法的符號引用。 其中ref三個字母就是reference的簡寫。 在上一小節(jié)中介紹了CONSTANT_Fieldref_info, 它是對字段的符號引用, 本節(jié)中介紹的CONSTANT_Methodref_info和CONSTANT_Fieldref_info很相似。既然是符號“引用”, 那么只有在原文件中調(diào)用了一個方法, 常量池中才有和這個被調(diào)用方法的相對應(yīng)的符號引用, 即存在一個CONSTANT_Methodref_info。 如果只是在類中定義了一個方法, 但是沒調(diào)用它, 則不會在常量池中出現(xiàn)和這個方法對應(yīng)的CONSTANT_Methodref_info 。 

和其他類型的常量池?cái)?shù)據(jù)項(xiàng)一樣, 它的第一個字節(jié)也必然是tag, 它的tag值為10 。 也就是說, 當(dāng)虛擬機(jī)訪問到一個常量池中的一項(xiàng)數(shù)據(jù), 如果發(fā)現(xiàn)這個數(shù)據(jù)的tag值為10, 就可以確定這個被訪問的數(shù)據(jù)項(xiàng)是一個CONSTANT_Methodref_info, 并且知道這個數(shù)據(jù)項(xiàng)表示對一個方法的符號引用。 

tag值下面的兩個字節(jié)是一個叫做class_index的索引值, 它指向一個CONSTANT_Class_info數(shù)據(jù)項(xiàng), 這個數(shù)據(jù)項(xiàng)表示被引用的方法所在的類型。 所以說, CONSTANT_Class_info可以作為方法符號引用的一部分。 

class_index以下的兩個字節(jié)是一個叫做name_and_type_index的索引, 它指向一個CONSTANT_NameAndType_info, 這個CONSTANT_NameAndType_info前面的博客中已經(jīng)解釋過了, 不明白的朋友可以先看前面的博客:深入理解Java Class文件格式(三) 。 這個CONSTANT_NameAndType_info描述的是被引用的方法的名稱和描述符。 我們在前面的博客中也提到過, CONSTANT_NameAndType_info可以作為方法符號引用的一部分。

到此, 我們可以知道, CONSTANT_Methodref_info就是對一個字段的符號引用, 這個符號引用包括兩部分, 一部分是該方法所在的類, 另一部分是該方法的方法名和描述符。 這就是所謂的 “對方法的符號引用” 。

下面結(jié)合實(shí)際代碼來說明, 代碼如下:
  1. package com.jg.zhang;  
  2.   
  3. public class Programer {  
  4.   
  5.     Computer computer;  
  6.       
  7.     public Programer(Computer computer){  
  8.         this.computer = computer;  
  9.     }  
  10.       
  11.     public void doWork(){  
  12.         computer.calculate();  
  13.     }  
  14. }  


  1. package com.jg.zhang;  
  2.   
  3. public class Computer {  
  4.   
  5.     public void calculate() {  
  6.         System.out.println("working...");  
  7.           
  8.     }  
  9. }  


上面的代碼包括兩個類, 其中Programer類引用了Computer類, 在Programer類的doWork方法中引用(調(diào)用)了Computer類的calculate方法。源碼中對一個方法的描述形式我們再熟悉不過了, 現(xiàn)在我們就反編譯Programer, 看看Programer中對Computer的doWork方法的引用, 在class文件中是如何描述的。 

下面給出Programer的反編譯結(jié)果, 其中省去了一些不相關(guān)的信息:
  1. Constant pool:  
  2. .........  
  3.   
  4.   
  5.   #12 = Utf8               ()V  
  6.   
  7.   
  8.   #20 = Methodref          #21.#23        //  com/jg/zhang/Computer.calculate:()V  
  9.   #21 = Class              #22            //  com/jg/zhang/Computer  
  10.   #22 = Utf8               com/jg/zhang/Computer  
  11.   #23 = NameAndType        #24:#12        //  calculate:()V  
  12.   #24 = Utf8               calculate  
  13.   
  14. {  
  15.   
  16.   com.jg.zhang.Computer computer;       
  17.     flags:  
  18.   
  19. .........  
  20.   
  21.   public void doWork();  
  22.     flags: ACC_PUBLIC  
  23.     Code:  
  24.       stack=1, locals=1, args_size=1  
  25.          0: aload_0  
  26.          1: getfield      #13                 // Field computer:Lcom/jg/zhang/Computer;  
  27.          4: invokevirtual #20                 // Method com/jg/zhang/Computer.calculate:()V  
  28.          7: return  
  29. }  

可以看到, doWork方法的位置為4的字節(jié)碼指令invokevirtual引用了索引為20的常量池?cái)?shù)據(jù)項(xiàng), 常量池中索引為20的數(shù)據(jù)項(xiàng)是一個CONSTANT_Methodref_info, 這個CONSTANT_Methodref_info又引用了索引為21和23的兩個數(shù)據(jù)項(xiàng), 索引為21的數(shù)據(jù)項(xiàng)是一個CONSTANT_Class_info, 這個CONSTANT_Class_info數(shù)據(jù)項(xiàng)又引用了索引為22的數(shù)據(jù)項(xiàng), 索引為22的數(shù)據(jù)項(xiàng)是一個CONSTANT_Utf8_info , 他存儲了被引用的Computer類中的calculate方法所在的類的全限定名com/jg/zhang/Computer 。 而CONSTANT_Methodref_info所引用的索引為23的數(shù)據(jù)項(xiàng)是一個CONSTANT_NameAndType_info, 它又引用了兩個數(shù)據(jù)項(xiàng), 分別為第24項(xiàng)和第12項(xiàng), 這是兩個CONSTANT_Utf8_info , 分別存儲了被引用的方法calculate的方法名calculate, 和該方法的描述符()V 。 

下面給出內(nèi)存布局圖, 這個圖中涉及的東西同樣有點(diǎn)多, 因?yàn)镃ONSTANT_Methodref_info引用了CONSTANT_Class_info和CONSTANT_NameAndType_info, CONSTANT_Class_info又引用了一個CONSTANT_Utf8_info , 而CONSTANT_NameAndType_info又引用了兩個CONSTANT_Utf8_info 。 





(11) CONSTANT_InterfaceMethodref_info


常量池中的一個CONSTANT_InterfaceMethodref_info, 可以看做是CONSTANT_InterfaceMethodref數(shù)據(jù)類型的一個實(shí)例。 該數(shù)據(jù)項(xiàng)表示對一個接口方法的符號引用, 不能是對類中的方法的符號引用。 其中ref三個字母就是reference的簡寫。 在上一小節(jié)中介紹了CONSTANT_Methodref_info, 它是對類中的方法的符號引用, 本節(jié)中介紹的CONSTANT_InterfaceMethodrefCONSTANT_Methodref_info很相似。既然是符號“引用”, 那么只有在原文件中調(diào)用了一個接口中的方法, 常量池中才有和這個被調(diào)用方法的相對應(yīng)的符號引用, 即存在一個CONSTANT_InterfaceMethodref。 如果只是在接口中定義了一個方法, 但是沒調(diào)用它, 則不會在常量池中出現(xiàn)和這個方法對應(yīng)的CONSTANT_InterfaceMethodref 。 

和其他類型的常量池?cái)?shù)據(jù)項(xiàng)一樣, 它的第一個字節(jié)也必然是tag, 它的tag值為11 。 也就是說, 當(dāng)虛擬機(jī)訪問到一個常量池中的一項(xiàng)數(shù)據(jù), 如果發(fā)現(xiàn)這個數(shù)據(jù)的tag值為11, 就可以確定這個被訪問的數(shù)據(jù)項(xiàng)是一個CONSTANT_InterfaceMethodref, 并且知道這個數(shù)據(jù)項(xiàng)表示對一個接口中的方法的符號引用。 

tag值下面的兩個字節(jié)是一個叫做class_index的索引值, 它指向一個CONSTANT_Class_info數(shù)據(jù)項(xiàng), 這個數(shù)據(jù)項(xiàng)表示被引用的方法所在的接口。 所以說, CONSTANT_Class_info可以作為方法符號引用的一部分。 

class_index以下的兩個字節(jié)是一個叫做name_and_type_index的索引, 它指向一個CONSTANT_NameAndType_info, 這個CONSTANT_NameAndType_info前面的博客中已經(jīng)解釋過了, 不明白的朋友可以先看前面的博客:深入理解Java Class文件格式(三) 。 這個CONSTANT_NameAndType_info描述的是被引用的方法的名稱和描述符。 我們在前面的博客中也提到過, CONSTANT_NameAndType_info可以作為方法符號引用的一部分。

到此, 我們可以知道, CONSTANT_InterfaceMethodref就是對一個接口中的方法的符號引用, 這個符號引用包括兩部分, 一部分是該方法所在的接口, 另一部分是該方法的方法名和描述符。 這就是所謂的 “對接口中的方法的符號引用” 。

下面結(jié)合實(shí)際代碼來說明, 代碼如下:

  1. package com.jg.zhang;  
  2.   
  3. public class Plane {  
  4.   
  5.     IFlyable flyable;  
  6.       
  7.     void flyToSky(){  
  8.         flyable.fly();  
  9.     }  
  10. }  

  1. package com.jg.zhang;  
  2.   
  3. public interface IFlyable {  
  4.   
  5.     void fly();  
  6. }  

在上面的代碼中, 定義可一個類Plane, 在這個類中有一個IFlyable接口類型的字段flyable, 然后在Plane的flyToSky方法中調(diào)用了IFlyable中的fly方法。 這就是源代碼中對一個接口中的方法的引用方式, 下面我們反編譯Plane, 看看在class文件層面, 對一個接口中的方法的引用是如何描述的。

下面給出反編譯結(jié)果, 為了簡潔期間, 省略了一些不相關(guān)的內(nèi)容:

[plain] view plain copy
在CODE上查看代碼片派生到我的代碼片
  1. Constant pool:  
  2. .........  
  3.   
  4.   #8 = Utf8               ()V  
  5.   
  6.   #19 = InterfaceMethodref #20.#22        //  com/jg/zhang/IFlyable.fly:()V  
  7.   #20 = Class              #21            //  com/jg/zhang/IFlyable  
  8.   #21 = Utf8               com/jg/zhang/IFlyable  
  9.   #22 = NameAndType        #23:#8         //  fly:()V  
  10.   #23 = Utf8               fly  
  11.   
  12. {  
  13.   
  14. .........  
  15.   
  16.   com.jg.zhang.IFlyable flyable;  
  17.     flags:  
  18.   
  19. .........  
  20.   
  21.   void flyToSky();  
  22.     flags:  
  23.     Code:  
  24.       stack=1, locals=1, args_size=1  
  25.          0: aload_0  
  26.          1: getfield      #17                 // Field flyable:Lcom/jg/zhang/IFlyable;  
  27.          4: invokeinterface #19,  1           // InterfaceMethod com/jg/zhang/IFlyable.fly:()V  
  28.          9: return  
  29.   
  30. }  


可以看到, flyToSky方法的位置為4的字節(jié)碼指令invokeinterface引用了索引為19的常量池?cái)?shù)據(jù)項(xiàng), 常量池中索引為19的數(shù)據(jù)項(xiàng)是一個CONSTANT_InterfaceMethodref_info, 這個CONSTANT_InterfaceMethodref_info又引用了索引為20和22的兩個數(shù)據(jù)項(xiàng), 索引為20的數(shù)據(jù)項(xiàng)是一個CONSTANT_Class_info, 這個CONSTANT_Class_info數(shù)據(jù)項(xiàng)又引用了索引為21的數(shù)據(jù)項(xiàng), 索引為21的數(shù)據(jù)項(xiàng)是一個CONSTANT_Utf8_info , 他存儲了被引用的方法fly所在的接口的全限定名com/jg/zhang/IFlyable 。 而CONSTANT_InterfaceMethodref_info所引用的索引為22的數(shù)據(jù)項(xiàng)是一個CONSTANT_NameAndType_info, 它又引用了兩個數(shù)據(jù)項(xiàng), 分別為第23項(xiàng)和第8項(xiàng), 這是兩個CONSTANT_Utf8_info , 分別存儲了被引用的方法fly的方法名fly, 和該方法的描述符()V 。 

下面給出內(nèi)存布局圖, 這個圖中涉及的東西同樣有點(diǎn)多, 因?yàn)镃ONSTANT_InterfaceMethodref_info引用了CONSTANT_Class_info和CONSTANT_NameAndType_info, CONSTANT_Class_info又引用了一個CONSTANT_Utf8_info , 而CONSTANT_NameAndType_info又引用了兩個CONSTANT_Utf8_info 。 





總結(jié)


到此為止, class文件中的常量池部分就已經(jīng)講解完了。 進(jìn)行一下總結(jié)。對于深入理解Java和JVM , 理解class文件的格式至關(guān)重要, 而在class文件中, 常量池是一項(xiàng)非常重要的信息。 常量池中有11種數(shù)據(jù)項(xiàng), 這個11種數(shù)據(jù)項(xiàng)存儲了各種信息, 包括常量字符串, 類的信息, 方法的符號引用, 字段的符號引用等等。 常量池中的數(shù)據(jù)項(xiàng)通過索引來訪問, 訪問形式類似于數(shù)組。 常量池中的各個數(shù)據(jù)項(xiàng)之前會通過索引相互引用, class文件的其他地方也會引用常量池中的數(shù)據(jù)項(xiàng) , 如方法的字節(jié)碼指令。 

在下面的文章中, 會繼續(xù)介紹class文件中, 位于常量池以下的其他信息。 這些信息包括:對本類的描述, 對父類的描述, 對實(shí)現(xiàn)的接口的描述, 本類中聲明的字段的描述, 本類匯總定義的方法的描述,還有各種屬性。 





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

更多關(guān)于Java和Android等其他技術(shù)的文章, 請關(guān)注我的博客: http://blog.csdn.net/zhangjg_blog


    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多