《Java虛擬機原理圖解》 1.2.3、Class文件中的常量池詳解(下)

NO9.類中引用到的field字段在常量池中是怎樣描述的?(CONSTANT_Fieldref_info, CONSTANT_Name_Type_info)

     一般而言,我們在定義類的過程中會定義一些 field 字段,然後會在這個類的其他地方(如方法中)使用到它。有可能我們在類的方法中只使用field字段一次,也有可能我們會在類定義的方法中使用它很多很多次。

     舉一個簡單的例子,我們定一個叫Person的簡單java bean,它有nameage兩個field字段,如下所示:

  1. package com.louis.jvm;  
  2.   
  3. public class Person {  
  4.   
  5.     private String name;  
  6.     private int age;  
  7.       
  8.     public String getName() {  
  9.         return name;  
  10.     }  
  11.       
  12.     public void setName(String name) {  
  13.         this.name = name;  
  14.     }  
  15.     public int getAge() {  
  16.         return age;  
  17.     }  
  18.       
  19.     public void setAge(int age) {  
  20.         this.age = age;  
  21.     }  
  22. }  

 

在上面定義的類中,我們在Person類中的一系列方法裏,多次引用到namefield字段 和agefield字段,對於JVM編譯器而言,nameage只是一個符號而已,並且它在由於它可能會在此類中重複出現多次,所以JVM把它當作常量來看待,將nameagefield字段常量的形式保存到常量池中。


 

將它nameage封裝成 CONSTANT_Fieldref_info 常量池項,放到常量池中,在類中引用到它的地方,直接放置一個指向field字段所在常量池的索引。

上面的Person類,使用javap -v Person指令,查看class文件的信息,你會看到,在Person類中引用到agenamefield字段的地方,都是指向了常量池中agenamefield字段對應的常量池項中。表示field字段的常量池項叫做CONSTANT_Fieldref_info

怎樣描述某一個field字段的引用?


 

    實例解析: 現在,讓我們來看一下Person類中定義的namefield字段在常量池中的表示。通過使用javap -v Person會查看到如下的常量池信息:

   請讀者看上圖中namefield字段的數據類型,它在#6個常量池項,以UTF-8編碼格式的字符串“Ljava/lang/String;” 表示,這表示着這個field 字段是java.lang.String 類型的。關於field字段的數據類型,class文件中存儲的方式和我們在源碼中聲明的有些不一樣。請看下圖的對應關係:


請注意!!!

    如果我們在類中定義了field 字段,但是沒有在類中的其他地方用到這些字段,它是不會被編譯器放到常量池中的。讀者可以自己試一下。(當然了,定義了但是沒有在類中的其它地方引用到這種情況很少。)

只有在類中的其他地方引用到了,纔會將他放到常量池中


NO10.類中引用到的method方法在常量池中是怎樣描述的?(CONSTANT_Methodref_info, CONSTANT_Name_Type_info)

      1.舉例:

             還是以Person類爲例。在Person類中,我們定義了setName(String name)、getName()、setAge(int age)、getAge()這些方法:   

  1. package com.louis.jvm;  
  2.   
  3. public class Person {  
  4.   
  5.     private String name;  
  6.     private int age;  
  7.       
  8.     public String getName() {  
  9.         return name;  
  10.     }  
  11.       
  12.     public void setName(String name) {  
  13.         this.name = name;  
  14.     }  
  15.     public int getAge() {  
  16.         return age;  
  17.     }  
  18.       
  19.     public void setAge(int age) {  
  20.         this.age = age;  
  21.     }  
  22.       
  23. }  

 雖然我們定義了方法,但是這些方法沒有在類總的其他地方被用到(即沒有在類中其他的方法中引用到),所以它們的方法引用信息並不會放到常量中。

現在我們在類中加一個方法 getInfo(),調用了getName()getAge() 方法:

  1. public String getInfo()  
  2. {  
  3.       
  4.     return getName()+"\t"+getAge();  
  5. }  

這時候JVM編譯器會將getName()getAge()方法的引用信息包裝成CONSTANT_Methodref_info結構體放入到常量池之中。


   這裏的方法調用的方式牽涉到Java非常重要的一個術語和機制,叫動態綁定。這個動態綁定問題以後在單獨談談。

  

2.  怎樣表示一個方法引用?

請看下圖:



3.  方法描述符的組成



4.  getName() 方法引用在常量池中的表示



