溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java類文件的知識點有哪些

發(fā)布時間:2022-03-17 15:49:52 來源:億速云 閱讀:259 作者:iii 欄目:大數(shù)據(jù)

今天小編給大家分享一下java類文件的知識點有哪些的相關知識點,內(nèi)容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

Class文件格式采用一種類似于C語言結(jié)構(gòu)體的偽結(jié)構(gòu)來存儲數(shù)據(jù),這種偽結(jié)構(gòu)中只有兩種數(shù)據(jù)類型:“無符號數(shù)”和“表”。

·無符號數(shù)屬于基本的數(shù)據(jù)類型,以u1、u2、u4、u8來分別代表1個字節(jié)、2個字節(jié)、4個字節(jié)和8個字節(jié)的無符號數(shù),無符號數(shù)可以用來描述數(shù)字、索引引用、數(shù)量值或者按照UTF-8編碼構(gòu)成字符串值。

·表是由多個無符號數(shù)或者其他表作為數(shù)據(jù)項構(gòu)成的復合數(shù)據(jù)類型,為了便于區(qū)分,所有表的命名都習慣性以“_info”結(jié)尾。表用于描述有層次關系的復合結(jié)構(gòu)的數(shù)據(jù),整個Class文件本質(zhì)上也可以視作是一張表

java類文件的知識點有哪些

每個Class文件的頭4個字節(jié)被稱為魔數(shù)(Magic Number),它的唯一作用是確定這個文件是否為一個能被虛擬機接受的Class文件。不僅是Class文件,很多文件格式標準中都有使用魔數(shù)來進行身份識別的習慣,譬如圖片格式,如GIF或者JPEG等在文件頭中都存有魔數(shù)。

Class文件的魔數(shù)取得很有“浪漫氣息”,值為0xCAFEBABE(咖啡寶貝?)

緊接著魔數(shù)的4個字節(jié)存儲的是Class文件的版本號:第5和第6個字節(jié)是次版本號(MinorVersion),第7和第8個字節(jié)是主版本號(Major Version)。Java的版本號是從45開始的,JDK 1.1之后的每個JDK大版本發(fā)布主版本號向上加1(JDK 1.0~1.1使用了45.0~45.3的版本號),高版本的JDK能向下兼容以前版本的Class文件,但不能運行以后版本的Class文件,

常量池

緊接著主、次版本號之后的是常量池入口,常量池可以比喻為Class文件里的資源倉庫,它是Class文件結(jié)構(gòu)中與其他項目關聯(lián)最多的數(shù)據(jù),通常也是占用Class文件空間最大的數(shù)據(jù)項目之一,另外,它還是在Class文件中第一個出現(xiàn)的表類型數(shù)據(jù)項目。

常量池中主要存放兩大類常量:字面量(Literal)和符號引用(Symbolic References)。字面量比較接近于Java語言層面的常量概念,如文本字符串、被聲明為final的常量值等。而符號引用則屬于編譯原理方面的概念,主要包括下面幾類常量:

·被模塊導出或者開放的包(Package)

·類和接口的全限定名(Fully Qualified Name)

·字段的名稱和描述符(Descriptor)

·方法的名稱和描述符

·方法句柄和方法類型(Method Handle、Method Type、Invoke Dynamic)

·動態(tài)調(diào)用點和動態(tài)常量(Dynamically-Computed Call Site、Dynamically-Computed Constant)

Java代碼在進行Javac編譯的時候,并不像C和C++那樣有“連接”這一步驟,而是在虛擬機加載Class文件的時候進行動態(tài)連接(具體見第7章)。也就是說,在Class文件中不會保存各個方法、字段最終在內(nèi)存中的布局信息,這些字段、方法的符號引用不經(jīng)過虛擬機在運行期轉(zhuǎn)換的話是無法得到真正的內(nèi)存入口地址,也就無法直接被虛擬機使用的。當虛擬機做類加載時,將會從常量池獲得對應的符號引用,再在類創(chuàng)建時或運行時解析、翻譯到具體的內(nèi)存地址之中。

