今天在項目開發時遇到一個問題,整個項目是使用Spring等框架搭建起來的在運行項目時不報任何的異常信息,就是找不到某個類信息,各方查找該類確實是存在的,最后通過斷點跟蹤時在異常棧內發現java.lang.ExceptionInInitializerError這個異常信息,但這個異常信息沒有在控制台或者日志系統中拋出來,查明原因之后就對症下葯最終解決了該問題。查找該問題也着實費了一翻功夫,正好趁此機會把該問題引發的原因及針對這類問題應該如何來避免記錄一下。由於該項目中的代碼比較復雜,這里舉一個簡單的例子來說明一下該異常產生的原因。
java.lang.ExceptionInInitializerError
該異常在Java中的繼承關系如下:
下面是jdk對該異常的描述
public class ExceptionInInitializerErrorextends LinkageError
靜態初始化程序中發生意外異常的信號。拋出 ExceptionInInitializerError
表明在計算靜態初始值或靜態變量的初始值期間發生異常。
從版本 1.4 開始,這個異常已經更新過,符合通用異常鏈機制。“保存的 throwable 對象”可能是在構造的時候提供的,並且可以通過 getException()
方法來訪問,這類對象目前被認為是導致異常的原因,可以通過 Throwable.getCause()
方法以及前面提到的“遺留方法”訪問它。
要理解這個異常還得從Java類中的靜態變量初始化過程說起,在Java類中靜態變量的初始化順序和靜態變量的聲明順序是一致的。為了驗證該說法,我們寫一個簡單的小程序,代碼如下:
-
/**
-
*
-
*/
-
package com.mhy.oo.statics;
-
-
import java.util.ArrayList;
-
import java.util.List;
-
-
/**
-
* @author mhy2011@163.com
-
* @date 2011-11-18
-
*/
-
public
class StaticParams {
-
-
private
static
int NUM_A = getA();
-
private
static
int NUM_B = getB();
-
private
static List<String> LIST_A = getListA();
-
-
private StaticParams() {
-
System.out.println(
"初始化構造方法");
-
}
-
-
public static StaticParams getInstance() {
-
return
new StaticParams();
-
}
-
-
private static int getA() {
-
System.out.println(
"初始化A");
-
return
10;
-
}
-
-
private static int getB() {
-
System.out.println(
"初始化B");
-
return
20;
-
}
-
-
private static List<String> getListA() {
-
System.out.println(
"初始化List");
-
return
new ArrayList<String>();
-
}
-
-
public static void main(String[] args) {
-
StaticParams.getInstance();
-
}
-
}
運行該程序,輸出結果如下:
調整靜態變量NUM_A和NUM_B的聲明順序,其他邏輯不變
然后再次運行該程序,輸出結果如下:
說到這,想必聰明的程序員就想到問題原因了。即在某個靜態變量的初始化還未執行之前就使用該靜態變量。為了模擬該問題產生原因,我們修改上面的代碼成一個單例模式,具體代碼如下:
-
/**
-
*
-
*/
-
package com.mhy.oo.statics;
-
-
import java.util.ArrayList;
-
import java.util.List;
-
-
/**
-
* @author mhy2011@163.com
-
* @date 2011-11-18
-
*/
-
public
class StaticParams {
-
-
private
static StaticParams sp = buildStaticParams();
-
-
private
static
int NUM_A = getA();
-
private
static
int NUM_B = getB();
-
private
static List<String> LIST_A = getListA();
-
-
/**
-
* 私有化構造方法
-
*/
-
private StaticParams() {
-
System.out.println(
"初始化構造方法");
-
}
-
-
/**
-
* sp的聲明在其他幾個靜態變量之前
-
*
-
* @return
-
*/
-
private static StaticParams buildStaticParams() {
-
if (
null == sp) {
-
sp =
new StaticParams();
-
}
-
int result = NUM_A * NUM_B;
// 基本類型有默認值,此處不會報錯,但結果不正確
-
System.out.println(
"result is :" + result);
-
LIST_A.add(
"abcd");
//此時LIST_A還未初始化,到此有異常
-
return sp;
-
}
-
-
/**
-
* 獲取StaticParams實例
-
*
-
* @return
-
*/
-
public static StaticParams getInstance() {
-
return sp;
-
}
-
-
private static int getA() {
-
System.out.println(
"初始化A");
-
return
10;
-
}
-
-
private static int getB() {
-
System.out.println(
"初始化B");
-
return
20;
-
}
-
-
private static List<String> getListA() {
-
System.out.println(
"初始化List");
-
return
new ArrayList<String>();
-
}
-
-
public static void main(String[] args) {
-
StaticParams.getInstance();
-
}
-
}
-
在上面的例子中,sp變量初始化中使用到的其他的變量,但其他的靜態變量還未初始化,故而在sp初始化時就會產生上面的異常,運行上面的程序就可以在控制台看到如下異常信息:
說到這里,想必大家在編程過程中就知道如何來避免該異常信息的產生了吧。