NoClassDefFoundError和ClassNotFoundException之间的区别是什么?
NoClassDefFoundError
和ClassNotFoundException
什么区别?
是什么导致他们被抛出? 他们如何解决?
在修改现有代码以包含新的jar文件时,我经常遇到这些throwables。 我已经在客户端和服务器端为通过webstart分发的Java应用程序打了他们。
可能的原因我遇到过:
- 不包含在
build.xml
中的代码的客户端的包 - 运行时类path缺less我们正在使用的新jar子
- 版本与之前的jar有冲突
当我今天遇到这些问题时,我会采取一种“跟踪 – 错误”的方法来使事情顺利进行。 我需要更多的清晰和理解。
与Java API规范的区别如下。
对于ClassNotFoundException
:
在应用程序尝试使用以下命令通过其string名称加载类时抛出:
- 类
Class
的forName
方法。- 类
ClassLoader
的findSystemClass
方法。- 类
ClassLoader
的loadClass
方法。但没有find具有指定名称的类的定义。
对于NoClassDefFoundError
:
如果Java虚拟机或
ClassLoader
实例尝试加载类的定义(作为常规方法调用的一部分,或作为使用新expression式创build新实例的一部分),并且不能find该类的定义,则抛出该exception。search到的类定义存在于当前正在执行的类被编译时,但定义不能再被find。
所以,当源成功编译时, NoClassDefFoundError
出现,但是在运行时没有find所需的class
文件。 这可能是在分发或生成JAR文件时可能发生的情况,并不是所有需要的class
文件都包含在内。
至于ClassNotFoundException
,它似乎可能源于尝试在运行时对类进行reflection调用,但程序试图调用的类不存在。
两者的区别在于一个是Error
,另一个是Exception
。 使用NoClassDefFoundError
是一个Error
,它来自于Java虚拟机在find它期望find的类时遇到的问题。 预计在编译时工作的程序由于没有findclass
文件而无法运行,或者与编译时产生或遇到的不一样。 这是一个非常严重的错误,因为程序不能由JVM启动。
另一方面, ClassNotFoundException
是一个Exception
,所以有点期待,并且是可以恢复的。 使用reflection可能是容易出错的(因为有些预期事情可能不会如预期的那样进行。没有编译时检查来查看所有必需的类存在,所以在运行时会出现find所需类的任何问题。
当ClassLoader未find所报告的类时,将引发ClassNotFoundException。 这通常意味着该类从CLASSPATH中丢失。 也可能意味着正在讨论的类正试图从另一个加载到父类加载器的类中加载,因此子类加载器中的类是不可见的。 在更复杂的环境(例如App Server)中工作时(WebSphere对这样的类加载器问题臭名昭着),情况有时是这种情况。
人们通常倾向于将java.lang.NoClassDefFoundError
与java.lang.ClassNotFoundException
混淆,但有一个重要的区别。 例如,一个exception(一个真正的错误,因为java.lang.NoClassDefFoundError
是java.lang.NoClassDefFoundError
的一个子类)像
java.lang.NoClassDefFoundError: org/apache/activemq/ActiveMQConnectionFactory
并不意味着ActiveMQConnectionFactory类不在CLASSPATH中。 事实上,它完全相反。 这意味着ActiveMQConnectionFactory类是由ClassLoaderfind的,但是当试图加载这个类的时候,它遇到了读取类定义的错误。 当有问题的类具有静态块或使用ClassLoader未find的类的成员时,通常会发生这种情况。 所以要find罪魁祸首,查看有问题的类的源(本例中是ActiveMQConnectionFactory),并使用静态块或静态成员查找代码。 如果您没有访问源代码,那么只需使用JAD反编译即可。
在检查代码时,假设你find如下所示的一行代码,请确保CLASSPATH中的SomeClass类。
private static SomeClass foo = new SomeClass();
提示:要找出一个类属于哪个jar,可以使用网站jarFinder。 这使您可以使用通配符指定类名称,并在其数据库中search该类的类。 jarhoo允许你做同样的事情,但不再免费使用。
如果您想要在本地path中查找某个类所属的jar,可以使用像jarscan这样的实用程序( http://www.inetfeedback.com/jarscan/ )。 您只需指定您想要查找的类和根目录path,然后在其中开始在jars和zip文件中search该类。
NoClassDefFoundError基本上是一个链接错误。 它发生在你试图实例化一个对象时(静态地用“new”),并且在编译期间没有find它。
当您尝试使用不存在的类时,ClassNotFoundException更通用,并且是运行时exception。 例如,你在一个函数中有一个参数接受一个接口,而有人传入一个实现了这个接口的类,但是你不能访问这个类。 它还涵盖了dynamic类加载的情况,例如使用loadClass()或Class.forName()。
NoClassDefFoundError(NCDFE)在您的代码运行“new Y()”时发生,并且找不到Y类。
这可能只是说Y从你的类加载器中丢失了,就像其他注释所表明的那样,但是可能是Y类没有签名或签名无效,或者Y是由不同的类加载器加载的, ,或者甚至Y取决于由于上述原因而无法加载的Z.
如果发生这种情况,那么JVM将会记住加载X(NCDFE)的结果,并且每次请求Y时都会抛出一个新的NCDFE而不告诉你原因:
class a { 静态类b {} 公共静态无效的主要(string参数[]){ System.out.println(“首先尝试new b():”); 尝试{新b(); catch(Throwable t){t.printStackTrace();} System.out.println(“\ nSecond尝试new b():”); 尝试{新b(); catch(Throwable t){t.printStackTrace();} } }
保存为a.java某处
代码只是尝试实例化一个新的“b”类两次,除此之外,它没有任何错误,它什么都不做。
用javac a.java
编译代码,然后通过调用java -cp . a
运行java -cp . a
java -cp . a
– 它应该只是打印出两行文字,它应该运行正常,没有错误。
然后删除“a $ b.class”文件(或填充垃圾,或复制a.class)来模拟丢失或损坏的类。 以下是发生的事情:
首先尝试新的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()
)应该可以正常工作。
第二次尝试当然也会失败,但是正如你所看到的那样,包装的exception已经不在了,因为ClassLoader似乎记得失败的类加载器。 你只看到NCDFE,根本不知道究竟发生了什么。
所以如果你看到一个NCDFE没有根本原因,你需要看看是否可以追溯到第一次加载类来找出错误的原因。
从http://www.javaroots.com/2013/02/classnotfoundexception-vs.html :
ClassNotFoundException
:当类加载器在类path中找不到所需的类时发生。 所以,基本上你应该检查你的类path,并在类path中添加类。
NoClassDefFoundError
:这是更难debugging和find原因。 这是在编译时引发所需的类,但在运行时类被改变或删除或类的静态初始化引发exception。 这意味着被加载的类在classpath中存在,但是这个类所要求的一个类被删除或者编译器加载失败。 所以你应该看到依赖于这个类的类。
例如 :
public class Test1 { } public class Test { public static void main(String[] args) { Test1 = new Test1(); } }
现在在编译这两个类之后,如果删除了Test1.class文件并运行Test类,它将会抛出
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
:应用程序尝试通过名称加载类时抛出,但未find具有指定名称的类的定义。
NoClassDefFoundError
:如果Java虚拟机尝试加载类的定义并且没有find该类的定义,则抛出该错误。
什么原因让他们每个人和思考过程如何处理这样的错误?
他们密切相关 Java根据名称查找特定的类并且无法成功加载时抛出ClassNotFoundException
。 当Java去寻找一个被链接到一些现有的代码但是由于某种原因(例如错误的类path,错误的Java版本,错误版本的库)而找不到它的类时,会引发NoClassDefFoundError
,并且是彻底的致命的,因为它表明事情已经走了糟糕的错误。
如果你有一个C背景,那么CNFE就像dlopen()
/ dlsym()
失败,NCDFE是链接器的问题。 在第二种情况下,有关的类文件应该永远不会在您尝试使用它们的configuration中实际编译。
示例#1:
class A{ void met(){ Class.forName("com.example.Class1"); } }
如果com/example/Class1
在任何类path中都不存在,则会引发ClassNotFoundException
。
示例#2:
Class B{ void met(){ com.example.Class2 c = new com.example.Class2(); } }
如果在编译B时存在com/example/Class2
,但在执行时没有find,则会抛出NoClassDefFoundError
。
两者都是运行时exception。
当尝试通过string引用类来加载类时抛出ClassNotFoundException 。 例如,在Class.forName()中的参数是一个string,这就产生了无效的二进制名称被传递给类加载器的可能性。
遇到可能无效的二进制名称时抛出ClassNotFoundException; 例如,如果类名具有“/”字符,则必定会得到一个ClassNotFoundException。 当直接引用的类在类path中不可用时也会引发这个问题。
另一方面,引发NoClassDefFoundError
- 当类的实际物理表示 – .class文件不可用时,
- 或者该类已经被加载到不同的类加载器中(通常父类加载器已经加载了类,因此该类不能被再次加载),
- 或者如果find不兼容的类定义 – 类文件中的名称与请求的名称不匹配,
- 或者(最重要的是)如果一个依赖类不能被定位和加载。 在这种情况下,直接引用的类可能已被定位和加载,但相关的类不可用或无法加载。 这是直接引用的类可以通过Class.forName或等效方法加载的情况。 这表示连接失败。
简而言之,当类加载程序无法find或加载类定义时,通常会在加载先前不存在的类的new()语句或方法调用上引发NoClassDefFoundError(与ClassNotFoundException的基于string的加载相对) S)。
最后,当ClassLoader实现无法加载类时,抛出ClassNotFoundException的实例。 大多数自定义类加载器实现都是在扩展URLClassLoader的情况下执行的。 通常,类加载器不会在任何方法实现上显式抛出NoClassDefFoundError – 通常在HotSpot编译器的JVM中抛出此exception,而不是由类加载器本身抛出。
ClassNotFoundException与NoClassDefFoundError之间的区别
有了名字本身,我们可以很容易地从
Exception
识别出一个,而另外一个则来自Error
。
exception:程序执行期间发生exception。 程序员可以通过try catch块来处理这些exception。 我们有两种例外。 检查在编译时抛出的exception。 在运行时抛出运行时exception,这些exception通常是由于编程错误而发生的。
错误:这些都不是例外,它超出了程序员的范围。 这些错误通常由JVM抛出。
图像源
区别:
ClassNotFoundException的:
- 类加载器无法validation我们在类加载子系统的 链接阶段提到的类字节代码,我们得到
ClassNotFoundException
。 -
ClassNotFoundException
是直接从java.lang.Exception
类派生的checked Exception,您需要为其提供显式处理 - 如果在运行时使用ClassLoader.loadClass(),Class.forName()和ClassLoader.findSystemClass()提供类的名称,则会引发
ClassNotFoundException
。
NoClassDefFoundError的:
- 类加载器无法解决 类加载子系统的 链接阶段中的类的引用,我们得到
NoClassDefFoundError
。 -
NoClassDefFoundError
是从LinkageError
类派生的一个错误,它用于指示错误情况,其中某个类对某个其他类具有依赖关系,并且该类在编译后发生了不兼容的更改。 -
NoClassDefFoundError
是隐式加载类的结果,因为该类的方法调用或任何variables访问。
相似点:
-
NoClassDefFoundError
和ClassNotFoundException
都与运行时类的不可用性有关。 -
ClassNotFoundException
和NoClassDefFoundError
都与Java类path相关。
鉴于类加载器sussystem操作:
这是一篇文章,帮助我了解了这些差异: http : //docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html
如果在类加载过程中发生错误,则必须在程序中直接或间接使用正在加载的类或接口的点上引发LinkageError子类的实例。
如果Java虚拟机在validation(§5.4.1)或parsing(§5.4.3)(而不是初始化(§5.5))期间尝试加载类C,并且用于初始化加载C的类加载器抛出一个ClassNotFoundException的实例,那么Java虚拟机必须抛出一个NoClassDefFoundError的实例,其原因是ClassNotFoundException的实例。
所以一个ClassNotFoundException是NoClassDefFoundError的根本原因。
NoClassDefFoundError是链接步骤中发生的types加载错误的特例。
在实践中增加一个可能的原因:
- ClassNotFoundException:正如Cletus所说,你使用接口而inheritance的接口类不在类path中。 例如,服务提供者模式(或服务定位器 )尝试find一些不存在的类
- NoClassDefFoundError:找不到给定类的依赖关系时发现给定的类
在实践中, 错误可能会被默默抛出,例如,你提交一个计时器任务,并在计时器任务中抛出错误 ,而在大多数情况下,你的程序只捕获exception 。 然后Timer主循环结束,没有任何信息。 NoClassDefFoundError类似的错误是ExceptionInInitializerError ,当你的静态初始值设定项或静态variables的初始值设定项引发exception时。
ClassNotFoundException是一个检查exception,当我们通过使用Class.forName()或ClassLoader.findSystemClass()或ClassLoader.loadClass()方法告诉JVM通过其string名称加载一个类并且在类path中找不到类时提供该类。
大多数情况下,当您尝试运行应用程序而不更新具有所需JAR文件的类path时,会发生此exception。 例如,在执行JDBC代码连接到数据库ieMySQL时,您可能会看到此exception,但您的类path没有JAR。
当JVM尝试加载作为代码执行的一部分的特定类(作为常规方法调用的一部分或作为使用new关键字创build实例的一部分)时,会发生NoClassDefFoundError错误,并且该类不存在于类path中,而是在编译时出现,因为为了执行你的程序,你需要编译它,如果你正在尝试使用一个不存在的类,编译器会引发编译错误。
以下是简要说明
你可以阅读一切关于ClassNotFoundException Vs NoClassDefFoundError的更多细节。
当我需要刷新时,我会一再提醒自己
ClassNotFoundException的
类层次结构
ClassNotFoundException extends ReflectiveOperationException extends Exception extends Throwable
在debugging时
- 必需的jar,类从类path中丢失。
- validation所有需要的jar子是否在jvm的classpath中。
的NoClassDefFoundError
类层次结构
NoClassDefFoundError extends LinkageError extends Error extends Throwable
在debugging时
- dynamic加载类的问题,已正确编译
- 问题与静态块,构造函数,依赖类的init()方法和实际的错误是由多层包装[特别是当你使用春季,hibernate的实际例外包装,你会得到NoClassDefError]
- 如果在依赖类的静态块下遇到“ClassNotFoundException”
- 问题与版本的类。 这发生在两个版本v1,v2在不同的jar / packages下的同一个类的版本,使用v1和v2在没有相关的方法/variables的运行时加载的情况下编译成功,你会看到这个exception。 [我曾经通过删除类path中出现的多个jar下的log4j相关类的重复来解决此问题]
在运行时找不到特定的类时会发生ClassNotFoundException和NoClassDefFoundError。但是,它们出现在不同的场景中。
ClassNotFoundException是在运行时使用Class.forName()或loadClass()方法尝试加载类时发生的exception,并且在类path中找不到类。
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运行时系统将像下面那样引发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)