常量池中每一項常量都是一個表,最初常量表中共有11種結(jié)構(gòu)各不相同的表結(jié)構(gòu)數(shù)據(jù),后來為了更好地支持動態(tài)語言調(diào)用,額外增加了4種動態(tài)語言相關的常量 [1] ,為了支持Java模塊化系統(tǒng)(Jigsaw),又加入了CONSTANT_Module_info和CONSTANT_Package_info兩個常量,所以截至JDK13,常量表中分別有17種不同類型的常量。

java類文件的知識點有哪些

順便提一下,由于Class文件中方法、字段等都需要引用CONSTANT_Utf8_info型常量來描述名稱,所以CONSTANT_Utf8_info型常量的最大長度也就是Java中方法、字段名的最大長度。而這里的最大長度就是length的最大值,既u2類型能表達的最大值65535。所以Java程序中如果定義了超過64KB英文字符的變量或方法名,即使規(guī)則和全部字符都是合法的,也會無法編譯。

Classfile /D:/BaiduYunDownload/geekbang-lessons/thinking-in-spring/validation/target/classes/org/geekbang/thinking/in/spring/validation/TestClass.class

  Last modified 2020-6-25; size 439 bytes

  MD5 checksum 18760ee8065f9fb68d4dab7bd7450c4c

  Compiled from "TestClass.java"

public class org.geekbang.thinking.in.spring.validation.TestClass

  minor version: 0

  major version: 52

  flags: ACC_PUBLIC, ACC_SUPER

Constant pool:

   #1 = Methodref          #4.#18         // java/lang/Object."<init>":()V

   #2 = Fieldref           #3.#19         // org/geekbang/thinking/in/spring/validation/TestClass.m:I

   #3 = Class              #20            // org/geekbang/thinking/in/spring/validation/TestClass

   #4 = Class              #21            // java/lang/Object

   #5 = Utf8               m

   #6 = Utf8               I

   #7 = Utf8               <init>

   #8 = Utf8               ()V

   #9 = Utf8               Code

  #10 = Utf8               LineNumberTable

  #11 = Utf8               LocalVariableTable

  #12 = Utf8               this

  #13 = Utf8               Lorg/geekbang/thinking/in/spring/validation/TestClass;

  #14 = Utf8               inc

  #15 = Utf8               ()I

  #16 = Utf8               SourceFile

  #17 = Utf8               TestClass.java

  #18 = NameAndType        #7:#8          // "<init>":()V

  #19 = NameAndType        #5:#6          // m:I

  #20 = Utf8               org/geekbang/thinking/in/spring/validation/TestClass

  #21 = Utf8               java/lang/Object

{

  public org.geekbang.thinking.in.spring.validation.TestClass();

    descriptor: ()V

    flags: ACC_PUBLIC

    Code:

      stack=1, locals=1, args_size=1

         0: aload_0

         1: invokespecial #1                  // Method java/lang/Object."<init>":()V

         4: return

      LineNumberTable:

        line 3: 0

      LocalVariableTable:

        Start  Length  Slot  Name   Signature

            0       5     0  this   Lorg/geekbang/thinking/in/spring/validation/TestClass;

  public int inc();

    descriptor: ()I

    flags: ACC_PUBLIC

    Code:

      stack=2, locals=1, args_size=1

         0: aload_0

         1: getfield      #2                  // Field m:I

         4: iconst_1

         5: iadd

         6: ireturn

      LineNumberTable:

        line 7: 0

      LocalVariableTable:

        Start  Length  Slot  Name   Signature

            0       7     0  this   Lorg/geekbang/thinking/in/spring/validation/TestClass;

}

SourceFile: "TestClass.java"

在常量池結(jié)束之后,緊接著的2個字節(jié)代表訪問標志(access_flags),這個標志用于識別一些類或者接口層次的訪問信息,包括:這個Class是類還是接口;是否定義為public類型;是否定義為abstract類型;如果是類的話,是否被聲明為final;

