什么原因导致NoClassDefFoundError和ClassNotFoundException之间的区别?


371

NoClassDefFoundError和之间有什么区别ClassNotFoundException

是什么导致它们被抛出?如何解决?

在修改现有代码以包括新的jar文件时,我经常遇到这些throwables。我在客户端和服务器端都通过Webstart分发了一个Java应用程序。

我遇到的可能原因:

  1. build.xml代码客户端未包含的软件包
  2. 我们正在使用的新jar缺少运行时类路径
  3. 版本与先前的jar冲突

今天,当我遇到这些问题时,我采取了走错一步的方法来使事情顺利进行。我需要更多的清晰度和理解。


我经常发现在-verbose(例如-verbose:class -verbose:jni)帮助下运行JVM –但是mogsie在他们的回答下报告说,这没有提供额外的有用信息:(
PJTraill

Answers:


388

与Java API规范的区别如下。

对于ClassNotFoundException

当应用程序尝试使用其字符串名称通过其字符串名称加载类时抛出:

  • forName课堂教学中的方法Class
  • findSystemClass课堂教学中的方法ClassLoader
  • loadClass课堂教学中的方法ClassLoader

但是找不到具有指定名称的类的定义。

对于NoClassDefFoundError

如果Java虚拟机或ClassLoader实例尝试加载类的定义(作为常规方法调用的一部分或使用新表达式创建新实例的一部分)而抛出,则找不到该类的定义。

当前编译的类在编译时就存在搜索到的类定义,但不再可以找到该定义。

因此,似乎NoClassDefFoundError在成功编译源代码时发生了,但是在运行时class找不到所需的文件。这可能是在分发或生成JAR文件时发生的,其中未class包含所有必需的文件。

至于ClassNotFoundException,似乎是由于试图在运行时对类进行反射性调用而引起的,但程序尝试调用的类不存在。

两者之间的区别在于,一个是an Error,另一个是an Exception。with NoClassDefFoundErrorError,它源自Java虚拟机在查找预期要找到的类时遇到问题。由于class找不到文件,或者与编译时生成或遇到的文件不同,预期无法在编译时运行的程序无法运行。这是一个非常严重的错误,因为该程序无法由JVM启动。

另一方面,ClassNotFoundExceptionException,因此在某种程度上是可以预期的,并且可以恢复。使用反射可能容易出错(因为有些期望可能不会按预期进行。没有进行编译时检查以确保所有必需的类都存在,因此查找所需类的任何问题都会在运行时出现。


53
NoClassDefFoundError通常在类的静态块或静态字段初始化出现问题(引发异常)时发生,因此无法成功初始化类。
大港

7
投票。一个是一个Error,另一个是一个Exception。:)
拉维

83

当ClassLoader未找到报告的类时,将引发ClassNotFoundException。这通常意味着CLASSPATH中缺少该类。这也可能意味着该类正试图从另一个已加载到父类加载器中的类中加载,因此子类加载器中的类不可见。在更复杂的环境(例如App Server)中工作时,有时就是这种情况(WebSphere臭名昭著的类加载器问题)。

人们常常会混淆java.lang.NoClassDefFoundErrorjava.lang.ClassNotFoundException但是有一个重要的区别。例如一个异常(实际上java.lang.NoClassDefFoundError是一个错误,因为它是java.lang.Error的子类)

java.lang.NoClassDefFoundError:
org/apache/activemq/ActiveMQConnectionFactory

这并不意味着ActiveMQConnectionFactory类不在CLASSPATH中。实际上是相反的。这意味着ClassLoader找到了ActiveMQConnectionFactory类,但是在尝试加载该类时,在读取类定义时遇到了错误。当所涉及的类具有使用ClassLoader找不到的类的静态块或成员时,通常会发生这种情况。因此,要找到罪魁祸首,请查看相关类的源(在本例中为ActiveMQConnectionFactory),并使用静态块或静态成员查找代码。如果您无权访问源,则只需使用JAD对其进行反编译。

在检查代码时,假设您发现如下所示的代码行,请确保CLASSPATH中的类SomeClass。

private static SomeClass foo = new SomeClass();

提示:要找出类所属的jar,可以使用网站jarFinder。这使您可以使用通配符指定类名称,并在其jar数据库中搜索该类。jarhoo允许您执行相同的操作,但不再免费使用。

如果要在本地路径中找到类所属的jar,可以使用jarscan(http://www.inetfeedback.com/jarscan/)之类的实用程序。您只需指定要查找的类以及您希望它开始在jar和zip文件中搜索该类的根目录路径。


9
我很奇怪,这是最后投票完全正确的答案。(在我投票之前甚至为-1)。ClassNotFoundException表示CL无法看到.class文件。NoClassDefFoundError表示.class文件在该文件中无法加载(可能是JNI错误)。
2011年

1
这个答案与coobird的答案不矛盾吗?
zardosht

我尝试了类似的静态块示例。我的课程Class1具有静态变量“ private static B foo = new B();” 编译后,我从bin文件夹中删除了B.class文件。现在,当我创建Class1的对象时,从第三类的Main方法开始。该错误被抛出如下:------“线程“主”中的异常java.lang.NoClassDefFoundError:spring / B” ........因此,它确切地提到了未找到的类ie类是在静态块中引用的,而不是在外部类中引用的。因此与该答案相反。
Kaushik Lele 2012年

+1的澄清是“不表示ActiveMQConnectionFactory类不在CLASSPATH中”
akila,

35

NoClassDefFoundError基本上是链接错误。当您尝试实例化一个对象时会发生这种情况(静态地使用“ new”),而在编译过程中则找不到该对象。

ClassNotFoundException当您尝试使用不存在的类时,它更为通用,并且是运行时异常。例如,您在函数中具有接受接口的参数,并且有人传入实现该接口的类,但您无权访问该类。它还介绍了动态类加载的情况,例如使用loadClass()Class.forName()


29

当您的代码运行“ new Y()”并且找不到Y类时,会发生NoClassDefFoundError(NCDFE)。

可能只是像其他注释所建议的那样,您的类加载器中缺少Y,但也可能是Y类未签名或具有无效的签名,或者Y是由您的代码不可见的其他类加载器加载的,或者甚至Y取决于Z,由于上述任何原因,Z均无法加载。

如果发生这种情况,那么JVM会记住加载X(NCDFE)的结果,并且每次您请求Y时它都会简单地抛出一个新的NCDFE,而不会告诉您原因:

A类{
  静态类b {}
  公共静态void main(String args []){
    System.out.println(“首次尝试新b():”);
    尝试{new b(); } catch(Throwable t){t.printStackTrace();}
    System.out.println(“ \ n第二次尝试new b():”);
    尝试{new b(); } catch(Throwable t){t.printStackTrace();}
  }
}

将其另存为a.java

该代码只是尝试两次实例化一个新的“ b”类,除此之外,它没有任何错误,并且没有任何作用。

使用编译代码javac a.java,然后通过调用运行a java -cp . a-它应该只打印出两行文本,并且可以正常运行而不会出错。

然后删除“ a $ b.class”文件(或将其填充为垃圾,或在其上复制a.class)以模拟丢失或损坏的类。这是发生了什么:

第一次尝试new b():
java.lang.NoClassDefFoundError:a $ b
    在a.main(a.java:5)
引起原因:java.lang.ClassNotFoundException:a $ b
    在java.net.URLClassLoader $ 1.run(URLClassLoader.java:200)
    在java.security.AccessController.doPrivileged(本机方法)
    在java.net.URLClassLoader.findClass(URLClassLoader.java:188)
    在java.lang.ClassLoader.loadClass(ClassLoader.java:307)
    在sun.misc.Launcher $ AppClassLoader.loadClass(Launcher.java:301)
    在java.lang.ClassLoader.loadClass(ClassLoader.java:252)
    在java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
    ...还有1个

第二次尝试new b():
java.lang.NoClassDefFoundError:a $ b
    在a.main(a.java:7)

第一次调用会导致ClassNotFoundException(当无法找到类时由类加载器抛出),该类必须包装在未经检查的NoClassDefFoundError中,因为所讨论的代码(new b())应该可以正常工作。

当然,第二次尝试也会失败,但是您可以看到包装的异常不再存在,因为ClassLoader似乎记得失败的类加载器。您只会看到NCDFE,而完全不知道真正发生了什么。

因此,如果您看到无根本原因的NCDFE,则需要查看是否可以追溯到第一次加载类时才查找错误原因。


使用JVM -verbose或类似的选项(取决于特定的JVM)运行JVM怎么样?也许是-verbose:class-verbose:class:jni如果使用JNI,但是我不确定语法。如果有用的话,也许您可​​以显示结果。
PJTraill '16

既不-verbose:class也不-verbose:jni提供与缺少的类相关的任何其他输出。
mogsie

1
感谢您尝试一下,即使结果令人失望。(自那时以来,我发现PS -verbose:class:jni是错误的:必须指定两个单独的选项:-verbose:class -verbose:jni。)
PJTraill 2016年

2
最后一句* 1,000,000:因此,如果您看到没有根本原因的NCDFE,则需要查看是否可以追溯到第一次加载类时才查找错误原因。
batwad

20

来自http://www.javaroots.com/2013/02/classnotfoundexception-vs.html

ClassNotFoundException:当类加载器在类路径中找不到所需的类时发生。因此,基本上,您应该检查您的类路径并将该类添加到类路径中。

NoClassDefFoundError:这更难以调试和查找原因。当在编译时存在所需的类时抛出此错误,但是在运行时更改或删除了这些类,或者类的静态初始化引发了异常。这意味着要加载的类存在于类路径中,但是该类所需的一个类已被编译器删除或加载失败。因此,您应该看到依赖于此类的类。

范例

public class Test1
{
}


public class Test 
{
   public static void main(String[] args)
   {
        Test1 = new Test1();    
   }

}

现在,在编译完两个类之后,如果删除Test1.class文件并运行Test class,它将抛出

Exception in thread "main" java.lang.NoClassDefFoundError: Test
    at Test1.main(Test1.java:5)
Caused by: java.lang.ClassNotFoundException: Test
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    ... 1 more

ClassNotFoundException:当应用程序尝试通过名称加载类时抛出,但找不到具有指定名称的类的定义。

NoClassDefFoundError:如果Java虚拟机尝试加载类的定义并且找不到该类的定义,则抛出该错误。


使用JVM -verbose或类似的选项(取决于特定的JVM)运行JVM怎么样?也许是-verbose:class-verbose:class:jni如果使用JNI,但是我不确定语法。
PJTraill '16

-verbose:class:jni是错误的,但是您可以传递两个单独的选项:-verbose:class -verbose:jni
PJTraill '16

15

得到每个人的原因是什么,以及有关如何处理此类错误的任何思考过程是什么?

它们密切相关。ClassNotFoundException当Java通过名称查找特定类并且无法成功加载它时,抛出A。NoClassDefFoundError当Java寻找链接到某些现有代码的类但由于某种原因(例如,错误的类路径,错误的Java版本,错误的库版本)而找不到它的类时,抛出A。因为它表明出现了严重错误。

如果您有C背景,那么CNFE就像是dlopen()/ 的失败,dlsym()而NCDFE就是链接器的问题;在第二种情况下,有关的类文件绝不应该在您尝试使用它们的配置中实际编译过。


11

范例1:

class A{
 void met(){
   Class.forName("com.example.Class1");
 }
}

如果com/example/Class1在任何类路径中都不存在,则抛出ClassNotFoundException

范例2:

Class B{
  void met(){
   com.example.Class2 c = new com.example.Class2();
 }
}

如果com/example/Class2在编译B时存在,但在执行时未找到,则抛出NoClassDefFoundError

两者都是运行时异常。


9

尝试通过String引用加载类时,抛出ClassNotFoundException。例如,Class.forName()中的参数to是一个String,这增加了将无效二进制名称传递给类加载器的可能性。

当遇到可能无效的二进制名称时,将引发ClassNotFoundException;例如,如果类名带有'/'字符,则势必会收到ClassNotFoundException。当直接引用的类在类路径上不可用时,也会引发该错误。

另一方面,抛出NoClassDefFoundError

  • 当该类的实际物理表示形式-.class文件不可用时,
  • 或已将类加载到其他类加载器中(通常,父类加载器会加载该类,因此无法再次加载该类),
  • 或如果找到了不兼容的类定义-类文件中的名称与请求的名称不匹配,
  • 或(最重要的)是否无法找到和加载相关类。在这种情况下,可能已经找到并加载了直接引用的类,但是从属类不可用或无法加载。在这种情况下,可以通过Class.forName或等效方法加载直接引用的类。这表明链接失败。

简而言之,当类加载器无法找到或加载类定义时,通常会在加载以前不存在的类的new()语句或方法调用上引发NoClassDefFoundError(与ClassNotFoundException的基于字符串的类加载相反) s)。

最终,当无法加载类时,由ClassLoader实现抛出ClassNotFoundException实例。大多数自定义类加载器实现都执行此操作,因为它们扩展了URLClassLoader。通常,类加载器不会在任何方法实现上显式抛出NoClassDefFoundError-通常从HotSpot编译器中的JVM抛出此异常,而不是由类加载器本身抛出。


赞成提及“类文件中的名称与所请求的名称不匹配”。这是一个很常见的原因。
罗恩侯爵,

8

ClassNotFoundException与NoClassDefFoundError之间的区别

在此处输入图片说明


不清晰。“在类路径中未更新”是模糊/不精确的。这是关于在类路径中不存在 JAR ,或者在类路径中存在错误的JAR 版本。和拼写错误。并且(叹气)由于您将信息发布为时髦的图形,因此我们无法修复此问题。
斯蒂芬C,

8

使用名称本身,我们可以轻松地识别一个来自Exception和另一个来自Error

异常:程序执行期间发生异常。程序员可以通过try catch块来处理这些异常。我们有两种类型的例外。在编译时抛出的已检查异常。运行时引发的运行时异常,这些异常通常是由于编程错误而发生的。

错误:这些根本不是例外,这超出了程序员的范围。这些错误通常由JVM引发。


在此处输入图片说明 图片来源

区别:

ClassNotFoundException:

  • 类加载器无法验证我们提到一个类字节代码链接阶段类装载子系统,我们得到的ClassNotFoundException
  • ClassNotFoundException是直接从java.lang.Exception类派生的已检查异常,您需要为其提供显式处理
  • ClassNotFoundException当通过在运行时使用ClassLoader.loadClass(),Class.forName()和ClassLoader.findSystemClass()提供类名来显式加载类时,会出现此错误。

NoClassDefFoundError:

  • 在我们获得的类加载子系统的Link阶段中类加载器无法解析类的引用。NoClassDefFoundError
  • NoClassDefFoundError是从LinkageError类派生的Error ,用于指示错误情况,其中某个类对某个其他类具有依赖项,并且该类在编译后发生了不兼容的更改。
  • NoClassDefFoundError是由于该类的方法调用或任何变量访问而隐式加载类的结果。

相似之处:

  • 两者NoClassDefFoundErrorClassNotFoundException都与运行时类的不可用性有关。
  • 两者ClassNotFoundExceptionNoClassDefFoundError都与Java类路径有关。

3

给定Class loader sussystem操作:

http://www.artima.com/insidejvm/ed2/images/fig7-1.gif

这篇文章对我的理解有所帮助:http : //docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html

如果在类加载期间发生错误,则必须在程序中直接或间接使用要加载的类或接口的位置上抛出LinkageError子类的实例。

如果Java虚拟机曾经在验证(第5.4.1节)或解析(第5.4.3节)(但未初始化(第5.5节))期间尝试加载类C,则尝试启动用于加载C的类加载器抛出ClassNotFoundException的实例,那么Java虚拟机必须抛出NoClassDefFoundError的实例,其原因是ClassNotFoundException的实例。

因此,ClassNotFoundExceptionNoClassDefFoundError的根本原因。
而一个NoClassDefFoundError的是类加载错误的特殊情况下,发生在链接的一步。


2

在实践中添加一个可能的原因:

  • ClassNotFoundException:正如cletus所说,您可以使用interface,而interface的继承类不在classpath中。例如,服务提供者模式(或服务定位器)尝试找到一些不存在的类
  • NoClassDefFoundError:找到给定的类而找不到给定类的依赖项

实际上,可能会默默地抛出Error,例如,您提交了一个计时器任务,并且在计时器任务中它抛出Error,而在大多数情况下,您的程序仅捕获Exception。然后,Timer主循环结束,没有任何信息。当您的静态初始值设定项或静态变量的初始值设定项引发异常时,与NoClassDefFoundError相似的错误是ExceptionInInitializerError


1

ClassNotFoundException是一个经过检查的异常,当我们告诉JVM使用Class.forName()或ClassLoader.findSystemClass()或ClassLoader.loadClass()方法通过其字符串名称加载类并且在类路径中找不到所提到的类时,将发生ClassNotFoundException

大多数情况下,当您尝试运行应用程序而不用必需的JAR文件更新类路径时,就会发生此异常。例如,当您执行JDBC代码连接到数据库即MySQL时,您可能已经看到此异常,但是您的类路径没有JAR。

当JVM尝试加载代码执行过程中的特定类(作为常规方法调用的一部分或使用new关键字创建实例的一部分)并且该类不存在于您的类路径中时,就会发生NoClassDefFoundError错误。出现在编译时,因为要执行您的程序,您需要对其进行编译,并且如果尝试使用不存在的类,则编译器将引发编译错误。

以下是简要说明

在此处输入图片说明

您可以阅读有关ClassNotFoundException的所有内容和NoClassDefFoundError的更多信息。


0

当我需要刷新时,我会一次又一次提醒自己

ClassNotFoundException

类层次结构

ClassNotFoundException extends ReflectiveOperationException extends Exception extends Throwable

调试时

  1. 必需的jar,classpath中缺少类。
  2. 验证所有必需的jar都在jvm的classpath中。

NoClassDefFoundError

类层次结构

NoClassDefFoundError extends LinkageError  extends Error extends Throwable

调试时

  1. 动态加载类(已正确编译)的问题
  2. 静态块,构造函数,依赖类的init()方法出现问题,并且实际错误由多层包装[特别是当您使用spring时,休眠实际包装的异常,您将得到NoClassDefError]
  3. 当您在依赖类的静态块下遇到“ ClassNotFoundException”时
  4. 类的版本有问题。当您在不同的jar /程序包下具有相同类的两个版本v1,v2,并且使用v1成功编译了两个版本,并且在没有相关方法/ vars的运行时加载了v2时,会发生此异常。[我曾经通过删除类路径中出现的多个jar下与log4j相关的类的重复项来解决此问题]

-1

当在运行时找不到特定的类时,会发生ClassNotFoundException和NoClassDefFoundError,但是它们发生在不同的情况下。

ClassNotFoundException是一种异常,当您尝试在运行时使用Class.forName()或loadClass()方法加载类并且在类路径中找不到所提到的类时,会发生此异常。

    public class MainClass
    {
        public static void main(String[] args)
        {
            try
            {
                Class.forName("oracle.jdbc.driver.OracleDriver");
            }catch (ClassNotFoundException e)
            {
                e.printStackTrace();
            }
        }
    }



    java.lang.ClassNotFoundException: oracle.jdbc.driver.OracleDriver
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Unknown Source)
    at pack1.MainClass.main(MainClass.java:17)

NoClassDefFoundError是当特定类在编译时存在但在运行时丢失时发生的错误。

    class A
    {
      // some code
    }
    public class B
    {
        public static void main(String[] args)
        {
            A a = new A();
        }
    }

当您编译上述程序时,将生成两个.class文件。一个是A.class,另一个是B.class。如果删除A.class文件并运行B.class文件,则Java Runtime System将抛出NoClassDefFoundError,如下所示:

    Exception in thread "main" java.lang.NoClassDefFoundError: A
    at MainClass.main(MainClass.java:10)
    Caused by: java.lang.ClassNotFoundException: A
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.