日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

62.类文件结构(平台无关性、类文件结构)

發(fā)布時間:2024/9/27 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 62.类文件结构(平台无关性、类文件结构) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

62.類文件結構
62.1.平臺無關性
62.2.類文件結構

62.類文件結構

62.1.平臺無關性

Java 是與平臺無關的語言,這得益于 Java 源代碼編譯后生成的存儲字節(jié)碼的文件,即 Class 文件,以及 Java 虛擬機的實現(xiàn)。不僅使用 Java 編譯器可以把 Java 代碼編譯成存儲字節(jié)碼的 Class 文件,使用 JRuby 等其他語言的編譯器也可以把程序代碼編譯成 Class 文件,虛擬機并不關心 Class 的來源是什么語言,只要它符合一定的結構,就可以在 Java 中運行。Java 語言中的各種變量、關鍵字和運算符的語義最終都是由多條字節(jié)碼命令組合而成的,因此字節(jié)碼命令所能提供的語義描述能力肯定會比 Java 語言本身更強大,這便為其他語言實現(xiàn)一些有別于 Java 的語言特性提供了基礎,而且這也正是在類加載時要進行安全驗證的原因。

62.2.類文件結構

Class 文件是一組以8位字節(jié)為基礎單位的二進制流,各個數(shù)據(jù)項目嚴格按照順序緊湊地排列在 Class 文件中,中間沒有添加任何分隔符,這使得整個 Class 文件中存儲的內(nèi)容幾乎全部都是程序運行的必要數(shù)據(jù)。根據(jù) Java 虛擬機規(guī)范的規(guī)定,Class 文件格式采用一種類似于 C 語言結構體的偽結構來存儲,這種偽結構中只有兩種數(shù)據(jù)類型:無符號數(shù)和表。無符號數(shù)屬于基本數(shù)據(jù)類型,以 u1、u2、u4、u8 來分別代表 1、2、4、8 個字節(jié)的無符號數(shù)。表是由多個無符號數(shù)或其他表作為數(shù)據(jù)項構成的符合數(shù)據(jù)類型,所有的表都習慣性地以“_info”結尾。

整個 Class 文件本質(zhì)上就是一張表,它由如下所示的數(shù)據(jù)項構成。

從表中可以看出,無論是無符號數(shù)還是表,當需要描述同一類型但數(shù)量不定的多個數(shù)據(jù)時,經(jīng)常會使用一個前置的容量計數(shù)器加若干個連續(xù)的該數(shù)據(jù)項的形式,稱這一系列連續(xù)的摸一個類型的數(shù)據(jù)為某一類型的集合,比如,fields_count 個 field_info 表數(shù)據(jù)構成了字段表集合。這里需要說明的是:Class 文件中的數(shù)據(jù)項,都是嚴格按照上表中的順序和數(shù)量被嚴格限定的,每個字節(jié)代表的含義,長度,先后順序等都不允許改變。

下表列出了 Class 文件中各個數(shù)據(jù)項的具體含義:

從表中可以看出,無論是無符號數(shù)還是表,當需要描述同一類型但數(shù)量不定的多個數(shù)據(jù)時,經(jīng)常會在其前面使用一個前置的容量計數(shù)器來記錄其數(shù)量,而便跟著若干個連續(xù)的數(shù)據(jù)項,稱這一系列連續(xù)的某一類型的數(shù)據(jù)為某一類型的集合,如:fields_count 個 field_info 表數(shù)據(jù)便組成了方法表集合。這里需要注意的是:Class 文件中各數(shù)據(jù)項是按照上表的順序和數(shù)量被嚴格限定的,每個字節(jié)代表的含義、長度、先后順序都不允許改變。

magic 與 version
每個 Class 文件的頭 4 個字節(jié)稱為魔數(shù)(magic),它的唯一作用是判斷該文件是否為一個能被虛擬機接受的 Class 文件。它的值固定為 0xCAFEBABE。緊接著 magic 的 4 個字節(jié)存儲的是 Class 文件的次版本號和主版本號,高版本的 JDK 能向下兼容低版本的 Class 文件,但不能運行更高版本的 Class 文件。