類索引、父類索引和接口索引集合都按順序排列在訪問標志之后,類索引和父類索引用兩個u2類型的索引值表示,它們各自指向一個類型為CONSTANT_Class_info的類描述符常量,通過CONSTANT_Class_info類型的常量中的索引值可以找到定義在CONSTANT_Utf8_info類型的常量中的全限定名字符串。

直到JDK 8中Lambda表達式和接口默認方法的出現(xiàn),InvokeDynamic指令才算在Java語言生成的Class文件中有了用武之地

所以JDK 8中新增的這個屬性,使得編譯器可以

(編譯時加上-parameters參數(shù))將方法名稱也寫進Class文件中,而且MethodParameters是方法表的屬

性,與Code屬性平級的,可以運行時通過反射API獲取。

·將一個局部變量加載到操作棧:iload

·將一個數(shù)值從操作數(shù)棧存儲到局部變量表:istore

·將一個常量加載到操作數(shù)棧:bipush

iload_<n>,它代表了iload_0、iload_1、iload_2和iload_3這幾條指令

·加法指令:iadd、ladd、fadd、dadd

·減法指令:isub、lsub、fsub、dsub

·乘法指令:imul、lmul、fmul、dmul

·除法指令:idiv、ldiv、fdiv、ddiv

·求余指令:irem、lrem、frem、drem

·取反指令:ineg、lneg、fneg、dneg

·位移指令:ishl、ishr、iushr、lshl、lshr、lushr

·按位或指令:ior、lor

·按位與指令:iand、land

·按位異或指令:ixor、lxor

·局部變量自增指令:iinc

·比較指令:dcmpg、dcmpl、fcmpg、fcmpl、lcmp

JDK  1.0.2時改動過invokespecial指令的語義,JDK 7增加了invokedynamic指令,禁止了ret和jsr指令。

類的生命周期

加載-> 連接(驗證,準備,解析)->初始化->使用->卸載。

加載、驗證、準備、初始化和卸載這五個階段的順序是確定的,類型的加載過程必須按照這種順序按部就班地開始,而解析階段則不一定:它在某些情況下可以在初始化階段之后再開始,這是為了支持Java語言的運行時綁定特性(也稱為動態(tài)綁定或晚期綁定)。

public static final int value = 123;

編譯時Javac將會為value生成ConstantValue屬性,在準備階段虛擬機就會根據(jù)Con-stantValue的設置將value賦值為123。

雙親委派模型的工作過程是:如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每一個層次的類加載器都是如此,因此所有的加載請求最終都應該傳送到最頂層的啟動類加載器中,只有當父加載器反饋自己無法完成這個加載請求(它的搜索范圍中沒有找到所需的類)時,子加載器才會嘗試自己去完成加載

首先,是擴展類加載器(Extension Class Loader)被平臺類加載器(Platform Class Loader)取代。這其實是一個很順理成章的變動,既然整個JDK都基于模塊化進行構(gòu)建(原來的rt.jar和tools.jar被拆分成數(shù)十個JMOD文件),其中的Java類庫就已天然地滿足了可擴展的需求,那自然無須再保留<JAVA_HOME>\lib\ext目錄,此前使用這個目錄或者java.ext.dirs系統(tǒng)變量來擴展JDK功能的機制已經(jīng)沒有繼續(xù)存在的價值了,用來加載這部分類庫的擴展類加載器也完成了它的歷史使命。

所有依賴靜態(tài)類型來決定方法執(zhí)行版本的分派動作,都稱為靜態(tài)分派。靜態(tài)分派的最典型應用表現(xiàn)就是方法重載。靜態(tài)分派發(fā)生在編譯階段,因此確定靜態(tài)分派的動作實際上不是由虛擬機來執(zhí)行的,這點也是為何一些資料選擇把它歸入“解析”而不是“分派”的原因。

在Java虛擬機支持以下5條方法調(diào)用字節(jié)碼指令,分別是:

·invokestatic。用于調(diào)用靜態(tài)方法。

·invokespecial。用于調(diào)用實例構(gòu)造器<init>()方法、私有方法和父類中的方法。

·invokevirtual。用于調(diào)用所有的虛方法。

