1. 程式人生 > >怎麼解決java.lang.NoClassDefFoundError錯誤

怎麼解決java.lang.NoClassDefFoundError錯誤

前言

在日常Java開發中,我們經常碰到java.lang.NoClassDefFoundError這樣的錯誤,需要花費很多時間去找錯誤的原因,具體是哪個類不見了?類明明還在,為什麼找不到?而且我們很容易把java.lang.NoClassDefFoundError和java.lang.ClassNotfoundException這兩個錯誤搞混,事實上這兩個錯誤是完全不同的。我們往往花費時間去不斷嘗試一些其他的方法去解決這個問題,而沒有真正去理解這個錯誤的原因。這篇文章就是通過解決NoClassDefFoundError錯誤處理的經驗分享來揭開NoClassDefFoundError的一些祕密。NoClassDefFoundError的錯誤並非不能解決或者說很難解決,只是這種錯誤的表現形式很容易迷惑其他的Java開發者。下面我們來分析下為什麼會發生NoClassDefFoundError這樣的錯誤,以及怎樣去解決這個錯誤。

NoClassDefFoundError錯誤發生的原因

NoClassDefFoundError錯誤的發生,是因為Java虛擬機器在編譯時能找到合適的類,而在執行時不能找到合適的類導致的錯誤。例如在執行時我們想呼叫某個類的方法或者訪問這個類的靜態成員的時候,發現這個類不可用,此時Java虛擬機器就會丟擲NoClassDefFoundError錯誤。與ClassNotFoundException的不同在於,這個錯誤發生只在執行時需要載入對應的類不成功,而不是編譯時發生。很多Java開發者很容易在這裡把這兩個錯誤搞混。

簡單總結就是,NoClassDefFoundError發生在編譯時對應的類可用,而執行時在Java的classpath路徑中,對應的類不可用導致的錯誤。發生NoClassDefFoundError錯誤時,你能看到如下的錯誤日誌:

Exception in thread "main" java.lang.NoClassDefFoundError
  • 1
  • 1

錯誤的資訊很明顯地指明main執行緒無法找到指定的類,而這個main執行緒可能時主執行緒或者其他子執行緒。如果是主執行緒發生錯誤,程式將崩潰或停止,而如果是子執行緒,則子執行緒停止,其他執行緒繼續執行。

NoClassDefFoundError和ClassNotFoundException區別

我們經常被java.lang.ClassNotFoundException和java.lang.NoClassDefFoundError這兩個錯誤迷惑不清,儘管他們都與Java classpath有關,但是他們完全不同。NoClassDefFoundError發生在JVM在動態執行時,根據你提供的類名,在classpath中找到對應的類進行載入,但當它找不到這個類時,就發生了java.lang.NoClassDefFoundError的錯誤,而ClassNotFoundException是在編譯的時候在classpath中找不到對應的類而發生的錯誤。ClassNotFoundException比NoClassDefFoundError容易解決,是因為在編譯時我們就知道錯誤發生,並且完全是由於環境的問題導致。而如果你在J2EE的環境下工作,並且得到NoClassDefFoundError的異常,而且對應的錯誤的類是確實存在的,這說明這個類對於類載入器來說,可能是不可見的。

怎麼解決NoClassDefFoundError錯誤

根據前文,很明顯NoClassDefFoundError的錯誤是因為在執行時類載入器在classpath下找不到需要載入的類,所以我們需要把對應的類載入到classpath中,或者檢查為什麼類在classpath中是不可用的,這個發生可能的原因如下:

  1. 對應的Class在java的classpath中不可用
  2. 你可能用jar命令執行你的程式,但類並沒有在jar檔案的manifest檔案中的classpath屬性中定義
  3. 可能程式的啟動指令碼覆蓋了原來的classpath環境變數
  4. 因為NoClassDefFoundError是java.lang.LinkageError的一個子類,所以可能由於程式依賴的原生的類庫不可用而導致
  5. 檢查日誌檔案中是否有java.lang.ExceptionInInitializerError這樣的錯誤,NoClassDefFoundError有可能是由於靜態初始化失敗導致的
  6. 如果你工作在J2EE的環境,有多個不同的類載入器,也可能導致NoClassDefFoundError

下面我們看一些當發生NoClassDefFoundError時,我們該如何解決的樣例。

