Class文件是一組以8位字節(jié)為基礎(chǔ)單位的二進(jìn)制流,各個(gè)數(shù)據(jù)項(xiàng)目按照順序緊湊地排列在Class文件中,中間沒(méi)有任何分隔符。
使用命令javac將.java 文件編譯為.class文件
使用命令javap輸出.class文件的字節(jié)碼內(nèi)容
Class文件格式采用類(lèi)似于C語(yǔ)言結(jié)構(gòu)體的偽結(jié)構(gòu),只有兩種數(shù)據(jù)類(lèi)型:無(wú)符號(hào)數(shù)和表。
- 無(wú)符號(hào)數(shù):屬于基本的數(shù)據(jù)類(lèi)型,以u(píng)1、u2、u4、u8分別代表1個(gè)字節(jié)、2個(gè)字節(jié)、4個(gè)字節(jié)、8個(gè)字節(jié)的無(wú)符號(hào)數(shù)。無(wú)符號(hào)數(shù)用來(lái)描述數(shù)字、索引引用、數(shù)量值或者按照UTF-8編碼構(gòu)成字符串值。
-
表:由多個(gè)無(wú)符號(hào)數(shù)或其他表作為數(shù)據(jù)項(xiàng)構(gòu)成的復(fù)合數(shù)據(jù)類(lèi)型。所有表都以
_info結(jié)尾。表用于描述有層次關(guān)系的復(fù)合結(jié)構(gòu)的數(shù)據(jù),整個(gè)Class文件本質(zhì)上就是一張表,由下表中的數(shù)據(jù)項(xiàng)構(gòu)成。
| 名稱 | 類(lèi)型 | 數(shù)量 |
|---|---|---|
| magic | u4 | 1 |
| minor_version | u2 | 1 |
| major_version | u2 | 1 |
| constant_pool_count | u2 | 1 |
| constant_pool | cp_info | constant_pool_count - 1 |
| access_flags | u2 | 1 |
| this_class | u2 | 1 |
| super_class | u2 | 1 |
| interfaces_count | u2 | 1 |
| interfaces | u2 | interfaces_count |
| fields_count | u2 | 1 |
| fields | field_info | fields_count |
| methods_count | u2 | 1 |
| methods | method_info | methods_count |
| attributes_count | u2 | 1 |
| attributes | attribute_info | attributes_count |
學(xué)習(xí)類(lèi)文件結(jié)構(gòu),就是要明白上表中各個(gè)數(shù)據(jù)項(xiàng)的具體含義。
魔數(shù)與Class文件版本
魔數(shù)
即上表中的
magic,4個(gè)字節(jié),在Class文件的開(kāi)頭。作用是確定該文件是否為一個(gè)被虛擬機(jī)接受的Class文件。使用魔數(shù)而不是后綴名的方式是基于安全的考慮:文件擴(kuò)展名可以任意更改。
-
正常的Class文件的魔數(shù)為
0xCAFEBABE。cafe babe,咖啡寶貝。
Java.png 版本號(hào)
即上表中的
major_version、minor_version:Class文件的第5、6個(gè)字節(jié)是次版本號(hào)minor_version,第7、8個(gè)字節(jié)是主版本號(hào)major_version。高版本的JDK可以向下兼容,但低版本的JDK不向上兼容,虛擬機(jī)會(huì)拒絕執(zhí)行。
常量池
常量池是Class文件中的資源倉(cāng)庫(kù),它是Class文件結(jié)構(gòu)中與其他數(shù)據(jù)項(xiàng)關(guān)聯(lián)最多的數(shù)據(jù)類(lèi)型,也是占用Class文件空間最大的數(shù)據(jù)項(xiàng)之一。
常量池容器計(jì)數(shù)值
即上表中的
constant_pool_count,2個(gè)字節(jié)常量池中的常量的數(shù)量是不固定的,所以需要
constant_pool_count來(lái)表示常量池中常量的數(shù)量。該容器計(jì)數(shù)從1開(kāi)始(而不是Java語(yǔ)言習(xí)慣中從0開(kāi)始),比如
constant_pool_count的值為16,則說(shuō)明常量池中有15項(xiàng)常量,索引值范圍為1-15。將第0項(xiàng)常量空出來(lái)的目的是為了滿足后面某些指向常量池的索引值的數(shù)據(jù)在特定情況下表達(dá)“不引用任何一個(gè)常量池項(xiàng)”的含義。常量池
從概念上理解,
常量池 = constant_pool[constant_pool_count - 1]。常量池中主要存放兩大類(lèi)常量:字面量和符號(hào)引用。
①字面量:接近于Java語(yǔ)言層面的常量概念,如文本字符串、聲明為final的常量值等。
②符號(hào)引用:屬于編譯原理方面的概念,主要包括:類(lèi)和接口的全限定名、字段的名稱和描述符、方法的名稱和描述符。Java代碼在經(jīng)過(guò)javac命令編譯之后,生成的Class文件中并不會(huì)保存各個(gè)方法、字段的最終內(nèi)存布局信息,必須等到虛擬機(jī)運(yùn)行時(shí),從常量池中獲得對(duì)應(yīng)的符號(hào)引用,再通過(guò)這些符號(hào)引用經(jīng)過(guò)類(lèi)創(chuàng)建或運(yùn)行時(shí)解析、翻譯等才能得到具體的內(nèi)存地址。常量池中共有14中類(lèi)型的常量,每一個(gè)常量(項(xiàng))都有自己的表結(jié)構(gòu),但這14種表的有一個(gè)共同特點(diǎn):表開(kāi)始的第一位是一個(gè)u1類(lèi)型的標(biāo)志位tag,代表這個(gè)常量屬于哪種常量類(lèi)型。常量池中的常量類(lèi)型見(jiàn)下表。
| 常量類(lèi)型 | 表類(lèi)型 | tag取值 |
|---|---|---|
| UTF-8編碼的字符串 | CANSTANT_Utf8_info | 1 |
| 整型字面量 | CANSTANT_Integer_info | 3 |
| 浮點(diǎn)型字面量 | CANSTANT_Float_info | 4 |
| 長(zhǎng)整型字面量 | CANSTANT_Long_info | 5 |
| 雙精度浮點(diǎn)型字面量 | CANSTANT_Double_info | 6 |
| 類(lèi)或接口的符號(hào)引用 | CANSTANT_Class_info | 7 |
| 字符串類(lèi)型字面量 | CANSTANT_String_info | 8 |
| 字段的符號(hào)引用 | CANSTANT_Fieldref_info | 9 |
| 類(lèi)中方法的符號(hào)引用 | CANSTANT_Methodref_info | 10 |
| 接口中方法的符號(hào)引用 | CANSTANT_IntefaceMethodref_info | 11 |
| 字段或方法的部分符號(hào)引用 | CANSTANT_NameAndType_info | 12 |
| 方法句柄 | CANSTANT_MethodHandle_info | 15 |
| 方法類(lèi)型 | CANSTANT_MethodType_info | 16 |
| 動(dòng)態(tài)方法調(diào)用點(diǎn) | CANSTANT_InvokeDynamic_info | 18 |
訪問(wèn)標(biāo)志
即Class文件數(shù)據(jù)項(xiàng)表中的access_flags,用于識(shí)別類(lèi)或者接口層次的訪問(wèn)信息:該是類(lèi)還是接口,是否為public,是否定義為abstract;如果是類(lèi),是否聲明為final;該類(lèi)是否由用戶代碼產(chǎn)生,是否為注解,是否為枚舉等信息。
類(lèi)索引、父類(lèi)索引和接口索引
Class文件根據(jù)這三項(xiàng)數(shù)據(jù)來(lái)確定這個(gè)類(lèi)的繼承關(guān)系。
-
類(lèi)索引
即Class文件數(shù)據(jù)項(xiàng)表中的this_class:用于確定這個(gè)類(lèi)的全限定名。 -
父索引
即Class文件數(shù)據(jù)項(xiàng)表中的super_class:用于確定這個(gè)類(lèi)的父類(lèi)的全限定名。由于Java是單繼承,只有一個(gè)父類(lèi)。所有類(lèi)(除了java.lang.Object)都有父類(lèi)。 -
接口索引
即Class文件數(shù)據(jù)項(xiàng)表中的interfaces_count和interfaces。interfaces_count表示接口索引表的容量,如果該值為0,接口索引表不占用任何字節(jié)。
其中,類(lèi)索引和父索引各自指向一個(gè)常量池中的類(lèi)型為CANSTANT_Class_info的常量,通過(guò)該常量的索引值再定位到常量池中的CANSTANT_Utf8_info類(lèi)型的常量表示的全限定名字符串。
字段表集合
從概念上理解,字段表集合 = fields[fields_count],用于描述該Class文件對(duì)應(yīng)的代碼中聲明的變量:包括類(lèi)級(jí)變量以及實(shí)例變量,但不包括方法內(nèi)聲明的局部變量。
對(duì)于一個(gè)字段,描述信息主要有:作用域(public、private、protected),是實(shí)例變量還是類(lèi)變量(有無(wú)static修飾),可變性(final),并發(fā)可見(jiàn)性(volatile),可否被序列化(transient),數(shù)據(jù)類(lèi)型(基本類(lèi)型、對(duì)象、數(shù)組),名稱。這些信息中各個(gè)修飾符都是布爾值(要么有,要么沒(méi)有),用標(biāo)志位表示,而名稱、數(shù)據(jù)類(lèi)型則引用常量池中的常量來(lái)描述。
每一個(gè)字段會(huì)對(duì)應(yīng)一個(gè)字段表,字段表的最終結(jié)構(gòu)如下。
| 名稱 | 類(lèi)型 | 數(shù)量 |
|---|---|---|
| access_flag | u2 | 1 |
| name_index | u2 | 1 |
| descriptor_index | u2 | 1 |
| attributes_count | u2 | 1 |
| attributes | attribute_info | attributes_count |
-
access_flag
該字段的修飾符信息:是否public、private、protected、static、final、volatile、transient、enum等。 -
name_index
該字段的簡(jiǎn)單名稱,是對(duì)常量池的常量引用。比如在代碼中定義private String name,則name字段的簡(jiǎn)單名稱就是name,但是這個(gè)name這個(gè)字面量是在常量池中的,name_index存儲(chǔ)的是對(duì)常量池中該常量項(xiàng)的引用。 -
descriptor_index
該字段的描述符,描述字段的數(shù)據(jù)類(lèi)型。 -
attributes和attributes
該字段的屬性表,見(jiàn)下文。
字段表集合中不會(huì)列出從超類(lèi)或父接口中繼承而來(lái)的字段,但有可能會(huì)列出原本Java代碼中不存在的字段,比如在內(nèi)部類(lèi)中為了保持對(duì)外類(lèi)的訪問(wèn)性,在編譯Class文件的時(shí)候會(huì)自動(dòng)添加外部類(lèi)的實(shí)例字段。
方法表集合
從概念上理解,方法表集合 = methods[methods_count]。每一個(gè)方法對(duì)應(yīng)一個(gè)方法表method_info。方法表與字段表的結(jié)構(gòu)一致,只是具體的信息項(xiàng)不同。
| 名稱 | 類(lèi)型 | 數(shù)量 |
|---|---|---|
| access_flag | u2 | 1 |
| name_index | u2 | 1 |
| descriptor_index | u2 | 1 |
| attributes_count | u2 | 1 |
| attributes | attribute_info | attributes_count |
其中,訪問(wèn)信息access_flag包括是否public、private、protected、static、final、synchronized、native、abstract、strictfp、是否接受不定參數(shù)、是否由編譯器自動(dòng)產(chǎn)生等。
其中,方法描述符descriptor_index中描述了方法的參數(shù)列表(數(shù)量、類(lèi)型、順序)和返回值。
而方法體中的代碼,經(jīng)過(guò)編譯器編譯成字節(jié)碼指令后,存儲(chǔ)在方法屬性表attributes[attributes_count]中。
如果子類(lèi)沒(méi)有重寫(xiě)父類(lèi)的方法,則子類(lèi)的方法表集合中不會(huì)出現(xiàn)來(lái)自父類(lèi)的方法信息,但可能會(huì)出現(xiàn)編譯器自動(dòng)添加的方法,如類(lèi)構(gòu)造器<clinit>方法和實(shí)例構(gòu)造器<init>方法。
屬性表集合
在Class文件、字段表、方法表中都可以包含自己的屬性表集合,以此描述某些場(chǎng)景專(zhuān)有的信息。
1、Code屬性
Java程序方法體中的代碼經(jīng)過(guò)javac編譯器編譯之后,最終變?yōu)樽止?jié)碼指令存儲(chǔ)在Code屬性表中,即Code屬性表是方法表的一部分。但并非所有的方法表都存在該屬性,比如接口或者抽象類(lèi)中的抽象方法就不存在Code屬性表。Code屬性表的結(jié)構(gòu)如下。
| 名稱 | 類(lèi)型 | 數(shù)量 |
|---|---|---|
| attribute_name_index | u2 | 1 |
| attribute_length | u4 | 1 |
| max_stack | u2 | 1 |
| max_locals | u2 | 1 |
| code_length | u4 | 1 |
| code | u1 | code_length |
| exception_table_length | u2 | 1 |
| exception_table | exception_info | exception_length |
| attributes_count | u2 | 1 |
| attributes | attribute_info | attributes_count |
attribute_name_index和attribute_length
attribute_name_index表示該屬性表的名稱,即Code,是指向常量池中類(lèi)型為CANSTANT_Utf8_info的常量的索引。
attribute_length表示該屬性值的長(zhǎng)度。max_stack
代表了操作數(shù)棧深度的最大值。在方法執(zhí)行的任意時(shí)刻,操作數(shù)棧都不會(huì)超過(guò)這個(gè)深度。虛擬機(jī)運(yùn)行的時(shí)候需要根據(jù)這個(gè)值來(lái)分配幀棧中的操作深度。max_locals
代表了局部變量表所需要的存儲(chǔ)空間。max_locals的單位是Slot,Slot是虛擬機(jī)為局部變量分配內(nèi)存所使用的最小單位。對(duì)于byte、char、float、int、short、boolean、returnAddress等長(zhǎng)度不超過(guò)32位的數(shù)據(jù)類(lèi)型,每個(gè)局部變量占用一個(gè)Slot,double和long這兩種64位的數(shù)據(jù)類(lèi)型則需要兩個(gè)Slot。方法參數(shù)(包括實(shí)例方法中的隱藏參數(shù)this)、顯式異常處理器的參數(shù)(try-catch中catch塊所定義的異常)、方法體中定義的局部變量都需要使用局部變量表來(lái)存放。局部變量表中的Slot可以重用,當(dāng)代碼執(zhí)行超過(guò)一個(gè)局部變量的作用域時(shí),這個(gè)局部變量所占用的Slot可以被其他局部變量所使用。Javac編譯器會(huì)根據(jù)變量的作用域來(lái)分配Slot給各個(gè)變量使用,然后計(jì)算出max_locals的大小。code_length和code
code_length代表字節(jié)碼長(zhǎng)度,即字節(jié)碼指令的個(gè)數(shù),也就是code的長(zhǎng)度。雖然是u4類(lèi)型(2^32),但虛擬機(jī)規(guī)范中明確規(guī)定一個(gè)方法不允許超過(guò)65535條字節(jié)碼指令,即它只使用了u2的長(zhǎng)度,超過(guò)這個(gè)長(zhǎng)度,Javac編譯器會(huì)拒絕編譯。code中存儲(chǔ)的是字節(jié)碼指令的一系列字節(jié)流。對(duì)于字節(jié)碼指令,每個(gè)指令都是單字節(jié)(u1類(lèi)型)。當(dāng)虛擬機(jī)讀取到code中的一個(gè)字節(jié)碼時(shí),就可以找出對(duì)應(yīng)的這個(gè)字節(jié)碼對(duì)應(yīng)的指令,并且可以知道這個(gè)指令后面是否需要跟隨參數(shù)以及參數(shù)應(yīng)當(dāng)如何理解。因?yàn)樽止?jié)碼指令是用1個(gè)字節(jié)(8位,2^8=256)來(lái)表示,所以一共可以表示256條指令。目前,Java虛擬機(jī)規(guī)范已經(jīng)定義了其中約200條編碼值對(duì)應(yīng)的指令含義。exception_table_length和exception_table
顯式異常處理表集合,對(duì)于Code屬性來(lái)說(shuō)并不是必須的,表示的是try-catch中的異常信息描述。
Code屬性是Class文件中最重要的一個(gè)屬性,如果把一個(gè)Java程序的信息分為代碼和元數(shù)據(jù)兩部分,那么在整個(gè)Class文件中,Code屬性用于描述代碼,所有的其他數(shù)據(jù)項(xiàng)目都用于描述元數(shù)據(jù)。
2、Exceptions屬性
屬于方法表中的一部分。作用是列出出方法中可能拋出的受檢查異常,也就是方法描述時(shí)在throws 關(guān)鍵字后面列出的異常。
3、LineNumberTable屬性
屬于Code屬性的一部分。用于描述Java源代碼行號(hào)與字節(jié)碼行號(hào)之間的對(duì)應(yīng)關(guān)系,它并不是運(yùn)行時(shí)必須的屬性,默認(rèn)生成,可以在javac命令中使用 -g:none或-g:lines屬性取消生成該信息。不生成該信息對(duì)程序運(yùn)行的影響:當(dāng)拋出異常時(shí),堆棧中將不會(huì)顯示出錯(cuò)的行號(hào),并且在調(diào)試程序的時(shí)候,也無(wú)法按照源碼行來(lái)設(shè)置斷點(diǎn)。
4、LocalVariableTable屬性
屬于Code屬性的一部分。用于描述幀棧中局部變量表中的變量與Java源代碼定義的變量之間的關(guān)系,不是運(yùn)行時(shí)必須的屬性,默認(rèn)生成,可以在javac命令中使用 -g:none或-g:vars屬性取消生成該信息。不生成該信息的影響:當(dāng)其他人引用該方法時(shí),所有的參數(shù)名稱都會(huì)丟失,IDE會(huì)使用類(lèi)似arg0、arg1等占位符代替原有的參數(shù)名,給代碼編寫(xiě)帶來(lái)不便。
5、ConstantValue屬性
屬于字段表中的一部分。作用是通知虛擬機(jī)自動(dòng)為靜態(tài)變量賦值,只有被static關(guān)鍵字修飾的變量(類(lèi)變量)才可以使用該屬性。虛擬機(jī)對(duì)類(lèi)變量和實(shí)例變量的賦值方式和時(shí)機(jī)有所不同。對(duì)于實(shí)例變量的賦值是在實(shí)例構(gòu)造器<init>方法中進(jìn)行的。對(duì)于類(lèi)變量,如果是同時(shí)有static和final修飾的基本類(lèi)型數(shù)據(jù)或String類(lèi)型數(shù)據(jù),則會(huì)生成ConstantValue屬性來(lái)進(jìn)行初始化,否則在<clinit>方法中進(jìn)行初始化。
實(shí)例分析
定義一個(gè)父類(lèi)Animal,兩個(gè)接口Eat、Sleep,一個(gè)要分析的Rabbit類(lèi)。
package constructor;
public class Animal {
protected String weight;
public String getWeight() {
return weight;
}
public void setWeight(String weight) {
this.weight = weight;
}
}
package constructor;
public interface Sleep {
void sleep();
}
package constructor;
public interface Eat {
void eat();
}
package constructor;
public class Rabbit extends Animal implements Eat, Sleep{
private String nickName;
private int age;
public static final boolean isCute = true;
@Override
public void eat() {
System.out.println("I eat grass");
}
@Override
public void sleep() {
System.out.println("I sleep well");
}
public String play(int temperature){
if(temperature > 10){
return "I want to play outside";
}else {
return "I want to stay at home";
}
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
使用命令javac *.java編譯所有源文件,生成class文件。(如果僅僅單獨(dú)編譯Rabbit.java文件會(huì)提示找不到類(lèi)Sleep、Eat和Animal等)。
使用javap -verbose Rabbit.class命令查看Rabbit.class文件的字節(jié)碼內(nèi)容。
Classfile /Users/yue/Documents/workspace/idea/datacenter/src/test/constructor/Rabbit.class
Last modified 2017-7-9; size 1092 bytes
MD5 checksum 64e6283bb3c70e9c41fb2f72e09aae13
Compiled from "Rabbit.java"
public class constructor.Rabbit extends constructor.Animal implements constructor.Eat,constructor.Sleep
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #11.#41 // constructor/Animal."<init>":()V
#2 = Fieldref #42.#43 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #44 // I eat grass
#4 = Methodref #45.#46 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = String #47 // I sleep well
#6 = String #48 // I want to play outside
#7 = String #49 // I want to stay at home
#8 = Fieldref #10.#50 // constructor/Rabbit.nickName:Ljava/lang/String;
#9 = Fieldref #10.#51 // constructor/Rabbit.age:I
#10 = Class #52 // constructor/Rabbit
#11 = Class #53 // constructor/Animal
#12 = Class #54 // constructor/Eat
#13 = Class #55 // constructor/Sleep
#14 = Utf8 nickName
#15 = Utf8 Ljava/lang/String;
#16 = Utf8 age
#17 = Utf8 I
#18 = Utf8 isCute
#19 = Utf8 Z
#20 = Utf8 ConstantValue
#21 = Integer 1
#22 = Utf8 <init>
#23 = Utf8 ()V
#24 = Utf8 Code
#25 = Utf8 LineNumberTable
#26 = Utf8 eat
#27 = Utf8 sleep
#28 = Utf8 play
#29 = Utf8 (I)Ljava/lang/String;
#30 = Utf8 StackMapTable
#31 = Utf8 getNickName
#32 = Utf8 ()Ljava/lang/String;
#33 = Utf8 setNickName
#34 = Utf8 (Ljava/lang/String;)V
#35 = Utf8 getAge
#36 = Utf8 ()I
#37 = Utf8 setAge
#38 = Utf8 (I)V
#39 = Utf8 SourceFile
#40 = Utf8 Rabbit.java
#41 = NameAndType #22:#23 // "<init>":()V
#42 = Class #56 // java/lang/System
#43 = NameAndType #57:#58 // out:Ljava/io/PrintStream;
#44 = Utf8 I eat grass
#45 = Class #59 // java/io/PrintStream
#46 = NameAndType #60:#34 // println:(Ljava/lang/String;)V
#47 = Utf8 I sleep well
#48 = Utf8 I want to play outside
#49 = Utf8 I want to stay at home
#50 = NameAndType #14:#15 // nickName:Ljava/lang/String;
#51 = NameAndType #16:#17 // age:I
#52 = Utf8 constructor/Rabbit
#53 = Utf8 constructor/Animal
#54 = Utf8 constructor/Eat
#55 = Utf8 constructor/Sleep
#56 = Utf8 java/lang/System
#57 = Utf8 out
#58 = Utf8 Ljava/io/PrintStream;
#59 = Utf8 java/io/PrintStream
#60 = Utf8 println
{
public static final boolean isCute;
descriptor: Z
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
ConstantValue: int 1
public constructor.Rabbit();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method constructor/Animal."<init>":()V
4: return
LineNumberTable:
line 11: 0
public void eat();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String I eat grass
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 21: 0
line 22: 8
public void sleep();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #5 // String I sleep well
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 26: 0
line 27: 8
public java.lang.String play(int);
descriptor: (I)Ljava/lang/String;
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: iload_1
1: bipush 10
3: if_icmple 9
6: ldc #6 // String I want to play outside
8: areturn
9: ldc #7 // String I want to stay at home
11: areturn
LineNumberTable:
line 30: 0
line 31: 6
line 33: 9
StackMapTable: number_of_entries = 1
frame_type = 9 /* same */
public java.lang.String getNickName();
descriptor: ()Ljava/lang/String;
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #8 // Field nickName:Ljava/lang/String;
4: areturn
LineNumberTable:
line 38: 0
public void setNickName(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: putfield #8 // Field nickName:Ljava/lang/String;
5: return
LineNumberTable:
line 42: 0
line 43: 5
public int getAge();
descriptor: ()I
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #9 // Field age:I
4: ireturn
LineNumberTable:
line 46: 0
public void setAge(int);
descriptor: (I)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: iload_1
2: putfield #9 // Field age:I
5: return
LineNumberTable:
line 50: 0
line 51: 5
}
SourceFile: "Rabbit.java"
內(nèi)容摘抄自《深入理解Java虛擬機(jī)》
