java.lang.NoClassDefFoundError錯(cuò)誤解決辦法
java.lang.NoClassDefFoundError錯(cuò)誤解決辦法
前言
在日常Java開(kāi)發(fā)中,我們經(jīng)常碰到j(luò)ava.lang.NoClassDefFoundError這樣的錯(cuò)誤,需要花費(fèi)很多時(shí)間去找錯(cuò)誤的原因,具體是哪個(gè)類(lèi)不見(jiàn)了?類(lèi)明明還在,為什么找不到?而且我們很容易把java.lang.NoClassDefFoundError和java.lang.ClassNotfoundException這兩個(gè)錯(cuò)誤搞混,事實(shí)上這兩個(gè)錯(cuò)誤是完全不同的。我們往往花費(fèi)時(shí)間去不斷嘗試一些其他的方法去解決這個(gè)問(wèn)題,而沒(méi)有真正去理解這個(gè)錯(cuò)誤的原因。這篇文章就是通過(guò)解決NoClassDefFoundError錯(cuò)誤處理的經(jīng)驗(yàn)分享來(lái)揭開(kāi)NoClassDefFoundError的一些秘密。NoClassDefFoundError的錯(cuò)誤并非不能解決或者說(shuō)很難解決,只是這種錯(cuò)誤的表現(xiàn)形式很容易迷惑其他的Java開(kāi)發(fā)者。下面我們來(lái)分析下為什么會(huì)發(fā)生NoClassDefFoundError這樣的錯(cuò)誤,以及怎樣去解決這個(gè)錯(cuò)誤。
NoClassDefFoundError錯(cuò)誤發(fā)生的原因
NoClassDefFoundError錯(cuò)誤的發(fā)生,是因?yàn)镴ava虛擬機(jī)在編譯時(shí)能找到合適的類(lèi),而在運(yùn)行時(shí)不能找到合適的類(lèi)導(dǎo)致的錯(cuò)誤。例如在運(yùn)行時(shí)我們想調(diào)用某個(gè)類(lèi)的方法或者訪(fǎng)問(wèn)這個(gè)類(lèi)的靜態(tài)成員的時(shí)候,發(fā)現(xiàn)這個(gè)類(lèi)不可用,此時(shí)Java虛擬機(jī)就會(huì)拋出NoClassDefFoundError錯(cuò)誤。與ClassNotFoundException的不同在于,這個(gè)錯(cuò)誤發(fā)生只在運(yùn)行時(shí)需要加載對(duì)應(yīng)的類(lèi)不成功,而不是編譯時(shí)發(fā)生。很多Java開(kāi)發(fā)者很容易在這里把這兩個(gè)錯(cuò)誤搞混。
簡(jiǎn)單總結(jié)就是,NoClassDefFoundError發(fā)生在編譯時(shí)對(duì)應(yīng)的類(lèi)可用,而運(yùn)行時(shí)在Java的classpath路徑中,對(duì)應(yīng)的類(lèi)不可用導(dǎo)致的錯(cuò)誤。發(fā)生NoClassDefFoundError錯(cuò)誤時(shí),你能看到如下的錯(cuò)誤日志:
Exception in thread "main" java.lang.NoClassDefFoundError
錯(cuò)誤的信息很明顯地指明main線(xiàn)程無(wú)法找到指定的類(lèi),而這個(gè)main線(xiàn)程可能時(shí)主線(xiàn)程或者其他子線(xiàn)程。如果是主線(xiàn)程發(fā)生錯(cuò)誤,程序?qū)⒈罎⒒蛲V梗绻亲泳€(xiàn)程,則子線(xiàn)程停止,其他線(xiàn)程繼續(xù)運(yùn)行。
NoClassDefFoundError和ClassNotFoundException區(qū)別
我們經(jīng)常被java.lang.ClassNotFoundException和java.lang.NoClassDefFoundError這兩個(gè)錯(cuò)誤迷惑不清,盡管他們都與Java classpath有關(guān),但是他們完全不同。NoClassDefFoundError發(fā)生在JVM在動(dòng)態(tài)運(yùn)行時(shí),根據(jù)你提供的類(lèi)名,在classpath中找到對(duì)應(yīng)的類(lèi)進(jìn)行加載,但當(dāng)它找不到這個(gè)類(lèi)時(shí),就發(fā)生了java.lang.NoClassDefFoundError的錯(cuò)誤,而ClassNotFoundException是在編譯的時(shí)候在classpath中找不到對(duì)應(yīng)的類(lèi)而發(fā)生的錯(cuò)誤。ClassNotFoundException比NoClassDefFoundError容易解決,是因?yàn)樵诰幾g時(shí)我們就知道錯(cuò)誤發(fā)生,并且完全是由于環(huán)境的問(wèn)題導(dǎo)致。而如果你在J2EE的環(huán)境下工作,并且得到NoClassDefFoundError的異常,而且對(duì)應(yīng)的錯(cuò)誤的類(lèi)是確實(shí)存在的,這說(shuō)明這個(gè)類(lèi)對(duì)于類(lèi)加載器來(lái)說(shuō),可能是不可見(jiàn)的。
怎么解決NoClassDefFoundError錯(cuò)誤
根據(jù)前文,很明顯NoClassDefFoundError的錯(cuò)誤是因?yàn)樵谶\(yùn)行時(shí)類(lèi)加載器在classpath下找不到需要加載的類(lèi),所以我們需要把對(duì)應(yīng)的類(lèi)加載到classpath中,或者檢查為什么類(lèi)在classpath中是不可用的,這個(gè)發(fā)生可能的原因如下:
- 對(duì)應(yīng)的Class在java的classpath中不可用
- 你可能用jar命令運(yùn)行你的程序,但類(lèi)并沒(méi)有在jar文件的manifest文件中的classpath屬性中定義
- 可能程序的啟動(dòng)腳本覆蓋了原來(lái)的classpath環(huán)境變量
- 因?yàn)镹oClassDefFoundError是java.lang.LinkageError的一個(gè)子類(lèi),所以可能由于程序依賴(lài)的原生的類(lèi)庫(kù)不可用而導(dǎo)致
- 檢查日志文件中是否有java.lang.ExceptionInInitializerError這樣的錯(cuò)誤,NoClassDefFoundError有可能是由于靜態(tài)初始化失敗導(dǎo)致的
- 如果你工作在J2EE的環(huán)境,有多個(gè)不同的類(lèi)加載器,也可能導(dǎo)致NoClassDefFoundError
下面我們看一些當(dāng)發(fā)生NoClassDefFoundError時(shí),我們?cè)撊绾谓鉀Q的樣例。
NoClassDefFoundError解決示例
當(dāng)發(fā)生由于缺少jar文件,或者jar文件沒(méi)有添加到classpath,或者jar的文件名發(fā)生變更會(huì)導(dǎo)致java.lang.NoClassDefFoundError的錯(cuò)誤。
當(dāng)類(lèi)不在classpath中時(shí),這種情況很難確切的知道,但如果在程序中打印出System.getproperty(“java.classpath”),可以得到程序?qū)嶋H運(yùn)行的classpath
運(yùn)行時(shí)明確指定你認(rèn)為程序能正常運(yùn)行的 -classpath 參數(shù),如果增加之后程序能正常運(yùn)行,說(shuō)明原來(lái)程序的classpath被其他人覆蓋了。
NoClassDefFoundError也可能由于類(lèi)的靜態(tài)初始化模塊錯(cuò)誤導(dǎo)致,當(dāng)你的類(lèi)執(zhí)行一些靜態(tài)初始化模塊操作,如果初始化模塊拋出異常,哪些依賴(lài)這個(gè)類(lèi)的其他類(lèi)會(huì)拋出NoClassDefFoundError的錯(cuò)誤。如果你查看程序日志,會(huì)發(fā)現(xiàn)一些java.lang.ExceptionInInitializerError的錯(cuò)誤日志,ExceptionInInitializerError的錯(cuò)誤會(huì)導(dǎo)致java.lang.NoClassDefFoundError: Could not initialize class,如下面的代碼示例:
/**
* Java program to demonstrate how failure of static initialization subsequently cause
* java.lang.NoClassDefFoundError in Java.
* @author Javin Paul
*/
public class NoClassDefFoundErrorDueToStaticInitFailure {
public static void main(String args[]){
List<User> users = new ArrayList<User>(2);
for(int i=0; i<2; i++){
try{
users.add(new User(String.valueOf(i))); //will throw NoClassDefFoundError
}catch(Throwable t){
t.printStackTrace();
}
}
}
}
class User{
private static String USER_ID = getUserId();
public User(String id){
this.USER_ID = id;
}
private static String getUserId() {
throw new RuntimeException("UserId Not found");
}
}
Output
java.lang.ExceptionInInitializerError
at testing.NoClassDefFoundErrorDueToStaticInitFailure.main(NoClassDefFoundErrorDueToStaticInitFailure.java:23)
Caused by: java.lang.RuntimeException: UserId Not found
at testing.User.getUserId(NoClassDefFoundErrorDueToStaticInitFailure.java:41)
at testing.User.<clinit>(NoClassDefFoundErrorDueToStaticInitFailure.java:35)
... 1 more
java.lang.NoClassDefFoundError: Could not initialize class testing.User
at testing.NoClassDefFoundErrorDueToStaticInitFailure.main(NoClassDefFoundErrorDueToStaticInitFailure.java:23)
Read more: http://javarevisited.blogspot.com/2011/06/noclassdeffounderror-exception-in.html#ixzz3dqtbvHDy
由于NoClassDefFoundError是LinkageError的子類(lèi),而LinkageError的錯(cuò)誤在依賴(lài)其他的類(lèi)時(shí)會(huì)發(fā)生,所以如果你的程序依賴(lài)原生的類(lèi)庫(kù)和需要的dll不存在時(shí),有可能出現(xiàn)java.lang.NoClassDefFoundError。這種錯(cuò)誤也可能拋出java.lang.UnsatisfiedLinkError: no dll in java.library.path Exception Java這樣的異常。解決的辦法是把依賴(lài)的類(lèi)庫(kù)和dll跟你的jar包放在一起。
如果你使用Ant構(gòu)建腳本來(lái)生成jar文件和manifest文件,要確保Ant腳本獲取的是正確的classpath值寫(xiě)入到manifest.mf文件
Jar文件的權(quán)限問(wèn)題也可能導(dǎo)致NoClassDefFoundError,如果你的程序運(yùn)行在像linux這樣多用戶(hù)的操作系統(tǒng)種,你需要把你應(yīng)用相關(guān)的資源文件,如Jar文件,類(lèi)庫(kù)文件,配置文件的權(quán)限單獨(dú)分配給程序所屬用戶(hù)組,如果你使用了多個(gè)用戶(hù)不同程序共享的jar包時(shí),很容易出現(xiàn)權(quán)限問(wèn)題。比如其他用戶(hù)應(yīng)用所屬權(quán)限的jar包你的程序沒(méi)有權(quán)限訪(fǎng)問(wèn),會(huì)導(dǎo)致java.lang.NoClassDefFoundError的錯(cuò)誤?;赬ML配置的程序也可能導(dǎo)致NoClassDefFoundError的錯(cuò)誤。比如大多數(shù)Java的框架像Spring,Struts使用xml配置獲取對(duì)應(yīng)的bean信息,如果你輸入了錯(cuò)誤的名稱(chēng),程序可能會(huì)加載其他錯(cuò)誤的類(lèi)而導(dǎo)致NoClassDefFoundError異常。我們?cè)谑褂肧pring MVC框架或者Apache Struts框架,在部署War文件或者EAR文件時(shí)就經(jīng)常會(huì)出現(xiàn)Exception in thread “main” java.lang.NoClassDefFoundError。
在有多個(gè)ClassLoader的J2EE的環(huán)境中,很容易出現(xiàn)NoClassDefFoundError的錯(cuò)誤。由于J2EE沒(méi)有指明標(biāo)準(zhǔn)的類(lèi)加載器,使用的類(lèi)加載器依賴(lài)與不同的容器像Tomcat、WebLogic,WebSphere加載J2EE的不同組件如War包或者EJB-JAR包。關(guān)于類(lèi)加載器的相關(guān)知識(shí)可以參考這篇文章類(lèi)加載器的工作原理。
總結(jié)來(lái)說(shuō),類(lèi)加載器基于三個(gè)機(jī)制:委托、可見(jiàn)性和單一性,委托機(jī)制是指將加載一個(gè)類(lèi)的請(qǐng)求交給父類(lèi)加載器,如果這個(gè)父類(lèi)加載器不能夠找到或者加載這個(gè)類(lèi),那么再加載它。可見(jiàn)性的原理是子類(lèi)的加載器可以看見(jiàn)所有的父類(lèi)加載器加載的類(lèi),而父類(lèi)加載器看不到子類(lèi)加載器加載的類(lèi)。單一性原理是指僅加載一個(gè)類(lèi)一次,這是由委托機(jī)制確保子類(lèi)加載器不會(huì)再次加載父類(lèi)加載器加載過(guò)的類(lèi)?,F(xiàn)在假設(shè)一個(gè)User類(lèi)在WAR文件和EJB-JAR文件都存在,并且被WAR ClassLoader加載,而WAR ClassLoader是加載EJB-JAR ClassLoader的子ClassLoader。當(dāng)EJB-JAR中代碼引用這個(gè)User類(lèi)時(shí),加載EJB-JAR所有class的Classloader找不到這個(gè)類(lèi),因?yàn)檫@個(gè)類(lèi)已經(jīng)被EJB-JAR classloader的子加載器WAR classloader加載。
這會(huì)導(dǎo)致的結(jié)果就是對(duì)User類(lèi)出現(xiàn)NoClassDefFoundError異常,而如果在兩個(gè)JAR包中這個(gè)User類(lèi)都存在,如果你使用equals方法比較兩個(gè)類(lèi)的對(duì)象時(shí),會(huì)出現(xiàn)ClassCastException的異常,因?yàn)閮蓚€(gè)不同類(lèi)加載器加載的類(lèi)無(wú)法進(jìn)行比較。
有時(shí)候會(huì)出現(xiàn)Exception in thread “main” java.lang.NoClassDefFoundError: com/sun/tools/javac/Main 這樣的錯(cuò)誤,這個(gè)錯(cuò)誤說(shuō)明你的Classpath, PATH 或者 JAVA_HOME沒(méi)有安裝配置正確或者JDK的安裝不正確。這個(gè)問(wèn)題的解決辦法時(shí)重新安裝你的JDK。
Java在執(zhí)行l(wèi)inking操作的時(shí)候,也可能導(dǎo)致NoClassDefFoundError。例如在前面的腳本中,如果在編譯完成之后,我們刪除User的編譯文件,再運(yùn)行程序,這個(gè)時(shí)候你就會(huì)直接得到NoClassDefFoundError,而錯(cuò)誤的消息只打印出User類(lèi)的名稱(chēng)。
java.lang.NoClassDefFoundError: testing/User at testing.NoClassDefFoundErrorDueToStaticInitFailure.main(NoClassDefFoundErrorDueToStaticInitFailure.java:23)
現(xiàn)在我們知道要怎樣去面對(duì)NoClassDefFoundError異常并解決它了。
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
相關(guān)文章
Java統(tǒng)計(jì)一個(gè)字符串在另外一個(gè)字符串出現(xiàn)次數(shù)的方法
這篇文章主要介紹了Java統(tǒng)計(jì)一個(gè)字符串在另外一個(gè)字符串出現(xiàn)次數(shù)的方法,涉及java字符串遍歷、正則匹配等相關(guān)操作技巧,需要的朋友可以參考下2018-03-03
MyBatisPlus唯一索引批量新增或修改的實(shí)現(xiàn)方法
本文主要介紹了MyBatisPlus唯一索引批量新增或修改的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03
關(guān)于SpringBoot整合RabbitMQ實(shí)現(xiàn)死信隊(duì)列
這篇文章主要介紹了關(guān)于SpringBoot整合RabbitMQ實(shí)現(xiàn)死信隊(duì)列,死信隊(duì)列實(shí)際上就是一個(gè)普通的隊(duì)列,只是這個(gè)隊(duì)列跟死信交換機(jī)進(jìn)行了綁定,用來(lái)存放死信而已,需要的朋友可以參考下2023-05-05
java加載properties文件的六種方法總結(jié)
這篇文章主要介紹了java加載properties文件的六種方法總結(jié)的相關(guān)資料,需要的朋友可以參考下2017-05-05
數(shù)組實(shí)現(xiàn)Java 自定義Queue隊(duì)列及應(yīng)用操作
這篇文章主要介紹了數(shù)組實(shí)現(xiàn)Java 自定義Queue隊(duì)列及應(yīng)用操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
springboot集成springsecurity 使用OAUTH2做權(quán)限管理的教程
這篇文章主要介紹了springboot集成springsecurity 使用OAUTH2做權(quán)限管理的教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12
使用IDEA將Java/Kotliin工程導(dǎo)出Jar包的正確姿勢(shì)
這篇文章主要介紹了使用IDEA將Java/Kotliin工程導(dǎo)出Jar包的正確姿勢(shì),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
SpringBoot實(shí)現(xiàn)RAS+AES自動(dòng)接口解密
本文主要介紹了SpringBoot實(shí)現(xiàn)RAS+AES自動(dòng)接口解密,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03

