十年網(wǎng)站開發(fā)經(jīng)驗(yàn) + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊(duì)
量身定制 + 運(yùn)營維護(hù)+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
前言
不了解JVM的類加載機(jī)制你也可以coding,但是當(dāng)你了解之后,可以讓你在coding的時(shí)候避免很多坑,本文將以一道常見的面試題去剖析一下。本文參考 深入理解Java虛擬機(jī)(第2版) 。
1 public class ClassLoadTest {
2 private static ClassLoadTest test = new ClassLoadTest();
3
4 static int x;
5 static int y = 0;
6
7 public ClassLoadTest() {
8 x++;
9 y++;
10 }
11
12 public static void main(String[] args) {
13 System.out.println(test.x);
14 System.out.println(test.y);
15 }
16}
這里大家可以先猜測(cè)一下答案,可能結(jié)果會(huì)出乎你的意料~
類加載過程
先用一個(gè)圖簡(jiǎn)單的描述一下類加載的這個(gè)過程
加載
這個(gè)過程相當(dāng)于從本地或者網(wǎng)絡(luò)端去讀取一個(gè)字節(jié)流,然后將一些靜態(tài)儲(chǔ)存結(jié)構(gòu)轉(zhuǎn)換成方法區(qū)中運(yùn)行時(shí)期的數(shù)據(jù),最后生成一個(gè)代表這個(gè)類的Class對(duì)象,作為方法區(qū)訪問這個(gè)類的入口。
例如:
咱們可以通過一個(gè)類的全限定名去加載類
通過jar、war包去加載類
通過http請(qǐng)求去第三方平臺(tái)上拉取指定的類來加載
針對(duì)上述例子,這里是加載一個(gè)?ClassLoadTest.class
對(duì)象。
驗(yàn)證
要理解這個(gè)環(huán)節(jié)并不是很難,一個(gè)東西要放到JVM上去運(yùn)行,咱們肯定得對(duì)其進(jìn)行一些過濾,不能啥都往上丟,這里的驗(yàn)證簡(jiǎn)單的舉幾個(gè)例子:
文件格式的驗(yàn)證:
①是否以魔數(shù)0xCAFEBABE開頭;?
②主次版本號(hào)是否在當(dāng)前虛擬機(jī)處理范圍內(nèi);?
③常量池中的常量是否有不被支持的常量類型等等。
元數(shù)據(jù)的驗(yàn)證:
①這個(gè)類是否有父類;?
②這個(gè)類的父類是否繼承了不被允許繼承的類(final修飾的類);?
③這個(gè)類不是抽象類,是否實(shí)現(xiàn)了所有接口中要實(shí)現(xiàn)的方法等等。
字節(jié)碼的驗(yàn)證:
①保證跳轉(zhuǎn)指令不會(huì)跳轉(zhuǎn)到方法體以外的字節(jié)碼指令上;?
②保證方法體中的類型轉(zhuǎn)換是有效的等等。
準(zhǔn)備過程
這個(gè)過程相當(dāng)于給類變量分配內(nèi)存并設(shè)置變量初始值的階段,這些變量所使用的內(nèi)存都將在方法區(qū)中進(jìn)行分配。
針對(duì)上述例子:
1test = null;
2x = 0;
3y = 0;
注意:這里有個(gè)特殊情況,如果該字段被?final
修飾,那么在準(zhǔn)備階段改字段就會(huì)被設(shè)置成咱們自定義的值。?public static final int?value?= 11
,在準(zhǔn)備階段就會(huì)直接賦值11,并不是該變量的初始值。
解析過程
將符號(hào)引用轉(zhuǎn)換成直接引用的過程。這里有兩個(gè)名詞?符號(hào)引用和?直接引用。
符號(hào)引用:符號(hào)引用與虛擬機(jī)的布局無關(guān),甚至引用的目標(biāo)不一定加載到了內(nèi)存中。符號(hào)可以是任何形式的字面量,只要使用時(shí)能夠準(zhǔn)確的定位到目標(biāo)即可。
而解析過程又會(huì)針對(duì)類、字段、方法進(jìn)行解析,解析失敗則會(huì)拋出相應(yīng)的異常。例如在解析時(shí)發(fā)現(xiàn)沒有訪問權(quán)限會(huì)拋出?java.lang.IllegalAccessException
異常,查詢不到引用字段會(huì)拋出?java.lang.NoSuchFieldException
異常,查詢不到方法會(huì)拋出?java.lang.NoSuchMethodException
異常等等。
初始化
在準(zhǔn)備階段,變量已經(jīng)賦值過系統(tǒng)要求的默認(rèn)值,在初始化階段,則會(huì)根據(jù)程序制定的主觀計(jì)劃去初始化類變量和其他資源。這句話聽起來有些繞口,根據(jù)上述例子,實(shí)際上就是:
1test = new ClassLoadTest();// x = 1;y =1
2y = 0;
這個(gè)過程,由于?x
咱們自己并沒有去設(shè)定一個(gè)值,所以初始化階段它不會(huì)發(fā)生任何改變,但是?y
咱們有設(shè)定一個(gè)值0,所以最后造成最終結(jié)果為?x = 1;y = 0
。
ps:在同一個(gè)類加載器下,一個(gè)類只會(huì)初始化一次。多個(gè)線程同時(shí)初始化一個(gè)類,只有一個(gè)線程能正常初始化,其他線程都會(huì)進(jìn)行阻塞等待,直到活動(dòng)線程執(zhí)行初始化方法完畢。
總結(jié)
對(duì)于上面這個(gè)面試題,咱們用流程圖簡(jiǎn)單的描述一下:
對(duì)Java技術(shù),架構(gòu)技術(shù)感興趣的同學(xué),歡迎加群,一起學(xué)習(xí),相互討論。可以獲取免費(fèi)的學(xué)習(xí)資料,群號(hào):614478470 點(diǎn)擊加入
創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國云服務(wù)器,動(dòng)態(tài)BGP最優(yōu)骨干路由自動(dòng)選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機(jī)房獨(dú)有T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確進(jìn)行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動(dòng)現(xiàn)已開啟,新人活動(dòng)云服務(wù)器買多久送多久。