Java类存在于类路径中,但启动失败,错误:无法找到或加载主类

我有一个包含以下两个类的jar文件foobar.jar

 public class Foo { public static void main(String[] args) { System.out.println("Foo"); } } 

另一个类看起来像这样:

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

如果我使用以下命令执行程序,程序将按预期运行并打印Foo

 $ java -cp foobar.jar Foo Foo $ 

但是如果我尝试使用类Bar的main方法启动程序,JVM会输出启动错误并退出:

 $ java -cp foobar.jar Bar Error: Could not find or load main class Bar $ 

这是同样的错误,就好像我会尝试使用不在jar中的类来启动程序,例如

 $ java -cp foobar.jar BarNotThere Error: Could not find or load main class BarNotThere $ 

为什么我会收到此错误? 可以启动Foo.main方法并且我能够从jar反编译类Bar的事实certificate,类应该在类路径上可用。 我意识到这可能与接口ItemProcessor不在类路径上有关。 但在这种情况下我不应该得到java.lang.ClassNotFoundException吗?

问题确实是接口ItemProcessor不在类路径上。 请注意,错误指出“ 查找加载主类”。 在BarNotThere的情况下,JVM实际上无法找到主类。 但在Bar案例中,它无法加载主类。

为了完全加载类,JVM还需要每个超类对象的实例。 在Bar这个过程中,JVM尝试加载ItemProcessor的类对象。 但由于此接口不在类路径上,因此加载主类Bar失败并且启动因Error: Could not find or load main class Bar终止Error: Could not find or load main class Bar

如果你很难找到有问题的类(因为没有这样的消息),你可以使用jdeps工具来检查类路径。 只需使用相同的类路径,但运行jdeps而不是java

 $ jdeps -cp foobar.jar Bar foobar.jar -> java.base foobar.jar -> not found  (foobar.jar) -> java.io -> java.lang -> javax.batch.api.chunk not found 

(这是使用openjdk-9创建的,实际输出可能会有很大差异,具体取决于Java版本)

这应该给你足够的提示,以寻找缺少的类。


进一步说明

注意加载和初始化类之间的区别。 如果在初始化期间类加载失败(这意味着该类已成功找到加载 ),您将获得预期的ClassNotFoundException 。 请参阅以下示例:

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

在这种情况下,可以在启动期间加载类FooBar 。 但它无法初始化,因为静态字段i需要ItemProcessor类,它不在类路径上。 如果在类上执行静态方法(即JVM尝试调用main方法),则初始化是一个前提条件。

 $ 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 $