Java之類加載機(jī)制案例講解
1.類加載
<1>.父子類執(zhí)行的順序
1.父類的靜態(tài)變量和靜態(tài)代碼塊(書寫順序)
2.子類的靜態(tài)變量和靜態(tài)代碼塊(書寫順序)
3.父類的實(shí)例代碼塊(書寫順序)
4.父類的成員變量和構(gòu)造方法
5.子類的實(shí)例代碼塊
6.子類的成員變量和構(gòu)造方法
<2>類加載的時(shí)機(jī)
如果類沒有進(jìn)行初始化,則需要先進(jìn)行初始化,虛擬機(jī)規(guī)范則是嚴(yán)格規(guī)定有且只有5種情況必須先對(duì)類進(jìn)行初始化(而加載,驗(yàn)證,準(zhǔn)備要在這個(gè)之前開始)
1.創(chuàng)建類的實(shí)例(new的方式),訪問某個(gè)類的靜態(tài)變量,或者對(duì)該靜態(tài)變量賦值,調(diào)用類的靜態(tài)方法
2.反射的方式
3.初始化某個(gè)類的子類,則其父類也會(huì)被初始化
4.java虛擬機(jī)啟動(dòng)時(shí)被標(biāo)記為啟動(dòng)類的類,直接使用java.exe來運(yùn)行的某個(gè)主類(如main類)
5.使用jdk1.7的動(dòng)態(tài)語言支持時(shí)
<3>類的生命周期
七個(gè)階段:加載,驗(yàn)證,準(zhǔn)備,解析,初始化,使用和卸載。其中驗(yàn)證,準(zhǔn)備和解析三個(gè)部分被稱為連接
解析階段在某些情況下可以在初始化階段之后再進(jìn)行,這是為了支持java語言的運(yùn)行時(shí)綁定(動(dòng)態(tài)綁定)
<4>類加載的過程
接下來我們?cè)敿?xì)講解一下Java虛擬機(jī)中類加載的全過程,也就是加載、驗(yàn)證、準(zhǔn)備、解析和初始化這5個(gè)階段所執(zhí)行的具體動(dòng)作。
1.加載
<1>通過一個(gè)類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流。
<2>將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)。
<3>在內(nèi)存中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問入口。
2.驗(yàn)證
這一階段的目的是為了確保Class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)自身的安全。
3.準(zhǔn)備
準(zhǔn)備階段是正式為類變量分配內(nèi)存并設(shè)置類變量初始值的階段,這些變量所使用的內(nèi)存都將在方法區(qū)中進(jìn)行分配。
假設(shè)一個(gè)類變量的定義為:
public static int value=123;
那變量value在準(zhǔn)備階段過后的初始值為0而不是123,因?yàn)檫@時(shí)候尚未開始執(zhí)行任何Java方法,而把value賦值為123的putstatic指令是程序被編譯后,存放于類構(gòu)造器()方法之中,所以把value賦值為123的動(dòng)作將在初始化階段才會(huì)執(zhí)行。
4.解析
虛擬機(jī)將常量池內(nèi)的符號(hào)引用替換為直接引用的過程。
符號(hào)引用:符號(hào)引用與虛擬機(jī)實(shí)現(xiàn)的內(nèi)存布局無關(guān),引用的目標(biāo)并不一定已經(jīng)加載到內(nèi)存中。
直接引用:直接引用是和虛擬機(jī)實(shí)現(xiàn)的內(nèi)存布局相關(guān)的。如果有了直接引用,那引用的目標(biāo)必定已經(jīng)在內(nèi)存中存在。
5.初始化
在準(zhǔn)備階段,變量已經(jīng)賦過一次系統(tǒng)要求的初始值,而在初始化階段,則根據(jù)程序員通過程序制定的主觀計(jì)劃去初始化類變量和其他資源,或者可以從另外一個(gè)角度來表達(dá):初始化階段是執(zhí)行類構(gòu)造器()方法的過程。
了解:
()方法是由編譯器自動(dòng)收集類中的所有類變量的賦值動(dòng)作和靜態(tài)語句塊(static{}塊)中的語句合并產(chǎn)生的,編譯器收集的順序是由語句在源文件中出現(xiàn)的順序所決定的,靜態(tài)語句塊中只能訪問到定義在靜態(tài)語句塊之前的變量,定義在它之后的變量,在前面的靜態(tài)語句塊可以賦值,但是不能訪問:
public class Test{ static{ i=0; //給變量賦值可以正常編譯通過 System.out.print(i); //這句編譯器會(huì)提示"非法向前引用" } static int i=1; }
1.()方法(Class類的構(gòu)造方法)與類的構(gòu)造函數(shù)(或者說實(shí)例構(gòu)造器()方法)不同,它不需要顯式地調(diào)用父類構(gòu)造器,虛擬機(jī)會(huì)保證在子類的()方法執(zhí)行之前,父類的()方法已經(jīng)執(zhí)行完畢。因此在虛擬機(jī)中第一個(gè)被執(zhí)行的()方法的類肯定是java.lang.Object。
2.()方法對(duì)于類或接口來說并不是必需的,如果一個(gè)類中沒有靜態(tài)語句塊,也沒有對(duì)變量的賦值操作,那么編譯器可以不為這個(gè)類生成()方法。
3.接口中定義的變量使用時(shí),接口才會(huì)初始化:接口中不能使用靜態(tài)語句塊,但仍然有變量初始化的賦值操作,因此接口與類一樣都會(huì)生()方法。但接口與類不同的是,執(zhí)行接口的()方法不需要先執(zhí)行父接口的()方法。只有當(dāng)父接口中定義的變量使用時(shí),父接口才會(huì)初始化。另外,接口的實(shí)現(xiàn)類在初始化時(shí)也一樣不會(huì)執(zhí)行接口的()方法。
4.虛擬機(jī)會(huì)保證一個(gè)類的()方法在多線程環(huán)境中被正確地加鎖、同步,如果多個(gè)線程同時(shí)去初始化一個(gè)類,那么只會(huì)有一個(gè)線程去執(zhí)行這個(gè)類的()方法,其他線程都需要阻塞等待,直到活動(dòng)線程執(zhí)行()方法完畢。如果在一個(gè)類的()方法中有耗時(shí)很長的操作,就可能造成多個(gè)進(jìn)程阻塞,在實(shí)際應(yīng)用中這種阻塞往往是很隱蔽的。
<5>類加載器
類加載器可以分為:啟動(dòng)類加載器、擴(kuò)展類加載器、應(yīng)用程序類加載器、自定義類加載器。他們的關(guān)系一般如下:
1.啟動(dòng)類加載器(BootstrapClassLoader)
這個(gè)類由C++語言實(shí)現(xiàn),是虛擬機(jī)自身的一部分,并不繼承ClassLoader,不能操作它。用來加載Java的核心類。
2.擴(kuò)展類加載器(ExtClassLoader)
這個(gè)類加載器是在類sun.misc.Launcher$ExtClassLoader中以Java代碼的形式實(shí)現(xiàn)的。它負(fù)責(zé)加載<JAVA_HOME>\lib\ext目錄中,或者被java.ext.dirs系統(tǒng)變量所指定的路徑中所有的類庫。
3.應(yīng)用程序類加載器(AppClassLoader)
它負(fù)責(zé)在 JVM 啟動(dòng)時(shí)加載來自 Java 命令的 -classpath 或者 -cp 選項(xiàng)、java.class.path 系統(tǒng)屬性指定的 jar 包和類路徑。在應(yīng)用程序代碼里可以通過 ClassLoader 的靜態(tài)方法 getSystemClassLoader() 來獲取應(yīng)用類加載器。如果沒有特別指定,則在沒有使用自定義類加載器情況下,用戶自定義的類都由此加載器加載。
4.2 自定義加載器
用戶自定義了類加載器,則自定義類加載器都以應(yīng)用類加載器作為父加載器。應(yīng)用類加載器的父類加載器為擴(kuò)展類加載器。這些類加載器是有層次關(guān)系的,啟動(dòng)加載器又叫根加載器,是擴(kuò)展加載器的父加載器
<6>類加載機(jī)制——雙親委派模型
雙親委派模型的過程:如果一個(gè)類加載器收到了類加載的請(qǐng)求,它首先不會(huì)自己嘗試加載這個(gè)類,而是把這個(gè)請(qǐng)求委派給父類加載器去完成,每一層次的類加載器都是如此,因此所有的加載請(qǐng)求信息最終都會(huì)傳送到最頂層的啟動(dòng)類加載器中,只有當(dāng)父加載器反饋?zhàn)约簾o法完成這個(gè)加載請(qǐng)求(即它的搜索范圍沒有找到所需要的類)時(shí),子加載器才會(huì)嘗試自己去完成加載
先查找,再進(jìn)行加載
(1)從下往上找
(2)從上往下加載
雙親委派模型的好處:雙親委派模型對(duì)于java程序的穩(wěn)定運(yùn)行極為重要
劣勢(shì):無法滿足靈活的類加載方式。(解決方案:自己重寫loadClass破壞雙親委派模型 例如SPI機(jī)制)
到此這篇關(guān)于Java之類加載機(jī)制案例講解的文章就介紹到這了,更多相關(guān)Java之類加載內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中遍歷Map的多種方法示例及優(yōu)缺點(diǎn)總結(jié)
在java中遍歷Map有不少的方法,下面這篇文章主要給大家介紹了關(guān)于Java中遍歷Map的多種方法,以及各種方法的優(yōu)缺點(diǎn)總結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。2017-07-07SpringBoot項(xiàng)目使用slf4j的MDC日志打點(diǎn)功能(最新推薦)
這篇文章主要介紹了SpringBoot項(xiàng)目使用slf4j的MDC日志打點(diǎn)功能,本文通過示例代碼給大家介紹非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-06-06在RedisTemplate中使用scan代替keys指令操作
這篇文章主要介紹了在RedisTemplate中使用scan代替keys指令操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-11-11Spring security如何重寫Filter實(shí)現(xiàn)json登錄
這篇文章主要介紹了Spring security 如何重寫Filter實(shí)現(xiàn)json登錄,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09mybatis-plus動(dòng)態(tài)數(shù)據(jù)源讀寫分離方式
在分布式項(xiàng)目開發(fā)中,動(dòng)態(tài)數(shù)據(jù)源的配置與使用至關(guān)重要,通過創(chuàng)建DynamicDatasourceService,實(shí)現(xiàn)數(shù)據(jù)源的動(dòng)態(tài)添加與調(diào)用,有效管理主從庫操作,減輕數(shù)據(jù)庫壓力,此外,通過配置類與@DS注解,實(shí)現(xiàn)了靈活的分庫查詢功能,為高效處理數(shù)據(jù)提供了強(qiáng)有力的支持2024-10-10Java如何獲取resources下的文件路徑和創(chuàng)建臨時(shí)文件
這篇文章主要介紹了Java如何獲取resources下的文件路徑和創(chuàng)建臨時(shí)文件,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12Alibaba?SpringCloud集成Nacos、openFeign實(shí)現(xiàn)負(fù)載均衡的解決方案
Spring?Cloud?Alibaba?致力于提供微服務(wù)開發(fā)的一站式解決方案,此項(xiàng)目包含開發(fā)分布式應(yīng)用微服務(wù)的必需組件,這篇文章主要介紹了Alibaba?SpringCloud集成Nacos、openFeign實(shí)現(xiàn)負(fù)載均衡,需要的朋友可以參考下2024-05-05PowerJob的QueryConvertUtils工作流程源碼解讀
這篇文章主要為大家介紹了PowerJob的QueryConvertUtils工作流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01