Java中的ExceptionInInitializerError异常及解决方法
2019獨角獸企業重金招聘Python工程師標準>>>
當在靜態初始化塊中出現了異常的時候,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??
????????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時,這個列表是空的。
Java代碼 ?
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;??
????}??
}??
輸出:
Java代碼 ?
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。
轉載于:https://my.oschina.net/solarxie/blog/650961
總結
以上是生活随笔為你收集整理的Java中的ExceptionInInitializerError异常及解决方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: redis简单了解 二 (集群)
- 下一篇: Java 反射的理解