java.lang.NoClassDefFoundError 和 java.lang.ClassNotFoundException 都是 Java 語言定義的標準異常。從異常類的名稱看似乎都跟類的定義找不到有關,但是還是有些差異。我們先來看一下 java 規範中對這兩個異常的說明:
java.lang.NoClassDefFoundError:
The searched-for class definition existed when the currently executing class was compiled, but the definition can no longer be found.
類載入器試圖載入類的定義,但是找不到這個類的定義,而實際上這個類檔案是存在的。
java.lang.ClassNotFoundException:
1. The forName method in class Class.
2. The findSystemClass method in class ClassLoader .
3. The loadClass method in class ClassLoader.
but no definition for the class with the specified name could be found.
從規範說明看, java.lang.ClassNotFoundException 異常丟擲的根本原因是類檔案找不到。
另外,從兩個異常的定義看,java.lang.NoClassDefFoundError 是一種 unchecked exception(也稱 runtime exception),而 java.lang.ClassNotFoundException 是一種 checked exception。(區分不了這兩類異常?看這裡 checked exception vs unchecked exception)
----------
有了前面的分析,我們知道這他們是兩個完全不同的異常。但是如果在實際執行程式碼時碰到了其中一個,還是很容易被混淆成同一個,尤其是當事先沒有留意到這兩個異常的差別時。
就我個人而言,在這兩個異常裡,平時碰到最多的是 java.lang.ClassNotFoundException。從異常的名字看,很容易理解這個異常產生的原因是缺少了 .class 檔案,比如少引了某個 jar,解決方法通常需要檢查一下 classpath 下能不能找到包含缺失 .class 檔案的 jar。
但是,很多人在碰到 java.lang.NoClassDefFoundError 異常時也會下意識的去檢查是不是缺少了 .class 檔案,比如 SO 上的這位提問者(java.lang.NoClassDefFoundError: Could not initialize class XXX)-- “明明 classpath 下有那個 jar 為什麼還報這個異常“。而實際上,這個異常的來源根本不是因為缺少 .class 檔案。而碰到這個異常的解決辦法,一般需要檢查這個類定義中的初始化部分(如類屬性定義、static 塊等)的程式碼是否有拋異常的可能,如果是 static 塊,可以考慮在其中將異常捕獲並列印堆疊等,或者直接在對類進行初始化呼叫(如 new Foobar())時作 try catch。
----------
前兩天我也碰到了一個類似場景導致的 java.lang.NoClassDefFoundError: Could not initialize class xxx 異常,下面詳細記錄一下。
我定義了一個類,為了使用 log4j 列印日誌,呼叫 org.slf4j.LoggerFactory 建立了一個 Logger,並作為類的 static 屬性,除此之外無其他的成員屬性,程式碼如下:
public class Foo {
private static Logger logger = LoggerFactory.getLogger(Foo.class);
// ... methods
}
在另一個類裡建立 Foo 例項:
public class Bar {
public void someMethod() {
Foo foo = new Foo();
// ...
}
}
在執行 new Foo() 時拋異常 java.lang.NoClassDefFoundError: Could not initialize class Foo。經過一番排查,這個異常最後的原因出在了 LoggerFactory.getLogger(Foo.class) 呼叫拋錯,關於 這條語句為什麼會拋錯,我會在另一篇文章裡詳細描述,在這裡只是簡單的說下原因:由於 slf4j-api.jar 和 slf4j 的某個 binding jar 版本不相容所致。
----
總結,記住他們是兩個不同的異常類,在碰到具體某個異常時,從名字並聯系它的 message 資訊(如 "Could not initialize class ")就很容易鎖定問題來源。
完。