Jvm中class文件如何加載、初始化

這篇文章主要介紹了Jvm中class文件如何加載、初始化,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

為吉利等地區(qū)用戶(hù)提供了全套網(wǎng)頁(yè)設(shè)計(jì)制作服務(wù),及吉利網(wǎng)站建設(shè)行業(yè)解決方案。主營(yíng)業(yè)務(wù)為網(wǎng)站設(shè)計(jì)制作、成都網(wǎng)站制作、吉利網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專(zhuān)業(yè)、用心的態(tài)度為用戶(hù)提供真誠(chéng)的服務(wù)。我們深信只要達(dá)到每一位用戶(hù)的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!

編寫(xiě)的java文件在要真正運(yùn)行時(shí),會(huì)首先被編譯成 “.class"結(jié)尾的二進(jìn)制文件,然后被虛擬機(jī)加載。那么在虛擬機(jī)中一個(gè)class文件要成為java實(shí)例,需要經(jīng)歷好幾個(gè)步驟:
一、class文件要成為java實(shí)例的步驟
1、裝載:裝載階段由三個(gè)基本動(dòng)作完成,要裝載一個(gè)類(lèi)型,java虛擬機(jī)必須:
(1)通過(guò)該類(lèi)型的完全限定名,產(chǎn)生一個(gè)代表該類(lèi)型的二進(jìn)制數(shù)據(jù)流
(2)解析這個(gè)二進(jìn)制數(shù)據(jù)流為方法區(qū)內(nèi)的內(nèi)部數(shù)據(jù)結(jié)構(gòu)
(3)創(chuàng)建一個(gè)表示該類(lèi)型的java.lang.Class的實(shí)例
*當(dāng)時(shí)用new關(guān)鍵字創(chuàng)建一個(gè)對(duì)象時(shí),該類(lèi)的數(shù)據(jù)結(jié)構(gòu)存放在方法區(qū)中,new出的對(duì)象存放在堆中
二進(jìn)制的數(shù)據(jù)流可由以下方式產(chǎn)生:
 1、從本地加載一個(gè)java class 文件
 2、通過(guò)網(wǎng)絡(luò)上下載一個(gè)class文件
3、從一個(gè)zip、jar、或者其他文檔中提取class文件    等…
2、驗(yàn)證:類(lèi)被裝載后,就要準(zhǔn)備連接,連接的第一步是驗(yàn)證——確認(rèn)類(lèi)型符合java語(yǔ)言規(guī)范,并且不會(huì)危及虛擬機(jī)的完整性。
類(lèi)型的檢查——確保除了Object之外的每個(gè)類(lèi)都必須有一個(gè)超類(lèi),并確保該類(lèi)的所有超類(lèi)都已經(jīng)被裝載了
類(lèi)之間的二進(jìn)制兼容檢查——檢查final類(lèi)不能用于子類(lèi)、檢查final類(lèi)的方法不能被覆蓋、確保子類(lèi)和超類(lèi)之間沒(méi)有不兼容的方法
——檢查所有的常量池入口相互之間一致
——檢查常量池中的所有特殊字符串是否符合格式
——檢查字節(jié)碼的完整性(最為復(fù)雜的一步)
3、準(zhǔn)備:在準(zhǔn)備階段java虛擬機(jī)為類(lèi)變量分配內(nèi)存,設(shè)置默認(rèn)初始值,但在到達(dá)初始化之前,類(lèi)變量都沒(méi)有被初始化為真正的初始值。即:我們?cè)陬?lèi)中聲明  int  a = 3;但在這一步,a的致被賦予類(lèi)型的默認(rèn)值 0  int a =0;java虛擬機(jī)不支持boolean 類(lèi)型,在內(nèi)部,boolean變量會(huì)被默認(rèn)的設(shè)置為int類(lèi)型的0,即初始化成false.
4、解析:經(jīng)過(guò)驗(yàn)證和準(zhǔn)備之后,就進(jìn)入了解析過(guò)程。解析就是在類(lèi)型的常量池中尋找類(lèi)、接口、字段、以及方法的符號(hào)引用,把這些引用替換成為直接引用的過(guò)程
5、初始化:初始化就是賦予一個(gè)變量真正的初始值
如我們定義 private static int a=1; 此時(shí)就是給a賦值1
二、動(dòng)態(tài)鏈接和解析
  class文件把所有的符號(hào)引用保存在——常量池中,每一個(gè) class文件都有一個(gè)常量池。每一個(gè)被虛擬機(jī)裝載類(lèi)或者接口 都有一個(gè)內(nèi)部版本的常量池,被稱(chēng)為運(yùn)行時(shí)常量池。
