在平常Java開發中,咱們常常碰到java.lang.NoClassDefFoundError這樣的錯誤,須要花費不少時間去找錯誤的緣由,具體是哪一個類不見了?類明明還在,爲何找不到?並且咱們很容易把java.lang.NoClassDefFoundError和java.lang.ClassNotfoundException這兩個錯誤搞混,事實上這兩個錯誤是徹底不一樣的。咱們每每花費時間去不斷嘗試一些其餘的方法去解決這個問題,而沒有真正去理解這個錯誤的緣由。這篇文章就是經過解決NoClassDefFoundError錯誤處理的經驗分享來揭開NoClassDefFoundError的一些祕密。NoClassDefFoundError的錯誤並不是不能解決或者說很難解決,只是這種錯誤的表現形式很容易迷惑其餘的Java開發者。下面咱們來分析下爲何會發生NoClassDefFoundError這樣的錯誤,以及怎樣去解決這個錯誤。html
NoClassDefFoundError錯誤的發生,是由於Java虛擬機在編譯時能找到合適的類,而在運行時不能找到合適的類致使的錯誤。例如在運行時咱們想調用某個類的方法或者訪問這個類的靜態成員的時候,發現這個類不可用,此時Java虛擬機就會拋出NoClassDefFoundError錯誤。與ClassNotFoundException的不一樣在於,這個錯誤發生只在運行時須要加載對應的類不成功,而不是編譯時發生。不少Java開發者很容易在這裏把這兩個錯誤搞混。java
簡單總結就是,NoClassDefFoundError發生在編譯時對應的類可用,而運行時在Java的classpath路徑中,對應的類不可用致使的錯誤。發生NoClassDefFoundError錯誤時,你能看到以下的錯誤日誌:linux
Exception in thread "main" java.lang.NoClassDefFoundError
錯誤的信息很明顯地指明main線程沒法找到指定的類,而這個main線程可能時主線程或者其餘子線程。若是是主線程發生錯誤,程序將崩潰或中止,而若是是子線程,則子線程中止,其餘線程繼續運行。框架
咱們常常被java.lang.ClassNotFoundException和java.lang.NoClassDefFoundError這兩個錯誤迷惑不清,儘管他們都與Java classpath有關,可是他們徹底不一樣。NoClassDefFoundError發生在JVM在動態運行時,根據你提供的類名,在classpath中找到對應的類進行加載,但當它找不到這個類時,就發生了java.lang.NoClassDefFoundError的錯誤,而ClassNotFoundException是在編譯的時候在classpath中找不到對應的類而發生的錯誤。ClassNotFoundException比NoClassDefFoundError容易解決,是由於在編譯時咱們就知道錯誤發生,而且徹底是因爲環境的問題致使。而若是你在J2EE的環境下工做,而且獲得NoClassDefFoundError的異常,並且對應的錯誤的類是確實存在的,這說明這個類對於類加載器來講,多是不可見的。this
根據前文,很明顯NoClassDefFoundError的錯誤是由於在運行時類加載器在classpath下找不到須要加載的類,因此咱們須要把對應的類加載到classpath中,或者檢查爲何類在classpath中是不可用的,這個發生可能的緣由以下:spa
下面咱們看一些當發生NoClassDefFoundError時,咱們該如何解決的樣例。操作系統
/** * 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
在有多個ClassLoader的J2EE的環境中,很容易出現NoClassDefFoundError的錯誤。因爲J2EE沒有指明標準的類加載器,使用的類加載器依賴與不一樣的容器像Tomcat、WebLogic,WebSphere加載J2EE的不一樣組件如War包或者EJB-JAR包。關於類加載器的相關知識能夠參考這篇文章類加載器的工做原理。.net
總結來講,類加載器基於三個機制:委託、可見性和單一性,委託機制是指將加載一個類的請求交給父類加載器,若是這個父類加載器不可以找到或者加載這個類,那麼再加載它。可見性的原理是子類的加載器能夠看見全部的父類加載器加載的類,而父類加載器看不到子類加載器加載的類。單一性原理是指僅加載一個類一次,這是由委託機制確保子類加載器不會再次加載父類加載器加載過的類。如今假設一個User類在WAR文件和EJB-JAR文件都存在,而且被WAR ClassLoader加載,而WAR ClassLoader是加載EJB-JAR ClassLoader的子ClassLoader。當EJB-JAR中代碼引用這個User類時,加載EJB-JAR全部class的Classloader找不到這個類,由於這個類已經被EJB-JAR classloader的子加載器WAR classloader加載。線程
這會致使的結果就是對User類出現NoClassDefFoundError異常,而若是在兩個JAR包中這個User類都存在,若是你使用equals方法比較兩個類的對象時,會出現ClassCastException的異常,由於兩個不一樣類加載器加載的類沒法進行比較。3d
有時候會出現Exception in thread 「main」 java.lang.NoClassDefFoundError: com/sun/tools/javac/Main 這樣的錯誤,這個錯誤說明你的Classpath, PATH 或者 JAVA_HOME沒有安裝配置正確或者JDK的安裝不正確。這個問題的解決辦法時從新安裝你的JDK。
java.lang.NoClassDefFoundError: testing/User at testing.NoClassDefFoundErrorDueToStaticInitFailure.main(NoClassDefFoundErrorDueToStaticInitFailure.java:23)
如今咱們知道要怎樣去面對NoClassDefFoundError異常並解決它了。
參考文章:
3 ways to solve java.lang.NoClassDefFoundError in Java J2EE