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


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


     通常而言,咱們在定義類的過程當中會定義一些
field
字段,而後會在這個類的其餘地方(如方法中)使用到它。有可能咱們在類的方法中只使用field字段一次,也有可能咱們會在類定義的方法中使用它不少不少次。
html


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


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

 


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




 


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


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



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






 



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




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





請注意!!!

    若是咱們在類中定義了field 字段,可是沒有在類中的其餘地方用到這些字段,它是不會被編譯器放到常量池中的。讀者能夠本身試一下。(固然了,定義了可是沒有在類中的其它地方引用到這種狀況不多。)
lua


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






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


      1.舉例:3d


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


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



 雖然咱們定義了方法,可是這些方法沒有在類總的其餘地方被用到(即沒有在類中其餘的方法中引用到),因此它們的方法引用信息並不會放到常量中。

如今咱們在類中加一個方法 getInfo(),調用了getName()getAge()
方法:


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

這時候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. * Worker 接口類
  4. * @author luan louis
  5. */
  6. public interface Worker{
  7. public void work();
  8. }
  
  
  
  
  
  1. package com.louis.jvm;
  2. /**
  3. * Boss 類,makeMoney()方法 調用Worker 接口的work
  4. * @author louluan
  5. */
  6. public class Boss {
  7. public void makeMoney(Worker worker)
  8. {
  9. worker.work();
  10. }
  11. }


咱們對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_infoCONSTANT_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動態語言支持詳解

相關文章
相關標籤/搜索