ClassNotFoundException 与 NoClassDefFoundError

Posted

技术标签:

【中文标题】ClassNotFoundException 与 NoClassDefFoundError【英文标题】:ClassNotFoundException vs NoClassDefFoundError 【发布时间】:2015-04-04 01:27:36 【问题描述】:

我已经通过这个线程What causes and what are the differences between NoClassDefFoundError and ClassNotFoundException? 这就是线程中具有最大 ups 的 ans 之一是: NoClassDefFoundError :"所以,看来NoClassDefFoundError发生在源码编译成功时,但在运行时找不到所需的类文件。这可能是可以发生在 JAR 文件的分发或生产中,其中并未包含所有必需的类文件。”

ClassNotFoundException:至于ClassNotFoundException,看来它可能源于试图在运行时对类进行反射调用,但程序试图调用的类并不存在。

我做了一个小实验。我创建了一个主类 class A 并尝试从中调用其他类 class B ,编译成功。

然后我删除了在 A 类中被调用的 B 类。 我得到了 java.lang.ClassNotFoundException 但根据步骤中的答案,我应该得到 NoClassDefFoundError (源编译成功,但在运行时找不到类文件) 谁能解释我在解释线程中的 ans 时遗漏了什么?

package com.random;

public class A 

    public static void main(String[] args) 
        B b= new B();

    



 package com.random;

public class B 




【问题讨论】:

有趣,因为另一个站点做了同样的测试,得到了你想要的结果:javaroots.com/2013/02/classnotfoundexception-vs.html 我很想知道如果您将 B 类放在不同的包中然后必须在 A 类中包含 import com.random.blah.B; 会发生什么。 有趣。删除 B.class 后,我得到了这个: 线程“main”中的异常 java.lang.NoClassDefFoundError: com/random/B at com.random.A.main(A.java:6) Caused by: java.lang.ClassNotFoundException: com.random.B 【参考方案1】:

NoClassDefFoundError

如果 Java 虚拟机或 ClassLoader 实例尝试 加载类的定义(作为正常方法调用的一部分或 作为使用 new 表达式创建新实例的一部分)并且没有 可以找到类的定义。

搜索到的类定义在当前执行时存在 类已编译,但无法再找到定义。


ClassNotFoundException

当应用程序尝试通过其字符串加载类时抛出 名称使用:Class 类中的 forName 方法。查找系统类 ClassLoader 类中的方法。类中的loadClass方法 类加载器。


你要明白JVM无法实现你删除的class的定义找不到,因为class本身找不到会自动抛出ClassNotFoundException

这个异常发生在runtime,所以不管它是否先编译,你删除了文件,因此找不到它并抛出exception

请注意,NoClassDefFoundError 实际上并不是一个例外,它是从LinkageError 派生的Error,而ClassNotFoundException 直接从java.lang.Exception 派生。

要恢复,NoClassDefFoundError 全局只是意味着JVM 试图访问runtime 的东西,根据compiled 代码应该存在,但实际上不存在(或不在类路径中) .


重现 ClassNotFoundException 的示例

public class ClassNotFoundExceptionExample 

    private static final String CLASS_TO_LOAD = "main.java.Utils";

    public static void main(String[] args) 
        try 
            Class loadedClass = Class.forName(CLASS_TO_LOAD);
            System.out.println("Class " + loadedClass + " found successfully!");
        
        catch (ClassNotFoundException ex) 
            System.err.println("A ClassNotFoundException was caught: " + ex.getMessage());
            ex.printStackTrace();
        
    


重现 NoClassDefFoundError 的示例

创建一个简单的类Test

public class Test 
        public Test() 
                System.out.println("A new instance of the Test class was created!");
        

还有一个班级NoClassDefFoundErrorExample

public class NoClassDefFoundErrorExample 
        private static Test test = new Test();

        public static void main(String[] args) 
                System.out.println("The definition of Test was found!");
        

现在创建一个可执行的.jar 来执行main 方法。您可以在.jar 内的Manifest.txt 文件中指定它

Main-Class: NoClassDefFoundErrorExample

现在运行以下命令

javac Test.java
javac NoClassDefFoundErrorExample.java
jar cfm NoClassDefFoundErrorExample.jar Manifest.txt NoClassDefFoundErrorExample.class
java -jar NoClassDefFoundErrorExample.jar

注意NoClassDefFoundError