常量池的解析——當(dāng)程序運(yùn)行時(shí),某個(gè)特定的符號(hào)引用要被使用, 首先要被解析。解析過(guò)程就是根據(jù) 符號(hào)引用查找到實(shí)體, 在把符號(hào)引用替換成為 直接引用的過(guò)程。每個(gè)符號(hào)引用都只被解析一次
早解析——預(yù)先解析所有的符號(hào)引用,從初始類(lèi)開(kāi)始,到后續(xù)的各個(gè)類(lèi),知道所有的符號(hào)引用都被解析。
遲解析——在訪問(wèn)每一個(gè)符號(hào)引用的最后一刻才去解析。(也可選擇兩種情況之間的折衷策略)
——程序執(zhí)行都是在第一次 實(shí)際訪問(wèn)一個(gè)符號(hào)引用時(shí)才會(huì)拋出錯(cuò)誤,對(duì)于用戶(hù)來(lái)說(shuō),看上去都是 遲解析
——java虛擬機(jī)會(huì)把所有具有相同字符串順序的字符串文字處理成一個(gè)String對(duì)象。即:如果有多個(gè)類(lèi)使用同一個(gè)字符串“Hello”,java虛擬機(jī)只會(huì)創(chuàng)建一個(gè)具有“Hello ”值的String對(duì)象 來(lái)表示所有的字符串文字。
——任何的byte、short、char返傭www.fx61.com,的值在被壓入棧中時(shí), 都會(huì)先被轉(zhuǎn)換成int型
——涉及byte、short、char的運(yùn)算操作會(huì)首先把他們都轉(zhuǎn)換成int類(lèi)型,進(jìn)行計(jì)算然后得到int結(jié)果,如果需要byte等結(jié)果,需要進(jìn)行顯示轉(zhuǎn)換。
——java虛擬機(jī)中內(nèi)存只能以對(duì)象形式在堆中進(jìn)行分配,如果需要可考慮基本類(lèi)型包裝器
——java所使用的同步機(jī)制是監(jiān)視器,java中的監(jiān)視器支持兩種線程:互斥和協(xié)作。java虛擬機(jī)是通過(guò)鎖來(lái)實(shí)現(xiàn)互斥,是通過(guò)Object的wait和notify方法來(lái)實(shí)現(xiàn)協(xié)作
——只有當(dāng)絕對(duì)確定等待區(qū)中只有一個(gè)線程掛起的時(shí)候才應(yīng)使用notify,只要存在同時(shí)有多個(gè)線程掛起的可能性,就應(yīng)該使用notify all。否則可能導(dǎo)致某個(gè)特定的線程在等待區(qū)中等待時(shí)間過(guò)長(zhǎng),甚至永遠(yuǎn)就不會(huì)蘇醒。
——堆和方法區(qū)是被所有線程共享的 
——會(huì)被多線程訪問(wèn)的兩種數(shù)據(jù):保存在堆中的實(shí)例變量,保存在方法區(qū)中的類(lèi)變量
——不需要進(jìn)行保護(hù)的變量:java棧中的局部變量,該數(shù)據(jù)是擁有該線程的線程私有的
——當(dāng)虛擬機(jī)裝載一個(gè)class文件的時(shí)候,會(huì)創(chuàng)建一個(gè)java.lang.Class類(lèi)的實(shí)例。當(dāng)鎖住一個(gè)類(lèi)的時(shí)候,其實(shí)就是鎖住那個(gè)類(lèi)的Class對(duì)象。
三、jvm內(nèi)置的三大類(lèi)加載器
BootStrap類(lèi)加載器(BootStrapClassLoader):根類(lèi)加載器。該加載器沒(méi)有父加載器,負(fù)責(zé)加載虛擬機(jī)的核心類(lèi)庫(kù),如:java.lang.*等,java.lang.Object就是由根加載器加載的
Extension 類(lèi)加載器(ExtClassLoader):它的父加載器為根加載器,也就是上面那個(gè)。它從java.ext.dirs系統(tǒng)屬性所指定的目錄中加載類(lèi)庫(kù),或者從jdk的安裝目錄jre/lib/ext子目錄中加載類(lèi)庫(kù)。該類(lèi)是純java類(lèi)是lava.lang.ClassLoader類(lèi)的子類(lèi)
System系統(tǒng)類(lèi)加載器(AppClassLoader),也稱(chēng)為應(yīng)用類(lèi)加載器,其父加載器為擴(kuò)展類(lèi)加載器,上面那個(gè)。它從環(huán)境變量classpath中多指定的目錄中加載。它是用戶(hù)自定義類(lèi)加載器的默認(rèn)父加載器,該類(lèi)是純java類(lèi)是lava.lang.ClassLoader類(lèi)的子類(lèi)
注意:使用Class.forName("com.test.Test1")進(jìn)行類(lèi)裝載時(shí),會(huì)自動(dòng)執(zhí)行類(lèi)中的靜態(tài)代碼塊,但不會(huì)執(zhí)行構(gòu)造方法
使用loadClass("com.test.Test1")進(jìn)行裝載時(shí),不會(huì)自動(dòng)執(zhí)行其中的靜態(tài)代碼塊,也不會(huì)執(zhí)行構(gòu)造方法
***:同一個(gè)類(lèi),由兩個(gè)不同的類(lèi)加載器去加載,會(huì)被認(rèn)為是兩個(gè)不相同的類(lèi),在方法區(qū)中會(huì)有兩份該類(lèi)的類(lèi)信息
只有使用啟動(dòng)類(lèi)加載器加載的類(lèi),比如:java.lang.Strong、java.lang.Object 等類(lèi),在方法區(qū)中只有一份類(lèi)信息。
 判斷兩個(gè)類(lèi)是否相等的基礎(chǔ)是,這兩個(gè)類(lèi)的類(lèi)加載器是不是同一個(gè)