constant_pool
major_version 之后是常量池(constant_pool)的入口,它是 Class 文件中與其他項目關聯(lián)最多的數(shù)據(jù)類型,也是占用 Class 文件空間最大的數(shù)據(jù)項目之一。
常量池中主要存放兩大類常量:字面量和符號引用。字面量比較接近于 Java 層面的常量概念,如文本字符串、被聲明為 final 的常量值等。而符號引用總結起來則包括了下面三類常量:
?類和接口的全限定名(即帶有包名的 Class 名,如:org.lxh.test.TestClass)
?字段的名稱和描述符(private、static 等描述符)
?方法的名稱和描述符(private、static 等描述符)

虛擬機在加載 Class 文件時才會進行動態(tài)連接,也就是說,Class 文件中不會保存各個方法和字段的最終內(nèi)存布局信息,因此,這些字段和方法的符號引用不經(jīng)過轉換是無法直接被虛擬機使用的。當虛擬機運行時,需要從常量池中獲得對應的符號引用,再在類加載過程中的解析階段將其替換為直接引用,并翻譯到具體的內(nèi)存地址中。

這里說明下符號引用和直接引用的區(qū)別與關聯(lián):
?符號引用:符號引用以一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時能無歧義地定位到目標即可。符號引用與虛擬機實現(xiàn)的內(nèi)存布局無關,引用的目標并不一定已經(jīng)加載到了內(nèi)存中。
?直接引用:直接引用可以是直接指向目標的指針、相對偏移量或是一個能間接定位到目標的句柄。直接引用是與虛擬機實現(xiàn)的內(nèi)存布局相關的,同一個符號引用在不同虛擬機實例上翻譯出來的直接引用一般不會相同。如果有了直接引用,那說明引用的目標必定已經(jīng)存在于內(nèi)存之中了。

常量池中的每一項常量都是一個表,共有 11 種(JDK1.7 之前)結構各不相同的表結構數(shù)據(jù),沒中表開始的第一位是一個 u1 類型的標志位(1-12,缺少 2),代表當前這個常量屬于的常量類型。11 種常量類型所代表的具體含義如下表所示:

這 11 種常量類型各自均有自己的結構。在 CONSTANT_Class_info 型常量的結構中有一項 name_index 屬性,該常屬性中存放一個索引值,指向常量池中一個 CONSTANT_Utf8_info 類型的常量,該常量中即保存了該類的全限定名字符串。而 CONSTANT_Fieldref_info、CONSTANT_Methodref_info、CONSTANT_InterfaceMethodref_info 型常量的結構中都有一項index屬性,存放該字段或方法所屬的類或接口的描述符 CONSTANT_Class_info 的索引項。另外,最終保存的諸如 Class 名、字段名、方法名、修飾符等字符串都是一個 CONSTANT_Utf8_info 類型的常量,也因此,Java 中方法和字段名的最大長度也即是CONSTANT_Utf8_info 型常量的最大長度,在 CONSTANT_Utf8_info 型常量的結構中有一項 length 屬性,它是 u2 類型的,即占用 2 個字節(jié),那么它的最大的 length 即為 65535。因此,Java 程序中如果定義了超過 64KB 英文字符的變量或方法名,將會無法編譯。

下表給出了常量池中 11 種數(shù)據(jù)類型的結構:


access_flag
在常量池結束之后,緊接著的 2 個字節(jié)代表訪問標志(access_flag),這個標志用于識別一些類或接口層次的訪問信息,包括:這個 Class 是類還是接口,是否定義為 public 類型,abstract 類型,如果是類的話,是否聲明為 final,等等。每種訪問信息都由一個十六進制的標志值表示,如果同時具有多種訪問信息,則得到的標志值為這幾種訪問信息的標志值的邏輯或。

this_class、super_class、interfaces
類索引(this_class)和父類索引(super_class)都是一個 u2 類型的數(shù)據(jù),而接口索引集合(interfaces)則是一組 u2 類型的數(shù)據(jù)集合,Class 文件中由這三項數(shù)據(jù)來確定這個類的繼承關系。類索引、父類索引和接口索引集合都按照順序排列在訪問標志之后,類索引和父類索引兩個 u2 類型的索引值表示,它們各自指向一個類型為 COMNSTANT_Class_info 的類描述符常量,通過該常量中的索引值找到定義在 COMNSTANT_Utf8_info 類型的常量中的全限定名字符串。而接口索引集合就用來描述這個類實現(xiàn)了哪些接口,這些被實現(xiàn)的接口將按 implements 語句(如果這個類本身是個接口,則應當是 extend 語句)后的接口順序從左到右排列在接口的索引集合中。

