Java 中的 ClassNotFoundException

10 Sept 2024 | 5 分钟阅读

对于初学者来说,一个特别令人困惑的异常是 **Java 中的 ClassNotFoundException**。在本教程中,我们将熟悉 ClassNotFound 异常及其解决方法。

ClassNotFoundException

顾名思义,当类找不到时,就会发生 ClassNotFoundException。当 Java 虚拟机 (JVM) 尝试加载特定类,但该类在指定的类路径中找不到时,就会抛出 ClassNotFoundException。换句话说,类路径已损坏。请注意,ClassNotFoundException 是一个已检查异常。

可以使用以下方法加载类:

  • ClassLoader 类的 findSystemClass() 方法。
  • Class 类的 forName() 方法。
  • ClassLoader 类的 loadClass() 方法。

ClassNotFoundException 的解决方法

在处理 ClassNotFoundException 时,可以考虑以下几点:

  • 仔细检查 java.lang.ClassNotFoundException 的堆栈跟踪,以识别在运行时未加载的类。
  • 还要检查请求的类名,以确保其名称正确。还需要在类路径中指定适当的 .jar 文件。例如,如果我们尝试加载类 A,而类路径中指定的 .jar 文件仅包含类 B 的文件,那么就会抛出 ClassNotFound 异常。
  • 如果类路径中存在适当的文件,那么很可能类路径被覆盖了。这也会导致异常被抛出。因此,应为应用程序提供精确的类路径。
  • 如果异常是由于包含第三方类引起的,则需要将适当的 .jar 文件添加到应用程序的类路径中。

示例 - 1

在此示例中,我们将看到 ClassNotFound 异常是如何抛出和解决的。在此示例中,我们将使用 findSystemClasas() 方法。

文件名: ClassException1.java

输出

Exception in thread "main" java.lang.ClassNotFoundException: com.google.common.collect.ImmutableList
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
	at java.base/java.lang.ClassLoader.findSystemClass(ClassLoader.java:1252)
	at SystemClass.loadSystemClass(ClassException1.java:6)
	at ClassException1.main(ClassException1.java:28)

说明: JVM 正在尝试加载 ImmutableList(在运行时),但它不存在。因此,抛出了 ClassNotFound 异常。请注意,当我们使用以下命令运行上述程序时,会抛出上述异常:

因此,我们需要将适当的 .jar 文件添加到类路径中。我们可以使用以下命令执行此操作。

我们得到以下输出:

示例 - 2

在下面的示例中,我们使用了 Class 类的 forName() 方法。

文件名: ClassException2.java

输出

Exception in thread "main" java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
	at java.base/java.lang.Class.forName0(Native Method)
	at java.base/java.lang.Class.forName(Class.java:375)
	at ClassException2.main(ClassException2.java:10)

说明: 问题相同。JVM 正在尝试加载 Driver 类但无法加载。因此,我们必须添加 JVM 可以找到 Driver.class 文件的路径。上述代码使用以下命令运行:

为了包含适当的 .jar 文件以成功运行程序,我们需要进行以下修改。

我们得到以下输出:

示例 - 3

在下面的示例中,我们使用了 loadClass() 方法。

文件名: ClassException3.java

输出

Exception in thread "main" java.lang.ClassNotFoundException: com.google.common.collect.Constraint
	at java.base/java.lang.ClassLoader.findClass(ClassLoader.java:718)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:587)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
	at SystemClass.loadSystemClass(ClassException3.java:6)
	at ClassException3.main(ClassException3.java:28)

说明: 与上面的示例相同。如果我们将 jar 文件包含在类路径中,上面的代码将顺利运行。读者可以参考示例 1,自行完成。

类路径和 ClassNotFoundException

类路径包含类被加载的位置列表。这些位置包括 jar 文件或目录。对于目录,Java 虚拟机在加载类时遵循一个模式。

例如,如果将目录“C:/javaProj/MyClasses”放在类路径中,并且在应用程序中尝试加载类 com.myproj.myprogram,则 JVM 会在 MyClasses 目录中搜索名为 com 的文件夹。在 com 中,它会查找名为 myproj 的目录,在 myproj 中,它会搜索名为 myprogram.class 的文件。

对于 jar 文件,Java 虚拟机将在 jar 文件中查找需要加载的类。与上面的示例类似,jar 文件是以压缩形式收集的各种目录。如果我们解压 jar 文件,我们会得到遵循上述模式的各种目录和各种类文件。

JVM 从头到尾读取类路径,并查找需要加载的类的类定义。例如,在以下类路径中:

C:/javaProj/MyClasses; C:/javaProj/MyClasses/jkl.jar; C:/javaProj/MyClasses/xyz.jar

JVM 首先在 MyClasses 目录中查找需要加载的适当类,然后在 jkl.jar 中,最后在 xyz.jar 文件中查找。

因此,如果抛出 ClassNotFoundException,则意味着所需文件既不在 .jar 文件中,也不在 MyClasses 目录中。