每天進(jìn)步一點(diǎn)點(diǎn)!
前面已經(jīng)介紹了類加載的加載、驗(yàn)證、準(zhǔn)備、解析等過程,今天來學(xué)習(xí)最后一個(gè)過程,初始化!
對(duì)于有過java開發(fā)經(jīng)驗(yàn)的朋友們來說,初始化這個(gè)詞自然不陌生,初始化階段與變量初始化自然不是一個(gè)概念,但是也有一些關(guān)聯(lián)。
在上一篇中,我們已經(jīng)看到了在準(zhǔn)備階段,不同變量初始化的區(qū)別,實(shí)際上,初始化階段是執(zhí)行類構(gòu)造器()方法的過程。

朋友們還記得上一篇中的例子嗎(有興趣的朋友可以看一下上一篇)?按照前面的分析,這個(gè)階段其實(shí)是對(duì)非final的static靜態(tài)變量初始化的過程。
注意:初始化過程實(shí)際上還會(huì)執(zhí)行靜態(tài)語(yǔ)句塊(static{})中的內(nèi)容。
既然說到了()方法,自然而然會(huì)想到()方法,有的朋友肯定會(huì)問,這兩個(gè)方法長(zhǎng)的好像啊,包括有些公司的面試題也會(huì)問,這兩個(gè)方法到底有什么區(qū)別呢?
包括筆者在內(nèi),以前也經(jīng)常搞不清楚,下面我們就將這兩個(gè)方法對(duì)比一下。
1. ()方法是類構(gòu)造器方法,()方法是對(duì)象構(gòu)造方法,從字面意思就可以看出,兩個(gè)方法面向的主體是不一樣的,一個(gè)作用于類,一個(gè)作用于對(duì)象。
()方法的指令來自于靜態(tài)變量和靜態(tài)語(yǔ)句塊,()來自于類的構(gòu)造方法。
2. ?當(dāng)存在類繼承關(guān)系的時(shí)候,()方法不需要像()方法那樣顯式地(super())調(diào)用父類的構(gòu)造器,虛擬機(jī)會(huì)先執(zhí)行父類的()方法。
然鵝,but,however,在接口里面,當(dāng)父接口定義的變量不使用的時(shí)候,子接口或?qū)崿F(xiàn)類執(zhí)行()方法時(shí),不會(huì)調(diào)用父接口中的()方法。
這是原書中的說法,但是筆者好奇啊,java為什么這么刁蠻不講理?
在好奇心驅(qū)使下,筆者打開了一個(gè)定義了靜態(tài)變量的接口字節(jié)碼文件,

呵呵噠……接口中的靜態(tài)變量編譯之后被轉(zhuǎn)換成了final的了,還記得上一篇的內(nèi)容嗎?
在準(zhǔn)備階段,final修飾的變量的值就被綁定了,自然不需要執(zhí)行()方法了。

到這里,相信大家對(duì)final修飾的變量不能被修改,也有了更深的認(rèn)識(shí)。
3. ?如上圖所示,當(dāng)類中沒有定義靜態(tài)變量和靜態(tài)語(yǔ)句塊的時(shí)候,字節(jié)碼中并沒有()方法,也就是說()方法并不是必須的,但是對(duì)于()方法,不管你定不定義,它就在那里不離不棄。
4. ()方法同步安全的,當(dāng)多個(gè)線程調(diào)用的時(shí)候,只有一個(gè)線程去執(zhí)行這個(gè)方法。并且,前面已經(jīng)提到過,一個(gè)類型在同一個(gè)類加載器只能初始化一次,其它線程實(shí)際并不能再調(diào)用這個(gè)()方法。

5. 前面的文章中,還提到過另一個(gè)點(diǎn), ()是虛擬機(jī)的方法,不能通過程序調(diào)用,而()是可以通過程序調(diào)用的。
通過以上的分析,我們也可以得出類變量賦值的順序,依次是:final變量(解析階段)>static變量(準(zhǔn)備階段)>普通變量(初始化階段)。
至此類加載的過程基本結(jié)束了,接下來將學(xué)習(xí)字節(jié)碼執(zhí)行引擎,自己給自己鼓鼓勁,加油!
喜歡文章或想一起學(xué)習(xí)的朋友可以關(guān)注我,給我點(diǎn)贊,我將會(huì)持續(xù)更新,有什么疑問或文中有不當(dāng)之處請(qǐng)給我留言,真誠(chéng)地希望能與大家一起交流探討,學(xué)習(xí)進(jìn)步。