初始化:對(duì)于類(lèi)的初始化階段,虛擬機(jī)規(guī)范規(guī)定了5種情況下必須立即對(duì)類(lèi)進(jìn)行初始化
1、遇到new、getstatic、putstatic、invokestatic這四條字節(jié)碼指令時(shí),如果類(lèi)之前沒(méi)有進(jìn)行過(guò)初始化會(huì)進(jìn)行初始化。這四條字節(jié)碼對(duì)應(yīng)的是:new 實(shí)例、靜態(tài)字段取值、靜態(tài)字段賦值、靜態(tài)字段調(diào)用。
2、使用java.lang.refleat包的方法進(jìn)行反射調(diào)用
3、當(dāng)初始化一個(gè)類(lèi)時(shí),如果發(fā)現(xiàn)其父類(lèi)還沒(méi)有被初始化,需要先初始化其父類(lèi)
4、當(dāng)虛擬機(jī)啟動(dòng),需要執(zhí)行main方法的類(lèi)
5、當(dāng)使用LDK1.7的動(dòng)態(tài)預(yù)言支持時(shí),如果一個(gè)java.lang.invole.MethodHandle實(shí)例的最后解析結(jié)果是REF_getStatic,REF_putStatic,REF_invokeStatic的方法句柄。
以下幾種情況需要注意:
1、對(duì)于靜態(tài)字段,只有直接定義這個(gè)字段的類(lèi)或者接口才會(huì)被初始化,如果是通過(guò)子類(lèi)引用父類(lèi)中的靜態(tài)字段,只會(huì)觸發(fā)父類(lèi)的初始化。
2、常量在編譯階段會(huì)存入調(diào)用類(lèi)的常量池中,本質(zhì)上并沒(méi)有直接引用到定義類(lèi)的常量,因此不會(huì)觸發(fā)定義常量的類(lèi)的初始化。備注:只有編譯器能確定之的常量才會(huì)進(jìn)行該操作,編譯器無(wú)法確定的常量值,還是會(huì)觸發(fā)目標(biāo)類(lèi)的初始化。
例如:public static final String uuid = UUID.randomUUID().toString();  這行代碼,當(dāng)其他類(lèi)在引用uuid時(shí),會(huì)觸發(fā)定義該uuid的類(lèi)的初始化
3、通過(guò)數(shù)組定義來(lái)引用類(lèi),不會(huì)觸發(fā)類(lèi)的初始化 例如: Super[ ] s = new Super[10]; 這行代碼并不會(huì)觸發(fā)Super類(lèi)的初始化
4、當(dāng)一個(gè)接口初始化的時(shí)候,并不要求其父接口也被初始化,只有真正使用到父接口的時(shí)候才會(huì)初始化。
四、case class與class的區(qū)別
1、初始化的時(shí)候,不需要new,當(dāng)然你也可以加上,普通類(lèi)一定需要加上new
class ABC(name:String){
def ff(): Unit ={
}
}
case class ABC1(name:String){
def ff1(): Unit ={
}
}
val abc1=ABC1("fg")
val abc= new ABC("xx")
2、toString的實(shí)現(xiàn)更加漂亮
println(abc1.toString)
println(abc.toString)
3、默認(rèn)實(shí)現(xiàn)了equals   hashcode
4、默認(rèn)是可以序列化的,也就是實(shí)現(xiàn)了Serializable
5、自動(dòng)從scala.Producet中繼承了一些函數(shù)
6、case class構(gòu)造函數(shù)的參數(shù)是publiec級(jí)別的,我們可以直接訪問(wèn)
7、支持模式匹配

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“Jvm中class文件如何加載、初始化”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!

分享名稱(chēng):Jvm中class文件如何加載、初始化
網(wǎng)頁(yè)網(wǎng)址:http://muchs.cn/article34/ppjgse.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、標(biāo)簽優(yōu)化移動(dòng)網(wǎng)站建設(shè)、網(wǎng)站制作、外貿(mào)網(wǎng)站建設(shè)、定制開(kāi)發(fā)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都網(wǎng)頁(yè)設(shè)計(jì)公司