·invokeinterface。用于調(diào)用接口方法,會在運行時再確定一個實現(xiàn)該接口的對象。

·invokedynamic。先在運行時動態(tài)解析出調(diào)用點限定符所引用的方法,然后再執(zhí)行該方法。前面4條調(diào)用指令,分派邏輯都固化在Java虛擬機內(nèi)部,而invokedynamic指令的分派邏輯是由用戶設定的引導方法來決定的。

只要能被invokestatic和invokespecial指令調(diào)用的方法,都可以在解析階段中確定唯一的調(diào)用版本,Java語言里符合這個條件的方法共有靜態(tài)方法、私有方法、實例構(gòu)造器、父類方法4種,再加上被final修飾的方法(盡管它使用invokevirtual指令調(diào)用),這5種方法調(diào)用會在類加載的時候就可以把符號引

用解析為該方法的直接引用。這些方法統(tǒng)稱為“非虛方法”(Non-Virtual Method),與之相反,其他方法就被稱為“虛方法”(Virtual Method)。

解析調(diào)用一定是個靜態(tài)的過程,在編譯期間就完全確定,在類加載的解析階段就會把涉及的符號引用全部轉(zhuǎn)變?yōu)槊鞔_的直接引用,不必延遲到運行期再去完成。而另一種主要的方法調(diào)用形式:分派(Dispatch)調(diào)用則要復雜許多,它可能是靜態(tài)的也可能是動態(tài)的,按照分派依據(jù)的宗量數(shù)可分為單分派和多分派 [1] 。這兩類分派方式兩兩組合就構(gòu)成了靜態(tài)單分派、靜態(tài)多分派、動態(tài)單分派、動態(tài)多分派4種分派組合情況,下面我們來看看虛擬機中的方法分派是如何進行的。

代碼中故意定義了兩個靜態(tài)類型相同,而實際類型不同的變量,但虛擬機(或者準確地說是編譯器)在重載時是通過參數(shù)的靜態(tài)類型而不是實際類型作為

判定依據(jù)的。由于靜態(tài)類型在編譯期可知,所以在編譯階段,Javac編譯器就根據(jù)參數(shù)的靜態(tài)類型決定了會使用哪個重載版本,因此選擇了sayHello(Human)作為調(diào)用目標,并把這個方法的符號引用寫到main()方法里的兩條invokevirtual指令的參數(shù)中。

所有依賴靜態(tài)類型來決定方法執(zhí)行版本的分派動作,都稱為靜態(tài)分派。靜態(tài)分派的最典型應用表現(xiàn)就是方法重載。靜態(tài)分派發(fā)生在編譯階段,因此確定靜態(tài)分派的動作實際上不是由虛擬機來執(zhí)行的,這點也是為何一些資料選擇把它歸入“解析”而不是“分派”的原因。

可見變長參數(shù)的重載優(yōu)先級是最低的。字段永遠不參與多態(tài),哪個類的方法訪問某個名字的字段時,該名字指的就是這個類能看到的那個字段。

重點

正是因為invokevirtual指令執(zhí)行的第一步就是在運行期確定接收者的實際類型,所以兩次調(diào)用中的invokevirtual指令并不是把常量池中方法的符號引用解析到直接引用上就結(jié)束了,還會根據(jù)方法接收者的實際類型來選擇方法版本,這個過程就是Java語言中方法重寫的本質(zhì)。我們把這種在運行期根據(jù)實際類型確定方法執(zhí)行版本的分派過程稱為動態(tài)分派。多態(tài)性的根源在于虛方法調(diào)用指令invokevirtual的執(zhí)行邏輯,那自然我們得出的結(jié)論就只會對方法有效,對字段是無效的,因為字段不使用這條指令。

Java語言是一門靜態(tài)多分派、動態(tài)單分派的語言。

為了程序?qū)崿F(xiàn)方便,具有相同簽名的方法,在父類、子類的虛方法表中都應當具有一樣的索引序號,這樣當類型變換時,僅需要變更查找的虛方法表,就可以從不同的虛方法表中按索引轉(zhuǎn)換出所需的入口地址。虛方法表一般在類加載的連接階段進行初始化,準備了類的變量初始值后,虛擬機會把該類的虛方法表也一同初始化完畢。

