Вопрос-ответ

What does "Could not find or load main class" mean?

Что означает "Не удалось найти или загрузить основной класс"?

Распространенная проблема, с которой сталкиваются начинающие разработчики Java, заключается в том, что их программы не запускаются с сообщением об ошибке: Could not find or load main class ...

Что это значит, что вызывает это и как вы должны это исправить?

Переведено автоматически
Ответ 1

java <class-name> Синтаксис команды

Прежде всего, вам нужно понять правильный способ запуска программы с помощью команды java (или javaw).

Обычный синтаксис1 таков:

    java [ <options> ] <class-name> [<arg> ...]

где <option> - параметр командной строки (начинающийся с символа "-"), <class-name> - полное имя класса Java и <arg> - произвольный аргумент командной строки, который передается вашему приложению.


1 - Есть некоторые другие синтаксисы, которые описаны ближе к концу этого ответа.

Полное имя (FQN) для класса обычно записывается так, как вы бы делали в исходном коде Java; например

    packagename.packagename2.packagename3.ClassName

Однако некоторые версии java команды позволяют использовать косые черты вместо точек; например

    packagename/packagename2/packagename3/ClassName

который (что сбивает с толку) выглядит как путь к файлу, но таковым не является. Обратите внимание, что термин полное имя является стандартной терминологией Java ... это не то, что я просто придумал, чтобы сбить вас с толку :-)

Вот пример того, как должна выглядеть java команда:

    java -Xmx100m com.acme.example.ListUsers fred joe bert

Вышесказанное приведет к тому, что java команда выполнит следующее:


  1. Найдите скомпилированную версию com.acme.example.ListUsers класса.

  2. Загрузите класс.

  3. Проверьте, есть ли у класса main метод с сигнатурой, возвращаемым типом и модификаторами, заданными public static void main(String[]). (Обратите внимание, имя аргумента метода НЕ является частью подписи.)

  4. Вызовите этот метод, передав ему аргументы командной строки ("fred", "joe", "bert") в качестве String[].

Причины, по которым Java не может найти класс

Когда вы получаете сообщение "Не удалось найти или загрузить основной класс ...", это означает, что первый шаг завершился неудачей. Команде java не удалось найти класс. И действительно, "..." в сообщении будет полным именем класса, который java ищет.

Итак, почему он может быть не в состоянии найти класс?

Причина № 1 - вы допустили ошибку с аргументом classname

Первая вероятная причина заключается в том, что вы, возможно, указали неправильное имя класса. (Или ... правильное имя класса, но в неправильной форме.) Учитывая приведенный выше пример, вот множество неправильных способов указать имя класса:


  • Пример # 1 - простое имя класса:


    java ListUser

    Когда класс объявлен в пакете, таком как com.acme.example, вы должны использовать полное имя класса, включая имя пакета в java команде; например


    java com.acme.example.ListUser


  • Пример # 2 - имя файла или пути вместо имени класса:


    java ListUser.class
    java com/acme/example/ListUser.class


  • Пример # 3 - имя класса с неправильной оболочкой:


    java com.acme.example.listuser


  • Пример # 4 - опечатка


    java com.acme.example.mistuser


  • Пример # 5 - исходное имя файла (за исключением Java 11 или более поздней версии; см. Ниже)


    java ListUser.java


  • Пример # 6 - вы полностью забыли название класса


    java lots of arguments


Причина № 2 - неверно указан путь к классу приложения

Вторая вероятная причина заключается в том, что имя класса указано правильно, но java команда не может найти класс. Чтобы понять это, вам нужно понять концепцию "пути к классу". Это хорошо объясняется документацией Oracle:

Итак ... если вы указали имя класса правильно, следующее, что нужно проверить, - это правильно ли вы указали путь к классу:


  1. Прочитайте три документа, на которые даны ссылки выше. (Да... ПРОЧИТАЙТЕ их! Важно, чтобы Java-программист понимал хотя бы основы того, как работают механизмы пути к классам Java.)

  2. Посмотрите в командной строке и / или в переменной окружения CLASSPATH, которая действует при запуске java команды. Убедитесь, что имена каталогов и JAR-файлов указаны правильно.

  3. Если в пути к классу есть относительные пути, убедитесь, что они разрешаются правильно ... из текущего каталога, который действует при запуске java команды.

  4. Убедитесь, что класс (упомянутый в сообщении об ошибке) может быть расположен на эффективном пути к классу.

  5. Обратите внимание, что синтаксис classpath отличается для Windows по сравнению с Linux и Mac OS. (Разделитель classpath есть ; в Windows и : в других. Если вы используете неправильный разделитель для вашей платформы, вы не получите явного сообщения об ошибке. Вместо этого вы получите несуществующий файл или каталог по пути, который будет автоматически проигнорирован.)

Причина # 2a - в пути к классу указан неправильный каталог

