class文件是一組以8位字節為基礎單位的二進制流,緊密排列沒有分隔符。 class文件數據結構,包括兩種數據類型:無符號數 和 表。 無符號數:屬于基本的數據類型,u1,u2,u4,u8分別代表1個字節,2個字節,4個字節,8個字節。 表:表是由多個無符號數或其他表作為數據項構成的符合數據類型,所有表都習慣性地以“_info”結尾。 
魔數:前四個字節,為確定的0xCA FE BA BE。 class文件此版本號+class文件主版本號:第5,6與第7,8字節 
第一個字節為常量池容量的計數值,計數從1開始,將第0項常量空出來是為了滿足后面某些指向常量池的索引值的數據在特定情況下需要表達“不引用任何一個常量池項目”的意思。例:該字節為0x0016,轉化為十進制為22,這就代表常量池有21項常量,索引值為1~21。
常量池之中主要存放兩大類常量:字面量,符號引用。 字面量:比較接近于java語言的常量概念,如文本字符串,被聲明為final的常量值。 符號引用:屬于編譯原理方面的概念,包括:類和接口的全限定名 和 字段的名稱和描述符 和 方法的名稱和描述符。
常量池中每一項常量都是一個表,共有11種不同的常量數據結構表:

常量池結束之后,緊接著的2個字節代表訪問標志,這標志用于識別一些類或接口的層次的訪問信息,包括:這個Class是類還是接口;是否定義為public;是否為abstract類型;是否為final等等。
總共兩個字節16位可以使用,當前之定義了其中的8個
類索引與父類索引:用于確定這個類與父類的全限定名。各用一個u2類型常量池引用表示。 接口索引:入口第一項為u2類型的計數器,表示索引表的容量。若沒有接口則為0。若有則在后面通過相應數量的u2類型常量池引用表示。
字段表集合:用于描述接口或類中聲明的變量,包括類級變量與實例級變量,并不包含局部變量。 字段表結構:標志位,跟隨其后的是兩項索引值:name_index與descriptor_index。他們都是對常量池的引用,分別代表著字段的簡單名稱及字段和方法的描述符。(這里表示字段的類型,字段就是變量)。attributes_count,attribute_info表示屬性,后邊有介紹。 
標志位:跟類的訪問標志相似。 
描述符:是用來描述字段的數據類型,方法的參數列表和返回值。基本類型(byte,char,double,float,int,long,short,boolean)及void都是用一個大學字符來表示,而對象類型用字符L加對象的全限定名來表示。
對于數組如:java.lang.String[][] 表示為:”[[Ljava/lang/String;” 用描述符描述方法時:先參數后返回值:”int f(int i, char c, String[] ss)” 表示為:”(IC[Ljava/lang/String)I”
方法表結構與字段表結構一樣,但各項含義有所不同。 方法訪問標志也與字段訪問標志基本一致。 方法名有了,但方法中的代碼去哪了?方法里的代碼經過編譯器編譯成字節碼指令之后,存放在方法屬性表集合中一個名為“Code”的屬性里,屬性表作為Class文件格式中最具擴展行的一種數據項目,將在后邊詳解。
如果父類方法在子類中沒有被重寫,方法表集合中就不會出現來自父類的方法信息。有可能會出現來自編譯器自動添加的類構造器《clinit》方法與《init》方法,這兩個將在第十章說明。
屬性表在前面的講解中出現過多次,在Class文件,字段表,方法表中都可以攜帶自己的屬性表集合,以用于描述某些場景專有的信息。 
還有時間,接下來簡單學習一下各個屬性的含義:

attribute_name_index:是一項指向CONSTANT_Utf8_info型常量的索引,常量值固定位“COde”,他代表了該屬性的屬性名稱 attribute_length:指示了屬性值的長度,屬性值長度為屬性表長度減去attribute_name_index與attribute_length的長度(即減6)。 max_stack:代表了操作數棧深度的最大值。虛擬機運行的時候需要根據這個值來分配棧幀中的操作棧深度。 max_locals:局部變量表所需的存儲空間。單位為Slot。byte,char,float,int,short,boolean,reference,returnAddress占1個Slot。double,long占2個Slot。方法參數(包括this),顯示異常處理器的參數,內部局部變量都用該表存放。 code_length與code:用來存儲java源程序編譯后生成的字節碼指令。 exception_table_length與exception_table:用來執行遇到異常時的代碼跳轉處理。如下圖,一個try,catch,finally組合中的幾個跳轉。 
表示方法可能拋出的異常,也就是方法描述時在throws關鍵字后面列舉的異常。
java源碼行號與字節碼行號之間的對應關系。可有可無,會默認生成。沒有這項屬性的后果是拋出異常時將不會顯示出錯行號。
局部變量們的名稱,類型與生存周期,及在棧幀局部變量表中所占空間的位置。

記錄生成這個CLass文件的源碼文件的名稱。
作用是通知虛擬機自動為靜態變量賦值,只有static關鍵字修飾的變量才可以使用這項屬性。 對于非static類型的變量的賦值是在實例構造器《init》方法中進行的。而對于非static類型的變量的賦值是在類構造器《clinit》方法中進行的,或者使用ConstantValue屬性來賦值。目前編譯器的選擇是:如果同時使用final和static來修飾一個變量,并且這個變量的數據類型是基本類型或java.lang.Sring的話,就生成ConstantValue屬性來進行初始化,如果這個變量沒有被fianl修飾,或者并非基本類型及字符串,則選擇在《clinit》方法中就行初始化。
用于記錄內部類與宿主類之間的關聯。如果一個類中定義了內部類,那么編譯器將會為 它及其所包含的內部類 生成InnerClasses屬性。

新聞熱點
疑難解答