代碼編譯的結果從本地機器碼轉變爲字節碼,是存儲格式發展的一小步,倒是編程語言發展的一大步。java
——跨平臺的保障編程
目錄:數組
1.概述安全
2.類加載的時機網絡
3.類加載的過程編程語言
4.類加載器性能
1.概述spa
過程概述:翻譯
①虛擬機把描述類的數據從class文件加載到內存;②並對數據進行校驗、轉換解析和初始化;③最終造成能夠被虛擬機直接使用的Java類型:這就是虛擬機的類加載機制。指針
Java裏天生能夠動態擴展的語言特性是依賴運行期動態加載和動態鏈接這個特色實現的:
詳細解釋:與那些在編譯時須要進行鏈接工做的語言不通,在java語言裏,類型的加載、鏈接和初始化過程都是在程序運行期間完成的。
優:爲java應用程序提供高度的靈活性
缺:這種策略會令類加載時稍微增長一些性能開銷
例:若是編寫一個面向接口的應用程序,能夠等到運行時再指定其實際的實現類;用戶能夠經過java預約義的和自定義類加載器,讓一個本地應用程序能夠在運行時,從網絡或者其餘地方加載一個二進制流做爲程序代碼的一部分,這種組裝應用程序的方式目前已普遍應用於Java程序當中。
2.類加載的時機
類從被加載到虛擬機內存中開始,到卸載出內存爲止,她的整個生命週期包括:加載(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸載(Unloading)七個階段。
其中驗證、準備、解析三個部分統稱爲鏈接。
爲了支持java語言的運行時綁定(動態綁定),解析階段可能在初始化以後再開始。
圖上的順序,表明的是各階段的開始順序,這些階段一般都是相互交叉地混合式進行的。
那麼加載的時機是什麼?
類的加載時機能夠由不一樣虛擬機的具體實現自由把握,可是對視初始化階段,虛擬機規範是有嚴格規定的。
有且只有5種狀況下必須當即對類進行初始化(對一個類的主動引用):
①遇到new、getstatic、putstatic或invokestatic這4條字節碼指令時,若是類沒有進行過初始化,則須要出發其初始化。生成這4條指令的最多見的java代碼場景是(一一對應):使用new關鍵字實例化對象的時候、讀取或設置一個類的靜態字段(被final修飾、已在編譯期把結果放入常量池的靜態字段除外)的時候、以及調用一個類的靜態方法的時候。
②使用java.lang.reflect包的方法對類進行反射調用的時候,若是類沒有進行過初始化,則須要先觸發其初始化。
③當初始化一個類的時候,若是發現其父類尚未進行過初始化,則須要先觸發其父類的初始化。
④當虛擬機啓動時,用戶須要制定一個要執行的主類(包含main()方法的那個類),虛擬機會先初始化這個主類。
⑤當時用jdk1.7的動態語言支持時,若是一個java.lang.invoke.MethodHandle實例最後解析結果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,而且這個方法的句柄所對應的類沒有進行過初始化、則須要先觸發其初始化。
除此之類,全部引用類的方法都不會觸發初始化,稱爲被動引用,下面舉3個例子:
①經過子類引用父類的靜態字段,不會致使子類初始化:
對於靜態字段,只有直接定義這個字段的類纔會被初始化。
②經過數組定義來引用類,不會觸發此類的初始化:
SuperClass[] sca=new SuperClass[10];
這段代碼不會觸發類org.fenixsoft.classloading.SuperClass的初始化階段,可是這段代碼裏面觸發了另外一個名爲「[Lorf.fenixsoft.classloading.SuperClass」的類的初始化。它是由虛擬機自動生成的,直接繼承於object的子類,建立動做由字節碼指令newarray觸發。
這個類表明了一個元素類型爲org.fenixsoft.classloading.SuperClass的一維數組,數組中應用的屬性和方法都實如今這個類裏。Java語言中對數組的訪問比C/C++相對安全是由於這個類封裝了數組元素的訪問方法,而C/C++直接翻譯爲對數組指針的移動。
③常量在編譯階段會存入調用類的常量池中,本質上並備有直接引用到定義常量的類,所以不會觸發定義常量的類的初始化:實際上常量在編譯以後,就已經屬於NotInitialization類的常量池了,經過原類名對常量的引用都會轉化爲對NotInitialization類常量的引用。
注:接口的加載過程與類加載過程稍有一些不一樣,針對接口須要作一些特殊說明,接口也有初始化過程,接口中雖然不能使用static{}語句塊,可是編譯器仍會爲接口生成「<clinit>()」類構造器,用於初始化接口中所定義的成員變量。
接口與類真正有區別的是前面講述的5種「有且僅有」須要開始初始化場景中的第三種:當一個類在初始化時,要求其父類所有都已初始化過了,可是一個接口在初始化時,並不要求其父接口所有都完成了初始化,只有在真正使用到父接口的時候(如引用接口中定義的常量)纔會初始化。