NO11.類中引用到某個接口中定義的method方法在常量池中是怎樣描述的?(CONSTANT_InterfaceMethodref_info,CONSTANT_Name_Type_info)

當我們在某個類中使用到了某個接口中的方法,JVM會將用到的接口中的方法信息方知道這個類的常量池中。

比如我們定義了一個Worker接口,和一個Boss類,在Boss類中調用了Worker接口中的方法,這時候在Boss類的常量池中會有Worker接口的方法的引用表示。

  1. package com.louis.jvm;  
  2.   
  3. /** 
  4.  * Worker 接口類 
  5.  * @author luan louis 
  6.  */  
  7. public interface Worker{  
  8.       
  9.     public void work();  
  10.   
  11. }  
  1. package com.louis.jvm;  
  2.   
  3. /** 
  4.  * Boss 類,makeMoney()方法 調用Worker 接口的work 
  5.  * @author louluan 
  6.  */  
  7. public class Boss {  
  8.       
  9.     public void makeMoney(Worker worker)  
  10.     {  
  11.         worker.work();  
  12.     }  
  13.   
  14. }  

我們對Boss.class執行javap -v  Boss,然後會看到如下信息:



如上圖所示,在Boss類的makeMoney()方法中調用了Worker接口的work()方法,機器指令是通過invokeinterface指令完成的,invokeinterface指令後面的操作數,是指向了Boss常量池中Worker接口的work()方法描述,表示的意思就是:“我要調用Worker接口的work()方法”。

Worker接口的work()方法引用信息,JVM會使用CONSTANT_InterfaceMethodref_info結構體來描述,CONSTANT_InterfaceMethodref_info定義如下:


   CONSTANT_InterfaceMethodref_info結構體和上面介紹的CONSTANT_Methodref_info 結構體很基本上相同,它們的不同點只有:

   1.CONSTANT_InterfaceMethodref_info 的tag 值爲11,而CONSTANT_Methodref_info的tag值爲10;

   2. CONSTANT_InterfaceMethodref_info 描述的是接口中定義的方法,而CONSTANT_Methodref_info描述的是實例類中的方法;

  

小試牛刀

關於方法的描述,完全相同CONSTANT_InterfaceMethodref_info和上述的CONSTANT_Methodref_info 結構體完全一致,這裏就不單獨爲CONSTANT_InterfaceMethodref_info繪製結構圖了,請讀者依照CONSTANT_Methodref_info的描述,結合本例子關於Worker接口和Boss類的關係,使用javap -v Boss,查看常量池信息,然後根據常量池信息,自己動手繪製work() 方法在常量池中的結構。


 

NO12.CONSTANT_MethodType_info,CONSTANT_MethodHandle_info,CONSTANT_InvokeDynamic_info

       如果你從我的《常量池詳解》NO1節看到了NO11節,那麼恭喜你,你已經學會了幾乎所有的常量池項!只要你掌握了上述的常量池項,你就可以讀懂你平常所見到的任何一個class文件的常量池了。

       至於NO12所列出來的三項:CONSTANT_MethodType_info,CONSTANT_MethodHandle_info,CONSTANT_InvokeDynamic_info,我想對你說,暫時先不管它吧。

       這三項主要是爲了讓Java語言支持動態語言特性而在Java 7 版本中新增的三個常量池項,只會在極其特別的情況能用到它,在class文件中幾乎不會生成這三個常量池項。   其實我花了一些時間來研究這三項,並且想通過各種方式生成這三項,不過沒有成功,最後搞的還是迷迷糊糊的。從我瞭解到的信息來看,Java 7對動態語言的支持很笨拙,並且當前沒有什麼應用價值,然後就對着三項的研究先放一放了。)

       如果讀者有興趣瞭解這三項,建議讀者搜索關於Java 7 動態語言特性方面的文章,推薦閱讀:

         探祕Java 7:JVM動態語言支持詳解

       執行篇:解析JDK 7的動態類型語言支持







作者的話

    本文是《Java虛擬機原理圖解》系列的其中一篇,如果您有興趣,請關注該系列的其他文章~

   覺得本文不錯,順手點個贊哦~~您的鼓勵,是我繼續分享知識的強大動力!

  

 

 

 

 

-----------------------------------------------------------------------------------------------------------------------------------------

本文源自  http://blog.csdn.net/luanlouis/,如需轉載,請註明出處,謝謝!


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