NoClassDefFoundError解決示例

  • 當發生由於缺少jar檔案,或者jar檔案沒有新增到classpath,或者jar的檔名發生變更會導致java.lang.NoClassDefFoundError的錯誤。
  • 當類不在classpath中時,這種情況很難確切的知道,但如果在程式中打印出System.getproperty(“java.classpath”),可以得到程式實際執行的classpath
  • 執行時明確指定你認為程式能正常執行的 -classpath 引數,如果增加之後程式能正常執行,說明原來程式的classpath被其他人覆蓋了。
  • NoClassDefFoundError也可能由於類的靜態初始化模組錯誤導致,當你的類執行一些靜態初始化模組操作,如果初始化模組丟擲異常,哪些依賴這個類的其他類會丟擲NoClassDefFoundError的錯誤。如果你檢視程式日誌,會發現一些java.lang.ExceptionInInitializerError的錯誤日誌,ExceptionInInitializerError的錯誤會導致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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 由於NoClassDefFoundError是LinkageError的子類,而LinkageError的錯誤在依賴其他的類時會發生,所以如果你的程式依賴原生的類庫和需要的dll不存在時,有可能出現java.lang.NoClassDefFoundError。這種錯誤也可能丟擲java.lang.UnsatisfiedLinkError: no dll in java.library.path Exception Java這樣的異常。解決的辦法是把依賴的類庫和dll跟你的jar包放在一起。
  • 如果你使用Ant構建指令碼來生成jar檔案和manifest檔案,要確保Ant指令碼獲取的是正確的classpath值寫入到manifest.mf檔案
  • Jar檔案的許可權問題也可能導致NoClassDefFoundError,如果你的程式執行在像linux這樣多使用者的作業系統種,你需要把你應用相關的資原始檔,如Jar檔案,類庫檔案,配置檔案的許可權單獨分配給程式所屬使用者組,如果你使用了多個使用者不同程式共享的jar包時,很容易出現許可權問題。比如其他使用者應用所屬許可權的jar包你的程式沒有許可權訪問,會導致java.lang.NoClassDefFoundError的錯誤。
  • 基於XML配置的程式也可能導致NoClassDefFoundError的錯誤。比如大多數Java的框架像Spring,Struts使用xml配置獲取對應的bean資訊,如果你輸入了錯誤的名稱,程式可能會載入其他錯誤的類而導致NoClassDefFoundError異常。我們在使用Spring MVC框架或者Apache Struts框架,在部署War檔案或者EAR檔案時就經常會出現Exception in thread “main” java.lang.NoClassDefFoundError。
  • 在有多個ClassLoader的J2EE的環境中,很容易出現NoClassDefFoundError的錯誤。由於J2EE沒有指明標準的類載入器,使用的類載入器依賴與不同的容器像Tomcat、WebLogic,WebSphere載入J2EE的不同元件如War包或者EJB-JAR包。關於類載入器的相關知識可以參考這篇文章類載入器的工作原理

    總結來說,類載入器基於三個機制:委託、可見性和單一性,委託機制是指將載入一個類的請求交給父類載入器,如果這個父類載入器不能夠找到或者載入這個類,那麼再載入它。可見性的原理是子類的載入器可以看見所有的父類載入器載入的類,而父類載入器看不到子類載入器載入的類。單一性原理是指僅載入一個類一次,這是由委託機制確保子類載入器不會再次載入父類載入器載入過的類。現在假設一個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的異常,因為兩個不同類載入器載入的類無法進行比較。

  • 有時候會出現Exception in thread “main” java.lang.NoClassDefFoundError: com/sun/tools/javac/Main 這樣的錯誤,這個錯誤說明你的Classpath, PATH 或者 JAVA_HOME沒有安裝配置正確或者JDK的安裝不正確。這個問題的解決辦法時重新安裝你的JDK。

  • Java在執行linking操作的時候,也可能導致NoClassDefFoundError。例如在前面的指令碼中,如果在編譯完成之後,我們刪除User的編譯檔案,再執行程式,這個時候你就會直接得到NoClassDefFoundError,而錯誤的訊息只打印出User類的名稱。
java.lang.NoClassDefFoundError: testing/User
    at testing.NoClassDefFoundErrorDueToStaticInitFailure.main(NoClassDefFoundErrorDueToStaticInitFailure.java:23)
  • 1
  • 2
  • 1
  • 2

現在我們知道要怎樣去面對NoClassDefFoundError異常並解決它了。