ExceptionInInitializerError-靜態(tài)代碼塊的細(xì)節(jié)
背景描述
某日調(diào)試的過程中發(fā)現(xiàn)一個(gè)非常奇怪的錯(cuò)誤,在IDEA中debug過程中我沒有辦法獲取一個(gè)已經(jīng)申請(qǐng)好的靜態(tài)變量,接口也每每執(zhí)行到此處就會(huì)跳錯(cuò)。那么按思路來說,縮小問題范圍,集中精力去發(fā)現(xiàn)這個(gè)問題根源。于是編寫單元測(cè)試對(duì)這一小截代碼進(jìn)行測(cè)試。出處部分主要來自下面這一段
public static Map<String, String> urlMapping = new ConcurrentHashMap<>();
static {
ConfigUtil.init();
urlMapping.put("xxx", PARTICIPLESURL);
urlMapping.put("yyy", INTENTURL);
urlMapping.put("zzz", SORTURL);
}
甚至報(bào)錯(cuò)了NoClassDefFoundError的錯(cuò)誤。一般來說,NoClassDefFound這個(gè)錯(cuò)誤常發(fā)生于依賴沖突的場(chǎng)景,但現(xiàn)在在調(diào)用自己的類出現(xiàn)這個(gè)問題就非常不可思議。日志中的錯(cuò)誤棧也比較冗長(zhǎng),影響了一開始捕獲關(guān)鍵信息的難度。
問題現(xiàn)象
觀察日志棧中可以發(fā)現(xiàn)有一個(gè)區(qū)分度比較明顯的錯(cuò)誤
java.lang.ExceptionInInitializerError
這個(gè)就是答案的解。這個(gè)錯(cuò)誤的意思其實(shí)是在靜態(tài)代碼塊中如果加載出錯(cuò)的會(huì)統(tǒng)一跳出這個(gè)Error。因?yàn)槭荅rror還沒辦法被普通的try-catch捕獲。
那之前的那個(gè)說的那個(gè)錯(cuò)誤
noclassdeffounderror: could not initialize class
又是怎么回事呢?我們知道jvm在需要加載類時(shí)會(huì)先加載類中的靜態(tài)代碼塊和靜態(tài)變量等。靜態(tài)代碼塊出現(xiàn)了沒有被捕獲的錯(cuò)誤,代碼塊崩潰,導(dǎo)致整個(gè)類加載失敗,jvm找不到這個(gè)類,順理成章的引出了上面這個(gè)錯(cuò)誤。這個(gè)錯(cuò)誤很容易往依賴沖突的方向去想,而偏離了問題的本質(zhì)。
那么代碼的問題又處在哪里呢?主要是ConcurrentHashMap的k和v是不允許為null空值的,而在當(dāng)時(shí)環(huán)境下被賦予了空值報(bào)錯(cuò)沒有接住導(dǎo)致的這個(gè)問題。ConcurrentHashMap不允許kv空值和本身需要做線程同步有關(guān),而HashMap就沒有這個(gè)問題,在非多線程環(huán)境下,即一塊內(nèi)存只會(huì)被一個(gè)線程訪問時(shí),還是使用HashMap就可以了。效率上也比ConcurrentHashMap快,因?yàn)镃oncurrentHashMap內(nèi)部還有為線程同步上鎖的問題存在。
解決方式
沒什么特別值得書寫的,最重要的還是知道了這個(gè)報(bào)錯(cuò)的原因,解決方式無非根據(jù)場(chǎng)景處理了空值現(xiàn)象。