NO9.類中引用到的field字段在常量池中是怎樣描述的?(CONSTANT_Fieldref_info, CONSTANT_Name_Type_info)
一般而言,我們在定義類的過程中會定義一些 field 字段,然後會在這個類的其他地方(如方法中)使用到它。有可能我們在類的方法中只使用field字段一次,也有可能我們會在類定義的方法中使用它很多很多次。
舉一個簡單的例子,我們定一個叫Person的簡單java bean,它有name和age兩個field字段,如下所示:
- package com.louis.jvm;
- public class Person {
- private String name;
- private int age;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- }
在上面定義的類中,我們在Person類中的一系列方法裏,多次引用到namefield字段 和agefield字段,對於JVM編譯器而言,name和age只是一個符號而已,並且它在由於它可能會在此類中重複出現多次,所以JVM把它當作常量來看待,將name和age以field字段常量的形式保存到常量池中。
將它name和age封裝成 CONSTANT_Fieldref_info 常量池項,放到常量池中,在類中引用到它的地方,直接放置一個指向field字段所在常量池的索引。
上面的Person類,使用javap -v Person指令,查看class文件的信息,你會看到,在Person類中引用到age和namefield字段的地方,都是指向了常量池中age和namefield字段對應的常量池項中。表示field字段的常量池項叫做CONSTANT_Fieldref_info。
實例解析: 現在,讓我們來看一下Person類中定義的namefield字段在常量池中的表示。通過使用javap -v Person會查看到如下的常量池信息:
請讀者看上圖中namefield字段的數據類型,它在#6個常量池項,以UTF-8編碼格式的字符串“Ljava/lang/String;” 表示,這表示着這個field 字段是java.lang.String 類型的。關於field字段的數據類型,class文件中存儲的方式和我們在源碼中聲明的有些不一樣。請看下圖的對應關係:
NO10.類中引用到的method方法在常量池中是怎樣描述的?(CONSTANT_Methodref_info, CONSTANT_Name_Type_info)
1.舉例:
還是以Person類爲例。在Person類中,我們定義了setName(String
name)、getName()、setAge(int age)、getAge()這些方法:
- package com.louis.jvm;
- public class Person {
- private String name;
- private int age;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- }
雖然我們定義了方法,但是這些方法沒有在類總的其他地方被用到(即沒有在類中其他的方法中引用到),所以它們的方法引用信息並不會放到常量中。
現在我們在類中加一個方法 getInfo(),調用了getName()和getAge() 方法:
- public String getInfo()
- {
- return getName()+"\t"+getAge();
- }
這時候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接口的方法的引用表示。
- package com.louis.jvm;
- /**
- * Worker 接口類
- * @author luan louis
- */
- public interface Worker{
- public void work();
- }
- package com.louis.jvm;
- /**
- * Boss 類,makeMoney()方法 調用Worker 接口的work
- * @author louluan
- */
- public class Boss {
- public void makeMoney(Worker worker)
- {
- worker.work();
- }
- }
我們對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描述的是實例類中的方法;
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 動態語言特性方面的文章,推薦閱讀:
-----------------------------------------------------------------------------------------------------------------------------------------
本文源自 http://blog.csdn.net/luanlouis/,如需轉載,請註明出處,謝謝!