這篇文章主要介紹了Jvm中class文件如何加載、初始化,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
為吉利等地區(qū)用戶提供了全套網(wǎng)頁設(shè)計制作服務(wù),及吉利網(wǎng)站建設(shè)行業(yè)解決方案。主營業(yè)務(wù)為網(wǎng)站設(shè)計制作、成都網(wǎng)站制作、吉利網(wǎng)站設(shè)計,以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會得到認(rèn)可,從而選擇與我們長期合作。這樣,我們也可以走得更遠(yuǎn)!
編寫的java文件在要真正運(yùn)行時,會首先被編譯成 “.class"結(jié)尾的二進(jìn)制文件,然后被虛擬機(jī)加載。那么在虛擬機(jī)中一個class文件要成為java實例,需要經(jīng)歷好幾個步驟:
一、class文件要成為java實例的步驟
1、裝載:裝載階段由三個基本動作完成,要裝載一個類型,java虛擬機(jī)必須:
(1)通過該類型的完全限定名,產(chǎn)生一個代表該類型的二進(jìn)制數(shù)據(jù)流
(2)解析這個二進(jìn)制數(shù)據(jù)流為方法區(qū)內(nèi)的內(nèi)部數(shù)據(jù)結(jié)構(gòu)
(3)創(chuàng)建一個表示該類型的java.lang.Class的實例
*當(dāng)時用new關(guān)鍵字創(chuàng)建一個對象時,該類的數(shù)據(jù)結(jié)構(gòu)存放在方法區(qū)中,new出的對象存放在堆中
二進(jìn)制的數(shù)據(jù)流可由以下方式產(chǎn)生:
1、從本地加載一個java class 文件
2、通過網(wǎng)絡(luò)上下載一個class文件
3、從一個zip、jar、或者其他文檔中提取class文件 等…
2、驗證:類被裝載后,就要準(zhǔn)備連接,連接的第一步是驗證——確認(rèn)類型符合java語言規(guī)范,并且不會危及虛擬機(jī)的完整性。
類型的檢查——確保除了Object之外的每個類都必須有一個超類,并確保該類的所有超類都已經(jīng)被裝載了
類之間的二進(jìn)制兼容檢查——檢查final類不能用于子類、檢查final類的方法不能被覆蓋、確保子類和超類之間沒有不兼容的方法
——檢查所有的常量池入口相互之間一致
——檢查常量池中的所有特殊字符串是否符合格式
——檢查字節(jié)碼的完整性(最為復(fù)雜的一步)
3、準(zhǔn)備:在準(zhǔn)備階段java虛擬機(jī)為類變量分配內(nèi)存,設(shè)置默認(rèn)初始值,但在到達(dá)初始化之前,類變量都沒有被初始化為真正的初始值。即:我們在類中聲明 int a = 3;但在這一步,a的致被賦予類型的默認(rèn)值 0 int a =0;java虛擬機(jī)不支持boolean 類型,在內(nèi)部,boolean變量會被默認(rèn)的設(shè)置為int類型的0,即初始化成false.
4、解析:經(jīng)過驗證和準(zhǔn)備之后,就進(jìn)入了解析過程。解析就是在類型的常量池中尋找類、接口、字段、以及方法的符號引用,把這些引用替換成為直接引用的過程
5、初始化:初始化就是賦予一個變量真正的初始值
如我們定義 private static int a=1; 此時就是給a賦值1
二、動態(tài)鏈接和解析
class文件把所有的符號引用保存在——常量池中,每一個 class文件都有一個常量池。每一個被虛擬機(jī)裝載類或者接口 都有一個內(nèi)部版本的常量池,被稱為運(yùn)行時常量池。
常量池的解析——當(dāng)程序運(yùn)行時,某個特定的符號引用要被使用, 首先要被解析。解析過程就是根據(jù) 符號引用查找到實體, 在把符號引用替換成為 直接引用的過程。每個符號引用都只被解析一次
早解析——預(yù)先解析所有的符號引用,從初始類開始,到后續(xù)的各個類,知道所有的符號引用都被解析。
遲解析——在訪問每一個符號引用的最后一刻才去解析。(也可選擇兩種情況之間的折衷策略)
——程序執(zhí)行都是在第一次 實際訪問一個符號引用時才會拋出錯誤,對于用戶來說,看上去都是 遲解析
——java虛擬機(jī)會把所有具有相同字符串順序的字符串文字處理成一個String對象。即:如果有多個類使用同一個字符串“Hello”,java虛擬機(jī)只會創(chuàng)建一個具有“Hello ”值的String對象 來表示所有的字符串文字。
——任何的byte、short、char返傭www.fx61.com,的值在被壓入棧中時, 都會先被轉(zhuǎn)換成int型
——涉及byte、short、char的運(yùn)算操作會首先把他們都轉(zhuǎn)換成int類型,進(jìn)行計算然后得到int結(jié)果,如果需要byte等結(jié)果,需要進(jìn)行顯示轉(zhuǎn)換。
——java虛擬機(jī)中內(nèi)存只能以對象形式在堆中進(jìn)行分配,如果需要可考慮基本類型包裝器
——java所使用的同步機(jī)制是監(jiān)視器,java中的監(jiān)視器支持兩種線程:互斥和協(xié)作。java虛擬機(jī)是通過鎖來實現(xiàn)互斥,是通過Object的wait和notify方法來實現(xiàn)協(xié)作
——只有當(dāng)絕對確定等待區(qū)中只有一個線程掛起的時候才應(yīng)使用notify,只要存在同時有多個線程掛起的可能性,就應(yīng)該使用notify all。否則可能導(dǎo)致某個特定的線程在等待區(qū)中等待時間過長,甚至永遠(yuǎn)就不會蘇醒。
——堆和方法區(qū)是被所有線程共享的
——會被多線程訪問的兩種數(shù)據(jù):保存在堆中的實例變量,保存在方法區(qū)中的類變量
——不需要進(jìn)行保護(hù)的變量:java棧中的局部變量,該數(shù)據(jù)是擁有該線程的線程私有的
——當(dāng)虛擬機(jī)裝載一個class文件的時候,會創(chuàng)建一個java.lang.Class類的實例。當(dāng)鎖住一個類的時候,其實就是鎖住那個類的Class對象。
三、jvm內(nèi)置的三大類加載器
BootStrap類加載器(BootStrapClassLoader):根類加載器。該加載器沒有父加載器,負(fù)責(zé)加載虛擬機(jī)的核心類庫,如:java.lang.*等,java.lang.Object就是由根加載器加載的
Extension 類加載器(ExtClassLoader):它的父加載器為根加載器,也就是上面那個。它從java.ext.dirs系統(tǒng)屬性所指定的目錄中加載類庫,或者從jdk的安裝目錄jre/lib/ext子目錄中加載類庫。該類是純java類是lava.lang.ClassLoader類的子類
System系統(tǒng)類加載器(AppClassLoader),也稱為應(yīng)用類加載器,其父加載器為擴(kuò)展類加載器,上面那個。它從環(huán)境變量classpath中多指定的目錄中加載。它是用戶自定義類加載器的默認(rèn)父加載器,該類是純java類是lava.lang.ClassLoader類的子類
注意:使用Class.forName("com.test.Test1")進(jìn)行類裝載時,會自動執(zhí)行類中的靜態(tài)代碼塊,但不會執(zhí)行構(gòu)造方法
使用loadClass("com.test.Test1")進(jìn)行裝載時,不會自動執(zhí)行其中的靜態(tài)代碼塊,也不會執(zhí)行構(gòu)造方法
***:同一個類,由兩個不同的類加載器去加載,會被認(rèn)為是兩個不相同的類,在方法區(qū)中會有兩份該類的類信息
只有使用啟動類加載器加載的類,比如:java.lang.Strong、java.lang.Object 等類,在方法區(qū)中只有一份類信息。
判斷兩個類是否相等的基礎(chǔ)是,這兩個類的類加載器是不是同一個
初始化:對于類的初始化階段,虛擬機(jī)規(guī)范規(guī)定了5種情況下必須立即對類進(jìn)行初始化
1、遇到new、getstatic、putstatic、invokestatic這四條字節(jié)碼指令時,如果類之前沒有進(jìn)行過初始化會進(jìn)行初始化。這四條字節(jié)碼對應(yīng)的是:new 實例、靜態(tài)字段取值、靜態(tài)字段賦值、靜態(tài)字段調(diào)用。
2、使用java.lang.refleat包的方法進(jìn)行反射調(diào)用
3、當(dāng)初始化一個類時,如果發(fā)現(xiàn)其父類還沒有被初始化,需要先初始化其父類
4、當(dāng)虛擬機(jī)啟動,需要執(zhí)行main方法的類
5、當(dāng)使用LDK1.7的動態(tài)預(yù)言支持時,如果一個java.lang.invole.MethodHandle實例的最后解析結(jié)果是REF_getStatic,REF_putStatic,REF_invokeStatic的方法句柄。
以下幾種情況需要注意:
1、對于靜態(tài)字段,只有直接定義這個字段的類或者接口才會被初始化,如果是通過子類引用父類中的靜態(tài)字段,只會觸發(fā)父類的初始化。
2、常量在編譯階段會存入調(diào)用類的常量池中,本質(zhì)上并沒有直接引用到定義類的常量,因此不會觸發(fā)定義常量的類的初始化。備注:只有編譯器能確定之的常量才會進(jìn)行該操作,編譯器無法確定的常量值,還是會觸發(fā)目標(biāo)類的初始化。
例如:public static final String uuid = UUID.randomUUID().toString(); 這行代碼,當(dāng)其他類在引用uuid時,會觸發(fā)定義該uuid的類的初始化
3、通過數(shù)組定義來引用類,不會觸發(fā)類的初始化 例如: Super[ ] s = new Super[10]; 這行代碼并不會觸發(fā)Super類的初始化
4、當(dāng)一個接口初始化的時候,并不要求其父接口也被初始化,只有真正使用到父接口的時候才會初始化。
四、case class與class的區(qū)別
1、初始化的時候,不需要new,當(dāng)然你也可以加上,普通類一定需要加上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的實現(xiàn)更加漂亮
println(abc1.toString)
println(abc.toString)
3、默認(rèn)實現(xiàn)了equals hashcode
4、默認(rèn)是可以序列化的,也就是實現(xiàn)了Serializable
5、自動從scala.Producet中繼承了一些函數(shù)
6、case class構(gòu)造函數(shù)的參數(shù)是publiec級別的,我們可以直接訪問
7、支持模式匹配
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“Jvm中class文件如何加載、初始化”這篇文章對大家有幫助,同時也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,更多相關(guān)知識等著你來學(xué)習(xí)!
分享名稱:Jvm中class文件如何加載、初始化
網(wǎng)頁網(wǎng)址:http://jinyejixie.com/article34/ppjgse.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、標(biāo)簽優(yōu)化、移動網(wǎng)站建設(shè)、網(wǎng)站制作、外貿(mào)網(wǎng)站建設(shè)、定制開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)