Когда вы помещаете каталог в classpath , он условно соответствует корню в пространстве полных имен. Классы располагаются в структуре каталогов под этим корнем, путем сопоставления полного имени с именем пути. Так, например, если "/ usr / local / acme / classes" находится в пути к классу, то когда JVM ищет класс с именем com.acme.example.Foon, он будет искать файл ".class" с этим путем:

  /usr/local/acme/classes/com/acme/example/Foon.class

Если бы вы поместили "/ usr / local / acme / classes / com / acme / example" в classpath , то JVM не смогла бы найти класс.

Причина # 2b - путь к подкаталогу не совпадает с FQN

Если ваш полный код classes равен com.acme.example.Foon, то JVM будет искать "Foon.class" в каталоге "com /acme /example":


  • Если ваша структура каталогов не соответствует названию пакета в соответствии с приведенным выше шаблоном, JVM не найдет ваш класс.



  • Если вы попытаетесь переименовать класс, переместив его, это также завершится неудачей ... но stacktrace исключения будет другим. Он может сказать что-то вроде этого:


    Caused by: java.lang.NoClassDefFoundError: <path> (wrong name: <name>)

    потому что FQN в файле класса не соответствует тому, что ожидает найти загрузчик классов.



Чтобы привести конкретный пример, предположим, что:


  • вы хотите запустить com.acme.example.Foon класс,

  • полный путь к файлу /usr/local/acme/classes/com/acme/example/Foon.class,

  • ваш текущий рабочий каталог /usr/local/acme/classes/com/acme/example/,

тогда:

# wrong, FQN is needed
java Foon

# wrong, there is no `com/acme/example` folder in the current working directory
java com.acme.example.Foon

# wrong, similar to above
java -classpath . com.acme.example.Foon

# fine; relative classpath set
java -classpath ../../.. com.acme.example.Foon

# fine; absolute classpath set
java -classpath /usr/local/acme/classes com.acme.example.Foon

Примечания:


  • В большинстве версий Java -classpath параметр может быть сокращен до -cp. Проверьте соответствующие записи руководства для java, javac и так далее.

  • Тщательно подумайте при выборе между абсолютными и относительными путями в classpaths . Помните, что относительный путь может "сломаться" при изменении текущего каталога.

Причина # 2c - в пути к классу отсутствуют зависимости

Путь к классу должен включать все другие (несистемные) классы, от которых зависит ваше приложение. (Системные классы находятся автоматически, и вам редко приходится беспокоиться об этом.) Для корректной загрузки основного класса JVM необходимо найти:

(Примечание: спецификации JLS и JVM допускают некоторую возможность для JVM загружать классы "лениво", и это может повлиять на возникновение исключения classloader.)

Причина № 3 - класс был объявлен в неправильном пакете

Иногда случается, что кто-то помещает файл исходного кода не в ту папку в своем дереве исходного кода или не учитывает package объявление. Если вы сделаете это в IDE, компилятор IDE немедленно сообщит вам об этом. Аналогично, если вы используете приличный инструмент сборки Java, инструмент будет запущен javac таким образом, чтобы обнаружить проблему. Однако, если вы создаете свой Java-код вручную, вы можете сделать это таким образом, что компилятор не заметит проблему, и результирующий файл ".class" окажется не в том месте, где вы ожидаете его найти.

Все еще не можете найти проблему?

Нужно многое проверить, и легко что-то пропустить. Попробуйте добавить -Xdiag опцию в java командную строку (как первое, что следует после java). Он выдаст различные сведения о загрузке класса, и это может дать вам подсказки относительно того, в чем заключается реальная проблема.

Также рассмотрите возможные проблемы, вызванные копированием и вставкой невидимых символов или символов, отличных от ASCII, с веб-сайтов, документов и т.д. И рассмотрите "гомоглифы", где две буквы или символа выглядят одинаково ... но это не так.

