Класс Java присутствует в classpath, но запуск завершается неудачно с ошибкой: не удалось найти или загрузить основной класс

У меня есть фляга 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 из банки доказывает, что класс должен быть доступен на пути к классам. Я понимаю, что это может быть связано с интерфейсом ItemProcessor не быть на пути к классам. Но я не должен получить java.lang.ClassNotFoundException в таком случае?

1 ответ

Решение

Проблема действительно в том, что интерфейс ItemProcessor не на пути к классам. Обратите внимание, что в сообщении об ошибке указано "найти или загрузить основной класс". В случае BarNotThere JVM действительно не может найти основной класс. Но в 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

(Это было создано с использованием 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
$
Другие вопросы по тегам