Рубрики
Без рубрики

Понимание пути к классам Java: Создание проекта вручную

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

Это второй пост из серии, посвященной пониманию Java-проектов. Ранее мы рассмотрели, как Java на самом деле устанавливается в систему mac OS, а теперь мы собираемся создать базовое приложение. Затем мы перейдем к включению того же приложения с помощью Gradle, популярного инструмента сборки, и закончим включением нашего проекта в IDE IntelliJ.

Наша структура каталогов

Мы собираемся скомпилировать полностью обновленную версию Hello World, и в нашем раздутом примере будут использоваться пакеты, библиотеки и тесты, потому что это более показательно для проекта, который мы бы заметили в дикой природе. Этот проект предназначен для людей, которые немного поработали с Java и не собираются много останавливаться на синтаксисе вне инструментария.

Мы будем свободно следовать соглашениям Maven стандартная компоновка каталогов , что, по общему признанию, немного излишне для нашего крошечного приложения, но мы также будем продвигаться вперед, потому что это указывает на соглашения, с которыми мы обычно сталкиваемся при работе с Java. Вот как это выглядит:

.
├── bin
├── lib
│   ├── jfiglet-0.0.8.jar
│   └── junit-platform-console-standalone-1.4.2.jar
└── src
    ├── main
    │   └── java
    │       └── space
    │           └── gaston
    │               ├── greeter
    │               │   └── Greeter.java
    │               ├── helloWorld
    │               │   └── HelloWorld.java
    │               └── textEffects
    │                   └── Bubble.java
    └── test
        └── java
            └── space
                └── gaston
                    ├── greeter
                    │   └── GreeterTest.java
                    └── textEffects
                        └── BubbleTest.java

Давайте распакуем то, что у нас здесь есть:

  • bin будет содержать наши скомпилированные файлы .class
  • lib будет содержать наши сторонние библиотеки. В этом случае мы используем Figlet и тестовый запуск JUnit, к которому мы вернемся позже.
  • src будет содержать наш исходный код .java . Внутри src у вас есть подкаталоги главная и test , которые оба имеют подкаталог java для обозначения языка, и затем стандартная иерархия пакетов Java . В конце концов, после нескольких месяцев поисков, вы, наконец, наткнетесь на наши настоящие файлы .java .

Просто небольшое предостережение: я не проверил папку lib в Git, поэтому вам нужно будет захватить JFiglet и JUnit .банка файлы из Maven, если вы хотите создать этот проект самостоятельно. Частично это делается для того, чтобы показать, насколько сложно управлять зависимостями вручную, и помочь нам понять, как позже, насколько здорово, что другие инструменты могут прийти нам на помощь.

.java, .класс и .банка

Ладно, тогда я перебрал довольно много типов файлов. Я думаю, что наша ИДЕЯ совершенно обычно скрывает многие из этих особенностей, поэтому вот краткое описание того, что делает каждый из этих типов:

  • Файл .java представляет собой текстовый файл с читаемым человеком исходным кодом Java, хотя, по общему признанию, обозначение “читаемый”, гм, довольно спорно, когда дело доходит до определенных аспектов синтаксиса Java. По сути, файлы .java – это файлы, в которые мы, мягкие монстры из человеческой плоти, помещаем наш код.
  • A файл .class представляет собой скомпилированный байт-код Java, который может быть выполнен виртуальной машиной Java, по общему признанию, шикарным инструментом, который запускает наши Java-программы. Эти холодные, механические файлы – это те, которые компьютер любит читать.
  • A .банка файл представляет собой архив файлов .class (и других необходимых ресурсов), аккуратно заархивированных для удобства.

javac и java

Два наших двоичных файла JDK отвечают за компиляцию и запуск нашего кода: javac и java . Короче говоря, javac отвечает за превращение наших .java файлов в .class файлы, которые java может запускать.

Если мы соберем совершенно голые кости HelloWorld.java , затем мы можем передать его в качестве аргумента в javac и запустить его с java :

public class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Oh no, not another Hello World example...");
  }
}

Без множества файлов .java , внешних библиотек или выходных каталогов, о которых нужно беспокоиться, компиляция и запуск этого так же просты, как отправка пути к файлу в javac а затем запуск вывода:

$ javac HelloWorld.java
$ java HelloWorld
Oh no, not another Hello World example...

Обратите внимание, что файл .class в настоящее время будет скомпилирован в тот же каталог, что и его сопутствующий исходный код, и что мы вызываем класс HelloWorld напрямую с помощью java без расширения .class – последнее вызовет страшный java.lang. ClassNotFoundException ошибка, но это история для другого дня. Оказывается, мы используем java для запуска классов Java, а не Java .класс файлы.

Путь к классу

Еще одна потенциальная ошибка, которую мы можем увидеть прямо сейчас, – это файл java.lang. NoClassDefFoundError , который обычно возникает из- за наличия файла .class , который был создан во время компиляции (с помощью javac ) , но где- то потерялся , когда мы пытаемся запустить его с помощью java .

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

Давайте увеличим масштаб наших основных файлов .java :

.
├── greeter
│   └── Greeter.java
├── helloWorld
│   └── HelloWorld.java
└── textEffects
    └── Bubble.java

Наш Greeter.java , Адский мир.java и Bubble.java все они хранятся в разных пакетах (что в Java также означает разные каталоги) и имеют свои собственные требования. Hello World включает в себя как Больший , так и Пузырь , причем Пузырь сам по себе является адаптером к стороннему классу шрифтов Figlet из JFiglet .jar в нашей папке lib .

В нашей папке test у нас есть тесты для Большего и Bubble , который включает классы из JUnit в lib , а также требует фактического Большего и Пузырь классы для тестирования.