Вы можете столкнуться с этой проблемой, если у вас неверные подписи в META-INF/*.SF. Вы можете попробовать открыть файл .jar в вашем любимом ZIP-редакторе и удалять файлы из META-INF до тех пор, пока не останется только ваш MANIFEST.MF. Однако в целом это НЕ РЕКОМЕНДУЕТСЯ. (Неверная подпись может быть результатом того, что кто-то внедрил вредоносное ПО в исходный подписанный файл JAR. Если вы удалите неверную подпись, вы заразите вредоносным ПО свое приложение!) Рекомендуемый подход заключается в том, чтобы получить файлы JAR с действительными сигнатурами или перестроить их из (аутентичного) исходного кода.

Наконец, вы, очевидно, можете столкнуться с этой проблемой, если в MANIFEST.MF файле есть синтаксическая ошибка (см. https://javalang.ru/a/67145190/139985).


Альтернативные синтаксисы для java

Существует три альтернативных синтаксиса для запуска Java- программ с использованием java command.


  1. Синтаксис, используемый для запуска "исполняемого" JAR-файла, следующий:


    java [ <options> ] -jar <jar-file-name> [<arg> ...]

    например


    java -Xmx100m -jar /usr/local/acme-example/listuser.jar fred

    Имя класса точки входа (т.е. com.acme.example.ListUser) и путь к классу указаны в МАНИФЕСТЕ файла JAR. Все, что вы указываете в качестве пути к классу в командной строке, игнорируется с таким синтаксисом: используется только Class-Path запись в манифесте (и, транзитивно, в любых файлах JAR, на которые ссылается эта запись). Обратите также внимание, что URL-адреса в этом Class-Path указаны относительно местоположения JAR, в котором он содержится.



  2. Синтаксис для запуска приложения из модуля (Java 9 и более поздних версий) следующий:


    java [ <options> ] --module <module>[/<mainclass>] [<arg> ...]

    Имя класса entrypoint либо определяется <module> самим, либо задается необязательным <mainclass>.



  3. Начиная с Java 11, вы можете использовать команду java для компиляции и запуска одного файла исходного кода, используя следующий синтаксис:


    java [ <options> ] <sourcefile> [<arg> ...]

    где <sourcefile> находится (обычно) файл с суффиксом ".java".



Для получения более подробной информации, пожалуйста, обратитесь к официальной документации по java команде для версии Java, которую вы используете.


IDEs

Типичная Java IDE поддерживает запуск Java-приложений в самой IDE JVM или в дочерней JVM. Они обычно защищены от этого конкретного исключения, потому что IDE использует свои собственные механизмы для построения пути к классу среды выполнения, идентификации основного класса и создания java командной строки.

Однако это исключение все еще возможно, если вы делаете что-то за пределами IDE. Например, если вы предварительно настроили средство запуска приложений для вашего Java-приложения в Eclipse, а затем переместили JAR-файл, содержащий "основной" класс, в другое место в файловой системе, не сообщив об этом Eclipse, Eclipse невольно запустит JVM с неправильным путем к классу.

Короче говоря, если вы столкнулись с этой проблемой в IDE, проверьте наличие таких вещей, как устаревшее состояние IDE, неработающие ссылки на проекты или неработающие конфигурации запуска.

IDE также может просто запутаться. IDE - это чрезвычайно сложные части программного обеспечения, состоящие из множества взаимодействующих частей. Многие из этих частей используют различные стратегии кэширования, чтобы сделать IDE в целом отзывчивой. Иногда они могут работать неправильно, и одним из возможных симптомов являются проблемы при запуске приложений. Если вы подозреваете, что это может происходить, стоит попробовать другие вещи, такие как перезапуск вашей IDE, перестройка проекта и так далее.


Другие ссылки

Ответ 2

Если ваше имя исходного кода равно HelloWorld.java, ваш скомпилированный код будет HelloWorld.class.

Вы получите эту ошибку, если вызовете ее с помощью:

java HelloWorld.class

Вместо этого используйте это:

java HelloWorld
Ответ 3

Если ваши классы находятся в пакетах, тогда вам нужно cd перейти в корневой каталог вашего проекта и запустить, используя полное имя класса (packageName.MainClassName).

Пример:

Мои классы находятся здесь:

D:\project\com\cse\

Полное имя моего основного класса:

com.cse.Main

Итак, я cd возвращаюсь в корневой каталог проекта:

D:\project

Затем выполните команду java:

java com.cse.Main

Этот ответ предназначен для того, чтобы избавить начинающих Java-программистов от разочарования, вызванного распространенной ошибкой. Я рекомендую вам прочитать принятый ответ для получения более глубоких знаний о пути к классам Java.

Ответ 4

С ключевым словом 'package'

Если у вас есть package ключевое слово в вашем исходном коде (основной класс определен в пакете), вы должны запустить его в иерархическом каталоге, используя полное имя класса (packageName.MainClassName).

Предположим, что существует файл исходного кода (Main.java):

package com.test;

public class Main {

public static void main(String[] args) {
System.out.println("salam 2nya\n");
}
}

Для запуска этого кода вы должны поместить Main.Class в каталог package like:


C:\Users\workspace\testapp\com\test\Main.Java


Затем измените текущий каталог терминала на корневой каталог проекта:

cd C:\Users\workspace\testapp

И, наконец, запустите код:

java com.test.Main

Без ключевого слова 'package'

Если у вас нет никакого пакета в имени вашего исходного кода, возможно, вы ошиблись с неверной командой. Предположим, что ваше имя файла Java Main.java, после компиляции:

javac Main.java

ваш скомпилированный код будет Main.class

Вы получите эту ошибку, если вызовете ее с помощью:

java Main.class

Вместо этого используйте это:

java Main
java class