通常而言,咱們在定義類的過程當中會定義一些
field 字段,而後會在這個類的其餘地方(如方法中)使用到它。有可能咱們在類的方法中只使用field字段一次,也有可能咱們會在類定義的方法中使用它不少不少次。html
舉一個簡單的例子,咱們定一個叫Person的簡單java bean,它有name和age兩個field字段,以下所示:java
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字段常量的形式保存到常量池中。jvm
將它name和age封裝成
CONSTANT_Fieldref_info 常量池項,放到常量池中,在類中引用到它的地方,直接放置一個指向field字段所在常量池的索引。post
上面的Person類,使用javap -v Person指令,查看class文件的信息,你會看到,在Person類中引用到age和namefield字段的地方,都是指向了常量池中age和namefield字段對應的常量池項中。表示field字段的常量池項叫作CONSTANT_Fieldref_info。ui
實例解析: 如今,讓咱們來看一下Person類中定義的namefield字段在常量池中的表示。經過使用javap
-v Person會查看到以下的常量池信息:this
請讀者看上圖中namefield字段的數據類型,它在#6個常量池項,以UTF-8編碼格式的字符串「Ljava/lang/String;」
表示,這表示着這個field 字段是java.lang.String 類型的。關於field字段的數據類型,class文件中存儲的方式和咱們在源碼中聲明的有些不同。請看下圖的對應關係:編碼
1.舉例:3d
仍是以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() 方法引用在常量池中的表示
好比咱們定義了一個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,我想對你說,暫時先無論它吧。
這三項主要是爲了讓Java語言支持動態語言特性而在Java 7 版本中新增的三個常量池項,只會在極其特別的狀況能用到它,在class文件中幾乎不會生成這三個常量池項。 其實我花了一些時間來研究這三項,而且想經過各類方式生成這三項,不過沒有成功,最後搞的仍是迷迷糊糊的。從我瞭解到的信息來看,Java 7對動態語言的支持很笨拙,而且當前沒有什麼應用價值,而後就對着三項的研究先放一放了。)
若是讀者有興趣瞭解這三項,建議讀者搜索關於Java 7 動態語言特性方面的文章,推薦閱讀: