ClassNotFoundException与NoClassDefFoundError的区别

35

我看了这个帖子:什么导致NoClassDefFoundError和ClassNotFoundException之间的差异? 其中一位回答获得了最多的赞,内容如下:

NoClassDefFoundError:"因此,看起来当源代码编译成功但在运行时找不到所需的类文件时,就会发生NoClassDefFoundError。这可能是在分发或制作JAR文件时可能发生的情况,其中未包含所有必需的类文件。"

ClassNotFoundException:至于ClassNotFoundException,似乎来源于尝试在运行时对类进行反射调用,但程序尝试调用的类不存在。

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

然后我删除了被类A调用的类B。我收到了java.lang.ClassNotFoundException的错误提示,但根据帖子中的答案,我应该收到NoClassDefFoundError(源代码已经成功编译,但在运行时找不到类文件)。请问有人能解释一下我的解读中漏掉了什么吗?

package com.random;

public class A {

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

    }

}

 package com.random;

public class B {



}

有趣的是,另一个网站进行了相同的测试,并得到了您要寻找的结果:http://www.javaroots.com/2013/02/classnotfoundexception-vs.html - Ascalonian
1
我很好奇如果你把B类放在不同的包中,然后在A类中必须包含import com.random.blah.B;,会发生什么。 - Chris Fei
有趣。在删除B.class之后,我得到了以下结果:Exception in thread "main" java.lang.NoClassDefFoundError: com/random/B at com.random.A.main(A.java:6)原因是:java.lang.ClassNotFoundException: com.random.B - victorantunes
9个回答

26

NoClassDefFoundError

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

当当前执行的类编译时,正在搜索的类定义存在,但是现在无法找到该定义。


ClassNotFoundException

当应用程序尝试通过其字符串名称加载类时引发:class Class中的forName方法。 class ClassLoader中的findSystemClass方法。 class ClassLoader中的loadClass方法。


您必须了解,JVM无法意识到您删除的类的定义无法找到,因为自身找不到该类,这会自动引发ClassNotFoundException异常。

此异常发生在运行时,因此编译是否成功都无关紧要,由于您删除了文件,因此找不到并引发异常。

请注意,NoClassDefFoundError实际上不是异常,它是从LinkageError派生的错误,而ClassNotFoundException直接从java.lang.Exception派生。

总之,NoClassDefFoundError全局简单地意味着JVM在运行时尝试访问根据编译的代码应该存在但实际上不存在(或不在类路径中)的内容。


复现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

2
堆栈跟踪错误地抱怨了 TestClass,而应该提到 Test - ᴠɪɴᴄᴇɴᴛ

13

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

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

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

原因2:Class名称是一个有效的类名... 但某种方式它没有打包到JAR中,或者在类路径中没有解析。因此就运行时而言,它可能是一个错误的类名... 类似于情况1。

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

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

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

基本上,一切都是正确的,但是某种方式类没有和jar打包在一起(或者更普遍地说- 没有在类路径中解析)。 在这种情况下,我们会收到 NoClassDefFoundError 错误。

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


2
在这里,运行时知道该类是有效的,因为它已经成功编译了... 但它找不到“类定义”- 这一行是这个答案的精髓。 - Deen John

13

区别取决于请求加载类的对象:

  • ClassNotFoundException 在尝试直接加载类时抛出异常,传递一个表示类全限定名的String参数。
    • 例如:Class.forName(String)ClassLoader.loadClass(String)
  • NoClassDefFoundError在间接请求JVM加载类时抛出异常
    • 例如:当类A使用类B且类B不在类路径上时,将引发NoClassDefFoundError异常。

2

ClassNotFoundException与NoClassDefFoundError的区别文章通过示例很清楚地解释了ClassNotFoundException和NoClassDefFoundError之间的区别。

ClassNotFoundException Vs NoClassDefFoundError -- Programming Mitra

ClassNotFoundException

是一种已检查异常,当我们使用Class.forName()或ClassLoader.findSystemClass()或ClassLoader.loadClass()方法按其字符串名称告诉JVM加载类时,并且所述类在类路径中未找到时发生。

大多数情况下,当您尝试在没有使用所需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使用。

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

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");
    }
}

2

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

如何获得NoClassDefFoundError
创建一个新项目,并包含此类。

public class A
{
    public void do()
    {
        System.out.println("Do!");
    }
}  

将它导出为一个.jar文件。 现在创建另一个项目,将导出的jar文件添加到类路径中。
import ???.A;
public class Main
{
    public static void main(String[] args)
    {
        A a = new A();
        a.do();//NoClassDefFoundError thrown at here.
    }
} 

导出项目时,请确保不要包含带有类A的jar文件。运行新导出的jar文件,你会看到出现了错误!


2

1) ClassNotFoundException

  1. 当我们使用 Class.forName() 或者 ClassLoader.loadClass() 或者 ClassLoader.findSystemClass() 方法在运行时加载类时,如果在类路径中找不到所需的类,则会出现此问题。

  2. 在这种情况下,我们应该检查类路径,如果缺少某个类,则将其添加到类路径中。

  3. 这是一个已检查异常,派生自 java.lang.Exception 类。

  4. 这属于显式加载。

2) NoClassDefFoundError

  1. 当某个类在编译时存在,但在运行时由于某些原因不可用时,就会出现此问题。这意味着正在加载的类在 classpath 中是存在的,但该类所依赖的其中一个或多个类由于被编译器删除无法加载而无法加载。

  2. 在这种情况下,我们只需要检查依赖于该类的类

  3. 这是一个Error,派生自 java.lang.LinkageError 类。

  4. 这属于隐式加载。


这总是令人困惑。在问题的给定示例中,类A间接地引用了类B。类A依赖于类B,因此它应该抛出NoClassDefFoundError。 - Omer Gillani

1

如之前的回答所述,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();
    }
  }
}

Result:

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时,系统会抛出NoClassDefFoundError错误。
为什么在Java中会出现NoClassDefFoundError错误?找到了这个示例。

0
一个NoClassDefFoundError出现的情况是当JVM试图访问的类在类路径中找不到时。 但如果类存在于类路径中,则会导致ClassNotFoundException。
简而言之,如果一个类在编译时存在,但在运行时Java类路径中不可用,就会出现NoClassDefFoundError。
尝试使用显式-classpath选项运行,其中类路径不包含类B。

2
如果类已经存在于类路径中,为什么会出现“ClassNotFoundException”呢? - CupawnTae

-2
这个帖子中的其他答案是正确的,我只想补充一些我花了几个小时才弄清楚的东西。即使

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

工作正常。

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接