Добро пожаловать, друзья, в “Зависимости”.

Java, хотя и довольно умна, должна знать, куда идти, чтобы выполнить все эти требования – отсюда и путь к классам. Мы можем получить сенсацию прямо из лошадиных уст :

Значение пути к классу по умолчанию равно “.”, что означает, что выполняется поиск только в текущем каталоге. Указание либо переменной ПУТИ к классу, либо переключателя командной строки -cp переопределяет это значение.

Также:

Установка ПУТИ к КЛАССУ может быть сложной задачей, и ее следует выполнять с осторожностью.

Что просто очаровательно. По сути, ваш путь к классу должен содержать путь к нашему .jar файлы на вершину нашей иерархии пакетов. Его можно установить либо с помощью переменной окружения, чего вам не следует делать, либо с помощью гораздо лучшего варианта флага -cp .

Это также помогает объяснить распространенную ошибку Java, когда вы пытаетесь запустить java без cd сначала в каталог.

$ java bin.space.gaston.helloworld.HelloWorld
Error: Could not find or load main class bin.space.gaston.helloworld.HelloWorld
Caused by: java.lang.NoClassDefFoundError: space/gaston/helloworld/HelloWorld (wrong name: bin/space/gaston/helloworld/HelloWorld)

видите ли, bin не является частью иерархии пакетов Java, но должен находиться на пути к классу, если вы находитесь за пределами папки. Таким образом, оба нижеперечисленных действия будут работать:

$ cd bin
$ java space.gaston.helloworld.HelloWorld
$ java -cp bin space.gaston.helloworld.HelloWorld

Сводя все это воедино

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

1. Составление нашей основной папки:

$ javac -d bin -cp lib/jfiglet-0.0.8.jar src/main/java/space/gaston/greeter/Greeter.java src/main/java/space/gaston/helloWorld/HelloWorld.java src/main/java/space/gaston/textEffects/Bubble.java

Теперь мы используем флаг -d , чтобы указать, где должны находиться наши скомпилированные файлы. Мы вручную отправляем каждый из наших .java файлов в явак , поэтому нам не нужно добавлять их в путь к классам, но нам нужно добавить наш JFiglet .jar файл так что Пузырь может компилироваться.

2. Составление нашей тестовой папки:

$ javac -d bin -cp lib/junit-platform-console-standalone-1.4.2.jar:lib/jfiglet-0.0.8.jar src/main/java/space/gaston/textEffects/Bubble.java src/test/java/space/gaston/textEffects/BubbleTest.java src/main/java/space/gaston/greeter/Greeter.java src/test/java/space/gaston/greeter/GreeterTest.java

Нам нужно добавить как фиглет, так и JUnit .jar |

Ваш файл bin теперь будет выглядеть следующим образом – обратите внимание, что структура каталогов наших файлов .class должна поддерживать ту же иерархию пакетов, что и исходные файлы .java :

.
├── BubbleTests.class
├── GreeterTests.class
└── space
    └── gaston
        ├── greeter
        │   └── Greeter.class
        ├── helloworld
        │   └── HelloWorld.class
        └── textEffects
            └── Bubble.class

3. Запускаем наши тесты:

$ java -jar lib/junit-platform-console-standalone-1.4.2.jar -cp bin:lib/jfiglet-0.0.8.jar --scan-class-path
╷
├─ JUnit Jupiter ✔
│  ├─ BubbleTests ✔
│  │  └─ helloReturnsAsciiHello() ✔
│  └─ GreeterTests ✔
│     ├─ greetWithArgumentSteveReturnsHelloSteve() ✔
│     └─ greetWithNoArgsReturnsHelloWorld() ✔
└─ JUnit Vintage ✔

Нашу версию JUnit можно запустить в командной строке. Мы передаем флаг --scanclasspath , чтобы JUnit автоматически искал все тесты в вашем пути к классу, поэтому для этого требуется добавить папку bin в путь к классу (потому что мы находимся в верхней части папки нашего проекта), а также JFIGLET, который по-прежнему требуется Bubble .

Кроме того, ура, тесты пройдены.

4. Запуск нашего основного приложения:

$ java -cp bin:lib/jfiglet-0.0.8.jar space.gaston.helloworld.HelloWorld
  _   _          _   _             ____    _
 | | | |   ___  | | | |   ___     / ___|  | |_    ___  __   __   ___
 | |_| |  / _ \ | | | |  / _ \    \___ \  | __|  / _ \ \ \ / /  / _ \
 |  _  | |  __/ | | | | | (_) |    ___) | | |_  |  __/  \ V /  |  __/
 |_| |_|  \___| |_| |_|  \___/    |____/   \__|  \___|   \_/    \___|

Нет, я тоже понятия не имею, почему мы поручили ему вывести “Привет, Стив”.

Итак, отлично. У нас это работает. Но, черт возьми, разве это не многовато даже для простого, полностью надуманного приложения? Можете ли вы представить, что буквально каждый раз, когда вы вносите изменения в приложение и вам нужно перекомпилировать его? Не знаю, как вы, но если бы мне пришлось так работать больше недели, я бы навсегда застрял в том, чтобы выглядеть так, будто я играю роль в фильме Эдварда Мунка “Крик”.

В следующем посте кавалерия выступит, чтобы спасти нас от вечного ужаса строительства.

Был ли этот пост полезен для вас? Я был бы очень признателен за любые комментарии и отзывы о том, можно ли что-то прояснить или объяснить лучше. Я также был бы очень признателен за любые исправления!

Оригинал: “https://dev.to/martingaston/understanding-the-java-classpath-building-a-project-manually-3c3l”