Exception in thread "main" java.lang.NoClassDefFoundError: TestClass
    at NoClassDefFoundErrorExample.(NoClassDefFoundErrorExample.java:2)
Caused by: java.lang.ClassNotFoundException: TestClass
    at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 1 more

【讨论】:

堆栈跟踪错误地抱怨TestClass,而它应该提到Test【参考方案2】:

嗯...ClassNotFoundException 发生在运行时试图查找由某些String 命名的类时,例如Class.forName(java.lang.String) 方法采用字符串参数并尝试查找具有该名称的类。在这种情况下,类名是一个字符串,只能在运行时检查。这里的异常清楚地表明......没有找到这个“类”。所以...这可能有两个原因:

原因 1. 类名不是有效的 java 类(例如 - “java.bang.kiting”)。

// Example    
Class cdef = Class.forName( "java.bang.kiting" );

原因 2. 类名是一个有效的类......但不知何故,它没有与 jar 打包在一起,或者没有在类路径中解析。所以就运行时所知......它可能是一个错误的类名......类似于案例 1。

// Example    
Class cdef =Class.forName( "apache.some.SomeLegitClass" );

NoClassDefFoundError 用于使用实际类引用的情况,

// example
import apache.some.SomeLegitClass
SomeLegitClass i = (SomeLegitClass) instanceOfSomeLegitClass;

所以基本上一切都是正确的,但不知何故,该类没有与 jar 一起打包(或更一般地说 - 没有在类路径中解析)。在这种情况下,我们得到NoClassDefFoundError

这里运行时知道该类是有效的,因为它编译成功...但是它找不到“类定义”。

【讨论】:

"这里runtime知道类是有效的,因为编译成功了……但是找不到“类定义”——这一行就是这个ans的精髓【参考方案3】:

区别取决于谁要求加载类

ClassNotFoundException 在代码直接尝试加载类时抛出,传递代表类的完全限定名称的String 参数。 例如Class.forName(String),或ClassLoader.loadClass(String)NoClassDefFoundError 在要求 JVM 间接加载类时抛出。 例如当 A 类使用 B 类且 B 类不在类路径上时,将抛出 NoClassDefFoundError

【讨论】:

【参考方案4】:

NoClassDefFoundError 通常在您使用库时调用(例如,Guava、Gson、CommonsIO)。您将库放在项目的类路径中,但您没有将它一起导出,当应用程序运行时您会得到一个NoClassDefFoundError

如何获得NoClassDefFoundError: 使用此类创建一个新项目。

public class A

    public void do()
    
        System.out.println("Do!");
    
  

将其导出为.jarfile。

现在创建另一个项目。将导出的 jar 文件添加到类路径。

import ???.A;
public class Main

    public static void main(String[] args)
    
        A a = new A();
        a.do();//NoClassDefFoundError thrown at here.
    
 

导出项目,确保不包含 jar 文件(类 A)。运行新导出的jar文件,你会看到那个错误!

【讨论】:

【参考方案5】:

Everything About ClassNotFoundException Vs NoClassDefFoundError 文章通过示例和根据它非常清楚地解释了 ClassNotFoundException Vs NoClassDefFoundError 之间的区别。

ClassNotFoundException

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

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

public class Test 
    public static void main(String[] args) throws Exception 

        // Provide any class name to Class.forName() which does not exist
        // Or compile Test.java and then manually delete Person.class file so Person class will become unavailable
        // Run the program using java Test

        Class clazz = Class.forName("Person");
        Person person = (Person) clazz.newInstance();
        person.saySomething();
    


class Person 
    void saySomething() 
        System.out.println("Hello");
    

NoClassDefFoundError

是 java.lang.Error 的子类型,Error 类表示应用程序确实不应该发生的异常行为,但应用程序开发人员不应该尝试捕捉它,它仅供 JVM 使用。

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

public class Test 
    public static void main(String[] args) throws Exception 

        // Do javac on Test.java, 
        // Program will compile successfully because Empoyee class exits
        // Manually delete Employee.class file
        // Run the program using java Test
        Employee emp = new Employee();
        emp.saySomething();

    


class Employee 
    void saySomething() 
        System.out.println("Hello");
    

【讨论】:

【参考方案6】:

1) ClassNotFoundException

    当我们尝试在运行时使用 Class.forName()ClassLoader.loadClass()ClassLoader.findSystemClass() 方法加载一个类时,它可能找不到 中找到所需的类类路径。 在这种情况下,我们应该检查class path,如果缺少该类,则将其添加到类路径中。 这是一个checked Exception,它派生自java.lang.Exception类。 这属于显式加载。

