ClassNotFoundException 与 NoClassDefFoundError

ClassNotFoundException vs NoClassDefFoundError

我已经看完了这个话题What causes and what are the differences between NoClassDefFoundError and ClassNotFoundException? 这就是线程中具有最大提升的答案之一: NoClassDefFoundError :"So, it appears that the NoClassDefFoundError occurs when the source was successfully compiled, but at runtime, the required class files were not found. This may be something that can happen in the distribution or production of JAR files, where not all the required class files were included."

ClassNotFoundException :至于 ClassNotFoundException,它似乎源于在运行时尝试对 classes 进行反射调用,但 class es 程序试图调用不存在。

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

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

package com.random;

public class A {

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

    }

}

 package com.random;

public class B {



}

NoClassDefFoundError 通常在您使用库(例如 Guava、Gson、CommonsIO)时调用。你把库放在你项目的class路径下,但是你没有一起导出,当应用程序是运行.

时你会得到一个NoClassDefFoundError

如何获得NoClassDefFoundError:
使用此 class.

创建一个新项目
public class A
{
    public void do()
    {
        System.out.println("Do!");
    }
}  

将其导出为 .jar 文件。

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

import ???.A;
public class Main
{
    public static void main(String[] args)
    {
        A a = new A();
        a.do();//NoClassDefFoundError thrown at here.
    }
} 

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

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

原因 1。Class-名称无效 java-class(示例 - "java.bang.kiting")。

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

原因 2。Class-name 是一个有效的 class... 但不知何故它没有与 jar 一起打包或未在 class-path 中解析。据运行时所知...它可能是一个错误的 class 名称...类似于情况 1.

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

其中 NoClassDefFoundError 对于使用实际 class 引用的情况,

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

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

这里运行时知道 class 是有效的,因为它编译成功...但是它找不到 "class definition".

NoClassDefFoundError

Thrown if the Java Virtual Machine or a ClassLoader instance tries to load in the definition of a class (as part of a normal method call or as part of creating a new instance using the new expression) and no definition of the class could be found.

The searched-for class definition existed when the currently executing class was compiled, but the definition can no longer be found.


ClassNotFoundException

Thrown when an application tries to load in a class through its string name using: The forName method in class Class. The findSystemClass method in class ClassLoader . The loadClass method in class ClassLoader.


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

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

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

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


重现 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 的示例

创建一个简单的classTest

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

还有一个classNoClassDefFoundErrorExample

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

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

现在创建一个执行 main 方法的可执行文件 .jar。您可以在 .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.run(URLClassLoader.java:372)
    at java.net.URLClassLoader.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

NoClassDefFoundError 发生的一种情况是 Class JVM 试图访问的内容未在 class 路径中找到。 但是如果 class 存在于 classpath 中,它将导致 ClassNotFoundException.

简而言之,如果 class 在编译时存在,但在 运行 期间在 java class 路径中不可用,则不会出现 ClassDefFoundError .

只需尝试 运行 使用明确的 -classpath 选项,其中 classpath 不包含 class B.

区别取决于谁要求加载 class:

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

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

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

有效,

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

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

1) ClassNotFoundException

  1. 当我们尝试使用 Class.forName()ClassLoader.loadClass()ClassLoader.findSystemClass() 方法在 运行 时加载 class 并且它可能 没有class路径中找到需要的class。
  2. 在这种情况下,我们应该检查 class path 并在 class 路径中添加 class 如果它丢失。
  3. 这是一个checked Exception,派生自java.lang.Exception class .
  4. 这属于 显式 加载。

2) NoClassDefFoundError

  1. 当 class 在 compile time 期间存在并且由于某些原因在 run time 期间不可用时,就会发生这种情况。这意味着正在加载的 class 是 classpath 中的 present ,但是此 class 所需的相关 classe(s) 之一是 已删除编译器加载失败

  2. 这种情况下,我们只需要检查classes which are dependent on this class.

  3. 这是一个 错误,它源自 java.lang.LinkageError.
  4. 这属于隐式加载。

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

我想添加另一种情况,它也可能导致 NoClassDefFoundError。

当您尝试加载一个 class 由于静态初始化块失败而导致加载失败时,系统将向您抛出 ExceptionInInitializerError。如果您 尝试再次加载相同的 class (之前加载失败),系统将抛出 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(之前初始化失败)时,系统抛出 NoClassDefFoundError。

Why am I getting a NoClassDefFoundError in Java?

中找到样本

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

ClassNotFoundException

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

大多数时候,当您尝试 运行 应用程序而不用所需的 JAR 文件更新 class 路径时,会发生此异常。例如,您可能在执行 JDBC 代码连接到您的数据库 i.e.MySQL 时看到了这个异常,但是您的 class 路径没有它的 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 class 表示应用程序确实不应该发生的异常行为,但应用程序开发人员不应尝试捕获它,它供 JVM 使用只有.

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

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