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, JVM печатает ошибку запуска и завершает работу:

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

Это та же ошибка, что и при попытке запустить программу, используя класс, который не находится в банке, например

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

Почему я получаю эту ошибку? Тот факт, что метод Foo.main можно запустить, и я могу декомпилировать класс Bar из jar, доказывает, что класс должен быть доступен в пути к классам. Я понимаю, что это может иметь какое-то отношение к интерфейсу ItemProcessor, не находящемуся в пути к классам. Но не должен ли я получить java.lang.ClassNotFoundException в этом случае?

Ответ 1

Проблема в том, что интерфейс ItemProcessor не находится в пути к классам. Обратите внимание, что в этой ошибке указано " найти или загрузить основной класс". В случае BarNotThere JVM действительно не может найти основной класс. Но в случае Bar он не может загружать основной класс.

Чтобы полностью загрузить класс, JVM также нуждаются в экземплярах каждого объекта суперкласса. Во время этого процесса для Bar JVM пытается загрузить объект класса для ItemProcessor. Но поскольку этот интерфейс не находится в пути к классам, загрузка основного класса Bar завершается с ошибкой, а запуск завершается с помощью Error: Could not find or load main class Bar.


Дальнейшее объяснение

Обратите внимание на разницу между загрузкой и инициализацией класса. Если при инициализации сбой при загрузке (что означает, что класс был успешно найден и загружен), вы получите ожидаемый 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
$