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

bos*_*oop 1 java jvm classpath classnotfoundexception classloading

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

public class Foo {

    public static void main(String[] args) {
        System.out.println("Foo");
    }
}
Run Code Online (Sandbox Code Playgroud)

另一个类看起来像这样:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

$ java -cp foobar.jar Foo
Foo
$ 
Run Code Online (Sandbox Code Playgroud)

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

$ java -cp foobar.jar Bar
Error: Could not find or load main class Bar
$
Run Code Online (Sandbox Code Playgroud)

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

$ java -cp foobar.jar BarNotThere
Error: Could not find or load main class BarNotThere
$
Run Code Online (Sandbox Code Playgroud)

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

bos*_*oop 9

问题确实是接口ItemProcessor不在类路径上.请注意,错误指出" 查找加载主类".在BarNotThereJVM 的情况下真的无法找到主类.但在这种Bar情况下,它无法加载主类.

为了完全加载类,JVM还需要每个超类对象的实例.在此过程中Bar,JVM尝试加载类对象ItemProcessor.但由于此接口不在类路径上,因此主类的加载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
   <unnamed> (foobar.jar)
      -> java.io
      -> java.lang
      -> javax.batch.api.chunk                              not found
Run Code Online (Sandbox Code Playgroud)

(这是使用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");
    }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,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
$
Run Code Online (Sandbox Code Playgroud)