Java 类存在于类路径中,但启动失败并出现错误:无法找到或加载主类
我有一个 jar 文件 foobar.jar
包含以下两个类:
公共类 Foo {公共静态无效主要(字符串[]参数){System.out.println("Foo");}}
另一个类是这样的:
import javax.batch.api.chunk.ItemProcessor;公共类 Bar 实现 ItemProcessor {公共静态无效主要(字符串[]参数){System.out.println("条");}@覆盖公共对象进程项(对象项)抛出异常 {归还物品;}}
如果我使用以下命令执行程序,程序会按预期运行并打印 Foo
:
$ java -cp foobar.jar Foo富$
但是如果我尝试使用 Bar
类中的 main 方法启动程序,JVM 会打印一个启动错误并退出:
$ java -cp foobar.jar 酒吧错误:无法找到或加载主类 Bar$
这与我尝试使用不在 jar 中的类启动程序的错误相同,例如
$ java -cp foobar.jar BarNotThere错误:无法找到或加载主类 BarNotThere$
为什么会出现此错误?可以启动 Foo.main
方法并且我能够从 jar 中反编译类 Bar
的事实证明,该类应该在类路径中可用.我意识到这可能与接口 ItemProcessor
不在类路径上有关.但是在那种情况下我不应该得到 java.lang.ClassNotFoundException
吗?
问题确实是接口ItemProcessor
不在classpath上.请注意,错误状态为find or load main class".在 BarNotThere
的情况下,JVM 确实无法 find 主类.但是在 Bar
的情况下,它无法加载主类.
为了完全加载一个类,JVM还需要每个超类对象的实例.在 Bar
的这个过程中,JVM 尝试为 ItemProcessor
加载类对象.但由于该接口不在类路径中,主类 Bar
的加载失败,启动以 Error: Could not find or load main class Bar
终止..p>
如果您难以找到有问题的类(因为没有消息这么说),您可以使用 jdeps
工具检查类路径.只需使用相同的类路径,但运行 jdeps
而不是 java
:
$ jdeps -cp foobar.jar 酒吧foobar.jar ->java.basefoobar.jar ->未找到<未命名>(foobar.jar)->java.io->java.lang->找不到 javax.batch.api.chunk
(这是使用 openjdk-9 创建的,实际输出可能因 Java 版本而有很大差异)
这应该会给你足够的提示,告诉你在哪里寻找丢失的类.
<小时>进一步说明
注意加载和初始化类之间的区别.如果在初始化期间类加载失败(这意味着类已成功找到并加载),您将获得预期的 ClassNotFoundException
.请参阅以下示例:
import javax.batch.api.chunk.ItemProcessor;公共类 FooBar {私有静态 ItemProcessor i = new ItemProcessor() {@覆盖公共对象进程项(对象项)抛出异常 {归还物品;}};公共静态无效主要(字符串[]参数){System.out.println("Foo");}}
在这种情况下,FooBar
类可以在启动期间加载.但它不能被初始化,因为静态字段 i
需要 ItemProcessor
类,它不在类路径中.如果一个类的静态方法被执行,那么初始化是一个先决条件,当 JVM 试图调用 main
方法时就是这种情况.
$ java -cp foobar.jar FooBar错误:发生 JNI 错误,请检查您的安装并重试线程主"java.lang.NoClassDefFoundError 中的异常:javax/batch/api/chunk/ItemProcessor在 java.lang.Class.getDeclaredMethods0(本机方法)在 java.lang.Class.privateGetDeclaredMethods(Class.java:2701)在 java.lang.Class.privateGetMethodRecursive(Class.java:3048)在 java.lang.Class.getMethod0(Class.java:3018)在 java.lang.Class.getMethod(Class.java:1784)在 sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)在 sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)引起:java.lang.ClassNotFoundException: javax.batch.api.chunk.ItemProcessor在 java.net.URLClassLoader.findClass(URLClassLoader.java:381)在 java.lang.ClassLoader.loadClass(ClassLoader.java:424)在 sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)在 java.lang.ClassLoader.loadClass(ClassLoader.java:357)... 7 更多$
I have a jar file foobar.jar
containing the following two classes:
public class Foo {
public static void main(String[] args) {
System.out.println("Foo");
}
}
The other class looks like this:
import javax.batch.api.chunk.ItemProcessor;
public class Bar implements ItemProcessor {
public static void main(String[] args) {
System.out.println("Bar");
}
@Override
public Object processItem(Object item) throws Exception {
return item;
}
}
If I execute the program using the following command, the program behaves as expected and prints Foo
:
$ java -cp foobar.jar Foo
Foo
$
But if I try to start the program using the main method in the class Bar
, the JVM prints a startup error and exits:
$ java -cp foobar.jar Bar
Error: Could not find or load main class Bar
$
This is the same error as if I would try to start the program using a class which is not in the jar, e.g.
$ java -cp foobar.jar BarNotThere
Error: Could not find or load main class BarNotThere
$
Why do I get this error? The fact that the Foo.main
method can be started and I'm able to decompile the class Bar
from the jar proves, that the class should be available on the classpath. I realize that this could have something to do with the interface ItemProcessor
not being on the classpath. But shouldn't I get a java.lang.ClassNotFoundException
in that case?
The problem is indeed that the interface ItemProcessor
is not on the classpath. Notice that the error states "find or load main class". In the case of BarNotThere
the JVM is really not able to find the main class. But in the Bar
case, it is not able to load the main class.
In order to completely load a class, the JVM also need instances of each superclass objects. During this process for Bar
, the JVM tries to load the class object for ItemProcessor
. But since this interface is not on the classpath, loading of the main class Bar
fails and the startup terminates with the Error: Could not find or load main class Bar
.
If you struggle with finding the problematic class in question (because the is no message saying so), you can use the jdeps
tool to inspect the classpath. Just use the same classpath, but run jdeps
instead of java
:
$ jdeps -cp foobar.jar Bar
foobar.jar -> java.base
foobar.jar -> not found
<unnamed> (foobar.jar)
-> java.io
-> java.lang
-> javax.batch.api.chunk not found
(This was created using openjdk-9, actual output may vary heavily depending on the Java version)
This should give you enough hints as where to look for the missing class.
Further explanation
Notice the difference between loading and initializing a class. If classloading fails during initialization (which means the class was successfully found and loaded), you will get your expected ClassNotFoundException
. See the following example:
import javax.batch.api.chunk.ItemProcessor;
public class FooBar {
private static ItemProcessor i = new ItemProcessor() {
@Override
public Object processItem(Object item) throws Exception {
return item;
}
};
public static void main(String[] args) {
System.out.println("Foo");
}
}
In this case, the class FooBar
can be loaded during startup. But it can not be initialized, since the static field i
needs the ItemProcessor
class, which is not on the classpath. Initialization is a precondition if a static method on a class is executed, which is the case, when the JVM tries to invoke the main
method.
$ java -cp foobar.jar FooBar
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.NoClassDefFoundError: javax/batch/api/chunk/ItemProcessor
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
at java.lang.Class.getMethod0(Class.java:3018)
at java.lang.Class.getMethod(Class.java:1784)
at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)
Caused by: java.lang.ClassNotFoundException: javax.batch.api.chunk.ItemProcessor
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)
... 7 more
$
相关文章