最近因為項目的原因,發(fā)現(xiàn)一個運行時錯誤:NoClassDefFoundError,對于這個常見的java Error進(jìn)行一下總結(jié)。同時區(qū)分一下NoClassDefFoundError與ClassNotFoundException。
ClassNotFoundException
下面是一個ClassNotFoundException出現(xiàn)的場景,當(dāng)使用JDBC去連接數(shù)據(jù)庫的時候,一般會使用Class.forName()的方式去加載JDBC的驅(qū)動,如果沒有將驅(qū)動放到應(yīng)用的classpath下,那么會導(dǎo)致運行時找不到類,所以運行Class.forName()會拋出ClassNotFoundException:
public class MainClass {
public static void main(String[] args) {
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Log
java.lang.ClassNotFoundException: oracle.jdbc.driver.OracleDriver
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at com.ebay.app.SampleResource.main(SampleResource.java:43)
NoClassDefFoundError
NoClassDefFoundError是一個運行時異常,造成這個異常的原因,是在運行過程中,JVM無法找到運行時需要的class文件。
為什么在編譯時能夠通過,但是運行時卻無法找到對應(yīng)的class類。下面舉一個例子。
有三個類,DependencyA(A),DependencyB(B),DependencyC(C)。這三個類是不同項目下的。
其中依賴關(guān)系:
-
A依賴B,并使用B中的打印方法:
-
B中依賴C,并使用了C中的方法:
pom.xml 如下:
<dependencies>
<dependency>
<groupId>com.test</groupId>
<artifactId>C</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
-
C只是進(jìn)行一個簡單的輸出:
NoClassDefFoundError
當(dāng)執(zhí)行A中的main方法時,可以看到,正常打印了A中的語句和B中的語句,但是在繼續(xù)運行C的方法的時候,出現(xiàn)異常java.lang.NoClassDefFoundError: com/demo/c/DependencyC
-
Log
- Pom
由于demo中的代碼使用maven進(jìn)行依賴管理,需要考慮依賴問題。當(dāng)查看pom文件配置后發(fā)現(xiàn),程序A中只引入了B所在的jar包,但是對于B中需要的C類的jar包沒有做依賴聲明。
<dependencies>
<dependency>
<groupId>com.test</groupId>
<artifactId>B</artifactId>
</dependency>
</dependencies>
- 分析
A的做法是標(biāo)準(zhǔn)的Maven的做法,但是由于B對C的依賴是provided。而對于scope=provided的情況,maven 不會管理C,因此在class path 就沒有C的jar。最常見的這類artifact是servlet-api.jar。
當(dāng)JVM在進(jìn)行類加載的過程中,A依賴B,加載B的時候,從當(dāng)前的依賴的Jar中找到對應(yīng)的class文件,B中打印語句可以正常執(zhí)行。當(dāng)B依賴C,JVM開始加載C的過程中發(fā)現(xiàn),無法在當(dāng)前class path中找到對應(yīng)的class文件,所以在這種情況下,就會報出NoClassDefFoundError。
其它出現(xiàn)NoClassDefFoundError的形式
其實還有更簡單的形式,例如編譯通過后,手動刪除引用的某個class文件;修改引用的jar包名稱。歸根結(jié)底,NoClassDefFoundError出現(xiàn)的原因都是因為類加載過程中沒有辦法找到對應(yīng)的class文件。
在源碼中也明確寫道,當(dāng)JVM或類加載器嘗試加載一個類時,這個類對應(yīng)的class文件沒有找到時,JVM會報出當(dāng)前的錯誤。
/**
* Thrown if the Java Virtual Machine or a <code>ClassLoader</code> instance
* tries to load in the definition of a class (as part of a normal method call
* or as part of creating a new instance using the <code>new</code> expression)
* and no definition of the class could be found.
* <p>
* The searched-for class definition existed when the currently
* executing class was compiled, but the definition can no longer be
* found.
*
* @author unascribed
* @since JDK1.0
*/
public
class NoClassDefFoundError extends LinkageError {
private static final long serialVersionUID = 9095859863287012458L;
/**
* Constructs a <code>NoClassDefFoundError</code> with no detail message.
*/
public NoClassDefFoundError() {
super();
}
/**
* Constructs a <code>NoClassDefFoundError</code> with the specified
* detail message.
*
* @param s the detail message.
*/
public NoClassDefFoundError(String s) {
super(s);
}
}
解決方案
以demo中演示的代碼為例,只需要在A所在的Project中引入C所在的依賴,即可解決當(dāng)前問題:

NoClassDefFoundError與ClassNotFoundException
如果JVM或者ClassLoader在加載類時找不到對應(yīng)的類,就會引發(fā)NoClassDefFoundError和ClassNotFoundException。由于不同的ClassLoader會從不同的地方加載類,有時是錯誤的CLASSPATH引發(fā)這類錯誤,有時是某個庫的jar包缺失引發(fā)這類錯誤。NoClassDefFoundError和ClassNotFoundException之間存在一些細(xì)微的不同點。
NoClassDefFoundError
- 表示該類在編譯階段還可以找到,但是在運行Java應(yīng)用的時候找不到了,有時靜態(tài)塊的初始化過程會導(dǎo)致NoClassDefFoundError。
- NoClassDefFoundError是Error,是unchecked,因此也不需要使用try-catch或者finally語句塊包圍。
-
NoClassDefFoundError 是鏈接錯誤,發(fā)生在鏈接階段,當(dāng)解析引用的時候找不到對應(yīng)的類,就會拋出java.lang.NoClassDefFoundError;ClassNotFoundException是異常,發(fā)生在運行階段。
常見Case
如果開發(fā)中遇到NoClassDefFoundError,那么最有可能的原因
- jar 包不存在
- 存在多個類加載器和多個目標(biāo)類,即我們常說的Jar包沖突。比如說上文的B ,如果有兩個版本的B存在于依賴中 中, B1是compile scope , B2 是provided scope,如果maven 解析到B1,就不會有NoClassDefFoundError的問題,但是解析到B2,就有NoClassDefFoundError的問題。這種情況迷惑性比較大,第一次build 是可以的,同樣的code 第二次build 就不可以了。非常難以定位。解決這種問題,通常需要用mvn dependency tree 來查看到底引用了哪些同名的Artifact,進(jìn)而exclude 錯誤的artifacts。
ClassNotFoundException
- 和編譯期沒什么關(guān)系,當(dāng)你在代碼中顯式加載類(使用Class.forName())時沒有找到對應(yīng)的類,則會拋出java.lang.ClassNotFoundException。例如加載SQL驅(qū)動時,對應(yīng)的類加載器找不到驅(qū)動類。
- ClassNotFoundException是受檢異常(checked Exception),因此需要使用try-catch語句塊或者try-finally語句塊包圍,否則會導(dǎo)致編譯錯誤
常見Case
調(diào)用Class.forName()、ClassLoader.findSystemClass()和ClassLoader.loadClass()等方法時可能會引起java.lang.ClassNotFoundException。