2) NoClassDefFoundError

    当类在 compile time 期间存在并且由于某些原因在 run time 期间不可用时,会发生这种情况。这意味着正在加载的类是 classpath 中的 present ,但是此类所需的依赖 classe(s) 之一是 removedfailed由编译器加载。

    在这种情况下,我们只需要检查classes which are dependent on this class即可。

    这是一个错误,派生自java.lang.LinkageError。 这是在隐式加载下进行的。

【讨论】:

它总是令人困惑。在给定的例子中,B类被A类间接引用。A类依赖于B类,所以它应该抛出NoClassDefFoundError。【参考方案7】:

如之前的答案中所述,当类在编译期间存在并且由于某些原因在运行期间不可用时,将发生 NoClassDefFoundError。

我想添加另一个场景,它也可能导致 NoClassDefFoundError。

当您尝试加载由于静态初始化块中的某些异常而无法加载的类时,系统将抛出 ExceptionInInitializerError。如果您再次尝试加载相同的类(之前加载失败),系统将抛出 NoClassDefFoundError

让我们用一个示例来探索它

ClassWithStaticBlock.java

public class ClassWithStaticBlock 

    static 
       int total = 1/0;
    

Main.java

public class Main 

public static void main(String[] args) 
    ClassWithStaticBlock cs;
    try 
       cs = new ClassWithStaticBlock();
    catch(Throwable e)
        e.printStackTrace();
    
  

结果:

java.lang.ExceptionInInitializerError
    at Main.main(Main.java:6)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: java.lang.ArithmeticException: / by zero
    at ClassWithStaticBlock.<clinit>(ClassWithStaticBlock.java:7)
    ... 6 more

让我们修改Main.java

public class Main 

    public static void main(String[] args) 
        ClassWithStaticBlock cs;
        try 
           cs = new ClassWithStaticBlock();
        catch(Throwable e)
            e.printStackTrace();
        
        cs = new ClassWithStaticBlock(); //try to use ClassWithStaticBlock again
    

结果:

java.lang.ExceptionInInitializerError
    at Main.main(Main.java:6)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: java.lang.ArithmeticException: / by zero
    at ClassWithStaticBlock.<clinit>(ClassWithStaticBlock.java:7)
    ... 6 more
Exception in thread "Main Thread" java.lang.NoClassDefFoundError: ClassWithStaticBlock
    at Main.main(Main.java:10)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

当我们再次尝试使用 ClassWithStaticBlock(之前初始化失败)时,System 正在抛出 NoClassDefFoundError。

从Why am I getting a NoClassDefFoundError in Java?找到样本

【讨论】:

【参考方案8】:

NoClassDefFoundError 发生的一种情况是在类路径中找不到类 JVM 尝试访问的情况。 但是如果类路径中存在类,则会导致 ClassNotFoundException。

简而言之,如果类在编译期间存在但在运行期间在 java 类路径中不可用,则会出现 NoClassDefFoundError。

尝试在类路径不包含 B 类的情况下使用显式 -classpath 选项运行。

【讨论】:

如果类存在于类路径中,为什么你会得到ClassNotFoundException【参考方案9】:

此线程中的其他答案是正确的,我只是想添加一些我花了几个小时试图弄清楚的东西。即使

Class.forName("apache.some.SomeLegitClass")

有效,

Class.forName("apache.some.somelegitclass")

将导致 NoClassDefFoundError。 Class.forName() 区分大小写。如果类名拼写错误,或者只是大小写错误,则会导致不同的异常。

【讨论】:

以上是关于ClassNotFoundException 与 NoClassDefFoundError的主要内容,如果未能解决你的问题,请参考以下文章

Java理解ClassNotFoundException与NoClassDefFoundError的区别

pyspark 与 MariaDB 的连接失败并出现 ClassNotFoundException

ClassNotFoundException: com.auth0.jwt.exceptions.JWTCreationException 与 DocuSignApi Java SDK

将 SafeArgs 与 Proguard 和导航架构组件一起使用时出现 ClassNotFoundException?

java.lang.ClassNotFoundException与java.lang.NoClassDefFoundError的区别

如何将简单的 Servlet 与 JDBC 结合起来?代码导致 ClassNotFoundException