fields
字段表(field_info)用于描述接口或類中聲明的變量。字段包括了類級變量或?qū)嵗壸兞?#xff0c;但不包括在方法內(nèi)聲明的變量。字段的名字、數(shù)據(jù)類型、修飾符等都是無法固定的,只能引用常量池中的常量來描述。下面是字段表的最種格式:

其中的 access_flags 與類中的 access_flagsfei 類似,是表示數(shù)據(jù)類型的修飾符,如 public、static、volatile 等。后面的 name_index 和 descriptor_index 都是對常量池的引用,分別代表字段的簡單名稱及字段和方法的描述符。這里簡單解釋下“簡單名稱”、“描述符”和“全限定名”這三種特殊字符串的概念。

前面有所提及,全限定名即指一個事物的完整的名稱,如在 org.lxh.test 包下的 TestClass 類的全限定名為:org/lxh/test/TestClass,即把包名中的“.”改為“/”,為了使連續(xù)的多個全限定名之間不產(chǎn)生混淆,在使用時最后一般會加入一個“,”來表示全限定名結束。簡單名稱則是指沒有類型或參數(shù)修飾的方法或字段名稱,如果一個類中有這樣一個方法 boolean get(int name)和一個變量 private final static int m,則他們的簡單名稱則分別為 get()和 m。

而描述符的作用則是用來描述字段的數(shù)據(jù)類型、方法的參數(shù)列表(包括數(shù)量、類型以及順序等)和返回值的。根據(jù)描述符規(guī)則,詳細的描述符標示字的含義如下表所示:

對于數(shù)組類型,每一維度將使用一個前置的 “[” 字符來描述,如一個整數(shù)數(shù)組 “int [][]” 將為記錄為 “[[I” ,而一個 String 類型的數(shù)組 “String[]” 將被記錄為 “[Ljava/lang/String” 。

用方法描述符描述方法時,按照先參數(shù)后返回值的順序描述,參數(shù)要按照嚴格的順序放在一組小括號內(nèi),如方法 int getIndex(String name,char[] tgc,int start,int end,char target) 的描述符為 “(Ljava/lang/String[CIIC)I”。

字段表包含的固定數(shù)據(jù)項目到 descriptor_index 為止就結束了,但是在它之后還緊跟著一個屬性表集合用于存儲一些額外的信息。比如,如果在類中有如下字段的聲明:staticfinalint m = 2;那就可能會存在一項名為ConstantValue 的屬性,它指向常量 2。關于 attribute_info 的詳細內(nèi)容,在后面關于屬性表的項目中會有詳細介紹。

最后需要注意一點:字段表集合中不會列出從父類或接口中繼承而來的字段,但有可能列出原本 Java 代碼中不存在的字段。比如在內(nèi)部類中為了保持對外部類的訪問性,會自動添加指向外部類實例的字段。

methods
方法表(method_info)的結構與屬性表的結構相同,不過多贅述。方法里的 Java 代碼,經(jīng)過編譯器編譯成字節(jié)碼指令后,存放在方法屬性表集合中一個名為“Code”的屬性里,關于屬性表的項目,同樣會在后面詳細介紹。

與字段表集合相對應,如果父類方法在子類中沒有被覆寫,方法表集合中就不會出現(xiàn)來自父類的方法信息。但同樣,有可能會出現(xiàn)由編譯器自動添加的方法,最典型的便是類構造器 “” 方法和實例構造器 “” 方法。

在 Java 語言中,要重載一個方法,除了要與原方法具有相同的簡單名稱外,還要求必須擁有一個與原方法不同的特征簽名,特征簽名就是一個方法中各個參數(shù)在常量池中的字段符號引用的集合,也就是因為返回值不會包含在特征簽名之中,因此 Java 語言里無法僅僅依靠返回值的不同來對一個已有方法進行重載。

ttributes
屬性表(attribute_info)在前面已經(jīng)出現(xiàn)過多系,在 Class 文件、字段表、方法表中都可以攜帶自己的屬性表集合,以用于描述某些場景專有的信息。

屬性表集合的限制沒有那么嚴格,不再要求各個屬性表具有嚴格的順序,并且只要不與已有的屬性名重復,任何人實現(xiàn)的編譯器都可以向?qū)傩员碇袑懭胱约憾x的屬性信息,但 Java 虛擬機運行時會忽略掉它不認識的屬性。Java 虛擬機規(guī)范中預定義了 9 項虛擬機應當能識別的屬性(JDK1.5 后又增加了一些新的特性,因此不止下面 9 項,但下面 9 項是最基本也是必要,出現(xiàn)頻率最高的),如下表所示:

對于每個屬性,它的名稱都需要從常量池中引用一個 CONSTANT_Utf8_info 類型的常量來表示,每個屬性值的結構是完全可以自定義的,只需說明屬性值所占用的位數(shù)長度即可。一個符合規(guī)則的屬性表至少應具有 “attribute_name_info”、“attribute_length” 和至少一項信息屬性。

Code 屬性
前面已經(jīng)說過,Java 程序方法體中的代碼講過 javac 編譯后,生成的字節(jié)碼指令便會存儲在 Code 屬性中,但并非所有的方法表都必須存在這個屬性,比如接口或抽象類中的方法就不存在 Code 屬性。如果方法表有 Code 屬性存在,那么它的結構將如下表所示:

attribute_name_index 是一項指向 CONSTANT_Utf8_info 型常量的索引,常量值固定為 “Code”,它代表了該屬性的名稱。attribute_length 指示了屬性值的長度,由于屬性名稱索引與屬性長度一共是 6 個字節(jié),所以屬性值的長度固定為整個屬性表的長度減去 6 個字節(jié)。

max_stack 代表了操作數(shù)棧深度的最大值,max_locals 代表了局部變量表所需的存儲空間,它的單位是Slot,并不是在方法中用到了多少個局部變量,就把這些局部變量所占 Slot 之和作為 max_locals 的值,原因是局部變量表中的 Slot 可以重用。
code_length 和 code 用來存儲 Java 源程序編譯后生成的字節(jié)碼指令。code 用于存儲字節(jié)碼指令的一系列字節(jié)流,它是 u1 類型的單字節(jié),因此取值范圍為 0x00 到 0xFF,那么一共可以表達 256 條指令,目前,Java 虛擬機規(guī)范已經(jīng)定義了其中 200 條編碼值對應的指令含義。code_length 雖然是一個 u4 類型的長度值,理論上可以達到 2^32-1,但是虛擬機規(guī)范中限制了一個方法不允許超過 65535 條字節(jié)碼指令,如果超過了這個限制,Javac 編譯器將會拒絕編譯。

字節(jié)碼指令之后是這個方法的顯式異常處理表集合(exception_table),它對于 Code 屬性來說并不是必須存在的。它的格式如下表所示:

它包含四個字段,這些字段的含義為:如果字節(jié)碼從第 start_pc 行到第 end_pc 行之間(不含 end_pc 行)出現(xiàn)了類型為 catch_type 或其子類的異常(catch_type為指向一個 CONSTANT_Class_info 型常量的索引),則轉到第 handler_pc 行繼續(xù)處理,當 catch_pc 的值為 0 時,代表人和的異常情況都要轉到 handler_pc 處進行處理。異常表實際上是 Java 代碼的一部分,編譯器使用異常表而不是簡單的跳轉命令來實現(xiàn) Java 異常即 finally 處理機制,也因此,finally 中的內(nèi)容會在 try 或 catch 中的 return 語句之前執(zhí)行,并且在 try 或 catch 跳轉到 finally 之前,會將其內(nèi)部需要返回的變量的值復制一份副本到最后一個本地表量表的 Slot中,也因此便有了http://blog.csdn.net/ns_code/article/details/17485221這篇文章中出現(xiàn)的情況。

Code 屬性是 Class 文件中最重要的一個屬性,如果把一個 Java 程序中的信息分為代碼和元數(shù)據(jù)兩部分,那么在整個 Class 文件里,Code 屬性用于描述代碼,所有的其他數(shù)據(jù)項目都用于描述元數(shù)據(jù)。

Exception 屬性
這里的 Exception 屬性的作用是列舉出方法中可能拋出的受查異常,也就是方法描述時在 throws 關鍵字后面列舉的異常。它的結構很簡單,只有 attribute_name_index、attribute_length、number_of_exceptions、exception_index_table 四項,從字面上便很容易理解,這里不再詳述。
LineNumberTable 屬性
它用于描述 Java 源碼行號與字節(jié)碼行號之間的對應關系。
LocalVariableTable 屬性
它用于描述棧幀中局部變量表中的變量與 Java 源碼中定義的變量之間的對應關系。
SourceFile 屬性
它用于記錄生成這個 Class 文件的源碼文件名稱。
ConstantValue 屬性
ConstantValue 屬性的作用是通知虛擬機自動為靜態(tài)變量賦值,只有被 static 修飾的變量才可以使用這項屬性。在 Java 中,對非 static 類型的變量(也就是實例變量)的賦值是在實例構造器方法中進行的;而對于類變量(static 變量),則有兩種方式可以選擇:在類構造其中賦值,或使用 ConstantValue 屬性賦值。

目前 Sun Javac 編譯器的選擇是:如果同時使用 final 和 static 修飾一個變量(即全局常量),并且這個變量的數(shù)據(jù)類型是基本類型或 String 的話,就生成 ConstantValue 屬性來進行初始化(編譯時 Javac 將會為該常量生成 ConstantValue 屬性,在類加載的準備階段虛擬機便會根據(jù) ConstantValue 為常量設置相應的值),如果該變量沒有被 final 修飾,或者并非基本類型及字符串,則選擇在方法中進行初始化。

雖然有 final 關鍵字才更符合”ConstantValue“的含義,但在虛擬機規(guī)范中并沒有強制要求字段必須用 final 修飾,只要求了字段必須用 static 修飾,對 final 關鍵字的要求是 Javac 編譯器自己加入的限制。因此,在實際的程序中,只有同時被 final 和 static 修飾的字段才有 ConstantValue 屬性。而且 ConstantValue 的屬性值只限于基本類型和 String,很明顯這是因為它從常量池中也只能夠引用到基本類型和 String 類型的字面量。

ConstantValue 屬性的作用是通知虛擬機自動為靜態(tài)變量賦值,只有被 static 修飾的變量才可以使用這項屬性。在 Java 中,對非 static 類型的變量(也就是實例變量)的賦值是在實例構造器方法中進行的;而對于類變量(static 變量),則有兩種方式可以選擇:在類構造其中賦值,或使用 ConstantValue 屬性賦值。

目前 Sun Javac 編譯器的選擇是:如果同時使用 final 和 static 修飾一個變量(即全局常量),并且這個變量的數(shù)據(jù)類型是基本類型或 String 的話,就生成 ConstantValue 屬性來進行初始化(編譯時 Javac 將會為該常量生成 ConstantValue 屬性,在類加載的準備階段虛擬機便會根據(jù) ConstantValue 為常量設置相應的值),如果該變量沒有被 final 修飾,或者并非基本類型及字符串,則選擇在方法中進行初始化。

雖然有 final 關鍵字才更符合”ConstantValue“的含義,但在虛擬機規(guī)范中并沒有強制要求字段必須用 final 修飾,只要求了字段必須用 static 修飾,對 final 關鍵字的要求是 Javac 編譯器自己加入的限制。因此,在實際的程序中,只有同時被 final 和 static 修飾的字段才有 ConstantValue 屬性。而且 ConstantValue 的屬性值只限于基本類型和 String,很明顯這是因為它從常量池中也只能夠引用到基本類型和 String 類型的字面量。

下面簡要說明下 final、static、static final 修飾的字段賦值的區(qū)別:
?static 修飾的字段在類加載過程中的準備階段被初始化為 0 或 null 等默認值,而后在初始化階段(觸發(fā)類構造器)才會被賦予代碼中設定的值,如果沒有設定值,那么它的值就為默認值。
?final 修飾的字段在運行時被初始化(可以直接賦值,也可以在實例構造器中賦值),一旦賦值便不可更改;
?static final 修飾的字段在 Javac 時生成 ConstantValue 屬性,在類加載的準備階段根據(jù)ConstantValue的值為該字段賦值,它沒有默認值,必須顯式地賦值,否則 Javac 時會報錯。可以理解為在編譯期即把結果放入了常量池中。
InnerClasses 屬性
該屬性用于記錄內(nèi)部類與宿主類之間的關聯(lián)。如果一個類中定義了內(nèi)部類,那么編譯器將會為它及它所包含的內(nèi)部類生成 InnerClasses 屬性
Deprecated 屬性和 Synthetic 屬性
該屬性用于表示某個類、字段和方法,已經(jīng)被程序作者定為不再推薦使用,它可以通過在代碼中使用 @Deprecated 注釋進行設置。
Synthetic 屬性
該屬性代表此字段或方法并不是 Java 源代碼直接生成的,而是由編譯器自行添加的,如 this 字段和實例構造器、類構造器等。

總結

以上是生活随笔為你收集整理的62.类文件结构(平台无关性、类文件结构)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。