當在靜態初始化塊中出現了異常的時候,JVM會拋出 java.lang.ExceptionInInitializerError異常。若是你瞭解Java中的靜態變量,你會知道它們是在類加載的時候進行初始化的。若是在這個靜態變量初始化的過程當中出現了異常,那麼就會拋出 java.lang.ExceptionInInitializerError異常。任何異常均可能會引起這種狀況,好比說,java.lang.ArrayIndexOutOfBound或者java.lang.NullPointerException。Java開發人員一般會被這個錯誤弄暈,他以爲本身並無定義任何的靜態初始化塊,爲何還會拋出ExceptionInInitializerError異常;事實上,Java默認會將靜態變量的初始化放在一個默認的靜態初始化塊中,而後按它們在源文件中聲明的順序來進行初始化。好比說變量ABC聲明在第一行,在第二行中使用到了,而在第三行的時候才初始化,那麼第二行的代碼會拋出一個NullPointerException異常,這個異常會被封裝到一個ExceptionInInitializerError異常中,若是這段代碼在主線程中執行了,你會看到控制檯或者日誌文件中出現這樣的錯誤信息: "Exception in thread "main" java.lang.ExceptionInInitializerError"。在一個擁有大量日誌文件的大型系統中,這樣的錯誤很容易被忽略,而程序員會獲得一個java.lang.NoClassDefFoundError異常。不幸的是只有當別人使用到了這個類的時候纔會出現這個錯誤,由於ExceptionInInitializerError致使了這個類沒法加載。因爲類加載失敗了,所以JVM會拋出NoClassDefFoundError。有的時候這會誤導Java開發人員,他們會檢查類路徑,PATH,以及java.library.path看是否是缺乏了這個類,卻又發現不了任何問題,這讓他們很困惑。若是你在分析NoClassDefFoundError的緣由,你最好看下你的日誌文件中有沒有ExceptionInInitializerError,而後再考慮要不要檢查classpath。本文中咱們將看到一段代碼,它會在靜態初始化過程當中引起異常從而致使 "Exception in thread "main" java.lang.ExceptionInInitializerError"。在稍後的部分,咱們將會看到如何去解決這個問題。
Exception in thread "main" java.lang.ExceptionInInitializerError的緣由
正如別的錯誤或者異常同樣,當你看見這行信息,你知道這是出現ExceptionInInitializerError異常了,這個異常是因爲類加載過程當中靜態塊初始化過程失敗所致使的。因爲它出如今負責啓動程序的主線程中,所以你最好從主類中開始分析,這裏說的主類是指你在命令行參數中指定的那個,或者說是你聲明瞭public static void main(String args[])方法的那個類。若是你仔細地看一下完整的堆棧跟蹤信息,你其實什麼也不用作,由於JVM已經把類名給打印出來了,這就是引起ExceptionInInitializerError的類。ExceptionInInitializerError是LinkageError的子類,這意味着這個異常會致使你的類沒法加載到JVM的內存中。如今咱們來看一下這個示例程序,它在執行的時候會拋出下面的異常:
java
Exception in thread "main" java.lang.ExceptionInInitializerError 程序員
Caused by: java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 ui
at java.util.ArrayList.rangeCheck(ArrayList.java:635) this
at java.util.ArrayList.get(ArrayList.java:411) spa
at StaticInitiazerDemo.<clinit>(StaticInitiazerDemo.java:15) .net
Exception in thread "main" java.lang.ExceptionInInitializerError Caused by: java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 at java.util.ArrayList.rangeCheck(ArrayList.java:635) at java.util.ArrayList.get(ArrayList.java:411) at StaticInitiazerDemo.<clinit>(StaticInitiazerDemo.java:15)
看一下棧跟蹤信息,你知道真正的異常是java.lang.IndexOutOfBoundsException,它在StaticInitiazerDemo的第二行被拋出來了。這是因爲你調用了ArrayList的get()方法並傳入了位置0,而這個ArrayList的大小也是0(Index: 0, Size: 0)。看到這條信息後你知道當咱們想從列表中取出第一張CreditCard時,這個列表是空的。
命令行
import java.util.ArrayList; 日誌
import java.util.List;
/**
* Java Program to understand and solve ExceptionInitializerError, which comes
* When static initializer blocks throws unchecked exception during class loading
* and initialization.
*
* @author Javin Paul
*/
public class StaticInitializerDemo{
private static final List<CreditCard> cards = new ArrayList<CreditCard>();
private static CreditCard prefferdCard = cards.get(0); // 1st card is default
public static boolean isVisa = "VISA".equalsIgnoreCase(prefferdCard.getNetwork());
public static void main(String args[]) {
makePayment(prefferdCard);
}
public static void makePayment(CreditCard cc) {
if (isVisa) {
//offer 5% discount
}
// deduct payment
}
}
class CreditCard {
private long card_number; //16 digit card number
private int cvv; // 3 digit cvv number
private int expiryMonth;
private int expiryYear;
private String bank;
private String network;
public CreditCard(long card_number, int cvv, int expiryMonth, int expiryYear, String bank, String network) {
super();
this.card_number = card_number;
this.cvv = cvv;
this.expiryMonth = expiryMonth;
this.expiryYear = expiryYear;
this.bank = bank;
this.network = network;
}
/**
* @return the card_number
*/
public final long getCard_number() {
return card_number;
}
/**
* @return the cvv
*/
public final int getCvv() {
return cvv;
}
/**
* @return the expiryMonth
*/
public final int getExpiryMonth() {
return expiryMonth;
}
/**
* @return the expiryYear
*/
public final int getExpiryYear() {
return expiryYear;
}
/**
* @return the bank
*/
public final String getBank() {
return bank;
}
/**
* @return the network
*/
public final String getNetwork() {
return network;
}
}
import java.util.ArrayList; import java.util.List; /** * Java Program to understand and solve ExceptionInitializerError, which comes * When static initializer blocks throws unchecked exception during class loading * and initialization. * * @author Javin Paul */ public class StaticInitializerDemo{ private static final List<CreditCard> cards = new ArrayList<CreditCard>(); private static CreditCard prefferdCard = cards.get(0); // 1st card is default public static boolean isVisa = "VISA".equalsIgnoreCase(prefferdCard.getNetwork()); public static void main(String args[]) { makePayment(prefferdCard); } public static void makePayment(CreditCard cc) { if (isVisa) { //offer 5% discount } // deduct payment } } class CreditCard { private long card_number; //16 digit card number private int cvv; // 3 digit cvv number private int expiryMonth; private int expiryYear; private String bank; private String network; public CreditCard(long card_number, int cvv, int expiryMonth, int expiryYear, String bank, String network) { super(); this.card_number = card_number; this.cvv = cvv; this.expiryMonth = expiryMonth; this.expiryYear = expiryYear; this.bank = bank; this.network = network; } /** * @return the card_number */ public final long getCard_number() { return card_number; } /** * @return the cvv */ public final int getCvv() { return cvv; } /** * @return the expiryMonth */ public final int getExpiryMonth() { return expiryMonth; } /** * @return the expiryYear */ public final int getExpiryYear() { return expiryYear; } /** * @return the bank */ public final String getBank() { return bank; } /** * @return the network */ public final String getNetwork() { return network; } }
輸出:
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.rangeCheck(Unknown Source)
at java.util.ArrayList.get(Unknown Source)
at StaticInitializerDemo.<clinit>(StaticInitializerDemo.java:15)
Exception in thread "main" java.lang.ExceptionInInitializerError Caused by: java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 at java.util.ArrayList.rangeCheck(Unknown Source) at java.util.ArrayList.get(Unknown Source) at StaticInitializerDemo.<clinit>(StaticInitializerDemo.java:15)
這裏是Java中全部Error類的類結構。你能夠看到ExceptionInInitializerError是繼承自LinkageError的。還應當知道的是,像RuntimeException同樣,Error也是未檢查異常,編譯器是不去檢查有沒有相應的異常處理代碼的。
如何解決Exception in thread "main" java.lang.ExceptionInInitializerError 須要記住如下幾點: 1. "Exception in thread "main" java.lang.ExceptionInInitializerError"意味着異常出如今主線程,而且是LinkageError的一個子類java.lang.ExceptionInInitializerError,這是JVM類加載失敗時才拋出的,緣由是靜態初始化代碼中出現了諸如IndexOutOfBoundsException或者NullPointerException這樣的RuntimeException。 2. 記住JVM會將全部的靜態變量的初始化按它們在源文件中的出現順序放到一個靜態初始化塊中。所以,不要以爲沒有看到靜態初始塊就認爲不會出現這個異常。事實上,你得確保靜態變量的正確順序,好比說,若是 一個變量初始化的時候用到了另外一個變量,你得確保這個變量在前面已經初始化過了。 3. 若是別的代碼想要使用這個類,則會拋出ExceptionInInitializerError異常,而它又會致使ClassNotFoundException或者NoClassDefFoundError。爲何?由於這個類加載失敗了,並無加載到JVM的內存中。所以若是你在解決類不存在之類的異常時,先看看你的日誌文件中有沒有這個異常。 4. 記住靜態初始化代碼塊會拋出RuntimeException而不是已檢查異常,然後者須要有對應的catch塊來進行處理。 這就是關於Exception in thread "main" java.lang.ExceptionInInitializerError的全部東西了。你已經瞭解到瞭如何去跟蹤此類問題,並找出拋出這個異常的罪魁禍首。須要謹記的是這個異常的一個反作用是NoClassDefFoundError,而Java程序拋出這個異常的位置可能會離java.lang.ExceptionInInitializerError很遠,這取決於你的客戶端代碼什麼時候引用到這個類。所以,在查看類路徑解決NoClassDefFoundError異常以前,最好先看看日誌有沒有出現ExceptionInInitializerError。