動態(tài)類型語言支持

Java虛擬機的字節(jié)碼指令集的數(shù)量自從Sun公司的第一款Java虛擬機問世至今,二十余年間只新增過一條指令,它就是隨著JDK 7的發(fā)布的字節(jié)碼首位新成員——invokedynamic指令。這條新增加的指令是JDK 7的項目目標:實現(xiàn)動態(tài)類型語言(Dynamically Typed Language)支持而進行的改進之一,也是為JDK 8里可以順利實現(xiàn)Lambda表達式而做的技術(shù)儲備。

何謂動態(tài)類型語言 [1] ?動態(tài)類型語言的關鍵特征是它的類型檢查的主體過程是在運行期而不是編譯期進行的,滿足這個特征的語言有很多,常用的包括:APL、Clojure、Erlang、Groovy、javaScript、Lisp、Lua、PHP、Prolog、Python、Ruby、Smalltalk、Tcl,等等。那相對地,在編譯期就進行類型檢查過程的語言,譬如C++和Java等就是最常用的靜態(tài)類型語言。變量無類型而變量值才有類型

在Java虛擬機層面上提供動態(tài)類型的直接支持就成為Java平臺發(fā)展必須解決的問題,這便是JDK 7時JSR-292提案中invokedynamic指令以及java.lang.invoke包出現(xiàn)的技術(shù)背景。

JDK 7時新加入的java.lang.invoke包 [1] 是JSR 292的一個重要組成部分,這個包的主要目的是在之前單純依靠符號引用來確定調(diào)用的目標方法這條路之外,提供一種新的動態(tài)確定目標方法的機制,稱為“方法句柄”(Method Handle)。

·Reflection和MethodHandle機制本質(zhì)上都是在模擬方法調(diào)用,但是Reflection是在模擬Java代碼層次的方法調(diào)用,而MethodHandle是在模擬字節(jié)碼層次的方法調(diào)用。

在Tomcat目錄結(jié)構(gòu)中,可以設置3組目錄(/common/*、/server/*和/shared/*,但默認不一定是開放的,可能只有/lib/*目錄存在)用于存放Java類庫,另外還應該加上Web應用程序自身的“/WEB-INF/*”目錄,一共4組。把Java類庫放置在這4組目錄中,每一組都有獨立的含義,分別是:

·放置在/common目錄中。類庫可被Tomcat和所有的Web應用程序共同使用。

·放置在/server目錄中。類庫可被Tomcat使用,對所有的Web應用程序都不可見。

·放置在/shared目錄中。類庫可被所有的Web應用程序共同使用,但對Tomcat自己不可見。

·放置在/WebApp/WEB-INF目錄中。類庫僅僅可以被該Web應用程序使用,對Tomcat和其他Web應用程序都不可見。

為了支持這套目錄結(jié)構(gòu),并對目錄里面的類庫進行加載和隔離,Tomcat自定義了多個類加載器,這些類加載器按照經(jīng)典的雙親委派模型來實現(xiàn)

java類文件的知識點有哪些

Common類加載器、Catalina類加載器(也稱為Server類加載器)、Shared類加載器和Webapp類加載器則是Tomcat自己定義的類加載器,它們分別加載/common/*、/server/*、/shared/*和/WebApp/WEB-INF/*中的Java類庫。其中WebApp類加載器和JSP類加載器通常還會存在多個實例,每一個Web應用程序?qū)粋€WebApp類加載器,每一個JSP文件對應一個JasperLoader類加載器。

而JasperLoader的加載范圍僅僅是這個JSP文件所編譯出來的那一個Class文件,它存在的目的就是為了被丟棄:當服務器檢測到JSP文件被修改時,會替換掉目前的JasperLoader的實例,并通過再建立一個新的JSP類加載器來實現(xiàn)JSP文件的HotSwap功能。

以上就是“java類文件的知識點有哪些”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI