Программирование в IDE это прекрасно: связанность зависимостей кода, удобный дебаг, понятное тестирование, темная тема. Так вот, благодаря IDE разработка развивается семимильными шагами. Но она расслабляет. С каждым днем, погружаясь в функционал IDE, разработчик привыкает к коммиту одной кнопкой или сборке двумя кликами.
Гораздо хуже обстоит ситуация с новичками в программировании, которые с самого начала работают в IDE, игнорируя работу в командной строке. Например, в Intellij IDEA компиляция Java приложения демонстрируется загрузочным баром в нижней панели, а все параметры компиляции, обработка classpath и прочих прелестей Java-жизни остается за кадром.
Предлагаем поговорить о компиляции в Java без IDE. Если совсем коротко, весь процесс это две команды:
javac Test.java превращает исходник в байт-код и кладет рядом файл
Test.class, а
java Test этот байт-код запускает. Все остальное в статье это подробности: что делать, когда классов много, как собрать их в jar и что берут на себя Ant, Maven и Gradle.
Для запуска примеров в статье следует убедиться, что на вашей машине установлена JDK. Команды
javac,
java и
jar работают начиная с седьмой версии, а для запуска исходника напрямую, о котором речь ниже, понадобится Java 11 или новее.
Кратко
- Компиляция в Java это превращение исходника
.java в байт-код .class, который потом выполняет виртуальная машина.
- Всю базовую работу делают две команды:
javac компилирует, java запускает. Расширение .class при запуске не пишут.
- Когда классов много, исходники держат в
src, результат компиляции отправляют в отдельную папку флагом -d, а при запуске указывают ее в -classpath.
- Запускать нужно полное имя класса вместе с пакетом, например
src.BoxMachine, а не просто BoxMachine.
- Чтобы программу было удобно переносить, классы собирают в jar-архив командой
jar, а главный класс прописывают в файле манифеста.
- Для проектов крупнее учебных руками это не делают: есть Ant со сценарием в
build.xml, Maven с описанием проекта в pom.xml и Gradle с build.gradle.
![Заставка в стиле «Матрицы»: зеленые символы, падающие столбцами на черном фоне]()
Как скомпилировать программу?
Компиляция в программировании это приведение исходного кода в
байт-код для последующего старта программы.
Порядок действий от исходного кода до запуска программ выглядит так:
- Есть исходный код в файле с именем НазваниеКласса.java;
- Если в коде нет ошибок, он компилируется в байт-код (в файл НазваниеКласса.class);
- Программа запускается.
Обычно каждая программа содержится в отдельном каталоге.
В качестве самого простого примера возьмем вывод в консоль:
class Test {
public static void main(String[] args) {
System.out.println("Это говорит приложение из командной строки");
}
}
Для чего нужна команда javac
Окей, первый пункт выполнен. Идем дальше, чтобы понять: скомпилировать это как? :) В этом нам поможет команда javac, в аргументе которой необходимо указать нужный файл:
javac Test.java
Если нет ошибок в коде, рядом с файлом Test.java появится файл Test.class. Это и есть скомпилированный байт-код. Теперь его нужно запустить. Здесь используется команда java, запускающая байт-код:
![Скриншот консоли: после команды java Test вместо русского текста выводятся нечитаемые символы из-за сбитой кодировки]()
На скриншоте видно, что в выводе получаем какие-то иероглифы: очевидно, это сбитая кодировка. Как правило, это происходит в системе Windows. Для корректного отображения кириллицы в консоли, есть следующие команды:
REM change CHCP to UTF-8
CHCP 65001
CLS
Они меняют текущую кодовую страницу командной консоли на время работы текущего окна.
Попробуем еще раз:
D:\Java>java Test
Это говорит приложение из командной строки.
Знать принцип работы команды javac очень полезно, так как эта команда лежит в основе любой системы сборки проектов.
Как запустить файл без компиляции
Начиная с Java 11 для одного файла шаг с
javac можно пропустить: команда
java умеет принимать сам исходник.
java Test.java
Компиляция никуда не делась, просто происходит в памяти, и файл
Test.class рядом не появится. Возможность описана в
JEP 330, и придумана она ровно для таких случаев, как наш: «It is desirable to be able to run the program directly from source with the java launcher». На учебных примерах и небольших утилитах это экономит одну команду.
С Java 22 (
JEP 458) так же запускаются и программы из нескольких файлов: достаточно указать тот, где лежит метод
main, остальные исходники java найдет и скомпилирует сама. Все остальное в статье от этого не устаревает: как только проект перестает быть учебным, классы все равно приходится раскладывать и собирать по-настоящему.
Компиляция и выполнение нескольких классов
Для работы с несколькими классами нужен
classpath. Он похож на файловую систему, в которой содержатся классы, а функцию папок выполняют пакеты (packages).
На этом этапе стоит задуматься об отделении файлов исходного кода от скомпилированных файлов. Как правило, исходники находятся в каталоге src, а скомпилированные классы в bin.
Например, у нас есть класс
Box и класс
BoxMachine, в котором содержится метод
main.
Класс
Box:
package src;
public class Box {
private double size;
public Box(double size) {
this.size = size;
}
public String toString() {
return "Box have size " + size;
}
}
Он находится в пакете src, это необходимо зафиксировать.
Класс
BoxMachine:
package src;
public class BoxMachine {
public static void main(String[] args) {
for(int i = 0; i < 5; i++) {
System.out.println(new Box(Math.random()*10));
}
}
}
Этот класс также находится в пакете src. В методе
main он создает пять объектов класса
Box разного размера и выводит в консоль информацию о них.
Чтобы скомпилировать эту группу классов, необходимо из главного каталога (в котором лежат папки src и bin) использовать команду javac с аргументами:
javac -d bin ./src/*
-d: флаг, после которого следует указать расположение, куда попадут скомпилированные классы. Это очень удобно, так как перекладывать, например, 1000 классов вручную очень трудоемко.
bin: название папки.
./src/*: расположение исходных файлов.
* указывает, что необходимо скомпилировать все файлы.
Теперь скомпилированные классы появились в папке bin. Для их запуска используется команда java из той же директории, также с аргументами:
java -classpath ./bin src.BoxMachine
-classpath: флаг, после которого следует указать местоположение скомпилированных классов. Java будет искать главный класс и все сопутствующие именно в этой директории.
./bin: название папки, в которой находятся скомпилированные классы.
src.BoxMachine: полное имя главного класса вместе с пакетом. Просто
BoxMachine здесь не сработает, потому что оба класса объявлены в пакете
src, и искать их java будет по полному имени. Разбор именно этой ошибки есть в отдельной статье про
нюансы компиляции через cmd. Как и в первом случае, не следует указывать
.class, так как это название класса, а не файла.
Вывод:
D:\Java>java -classpath ./bin src.BoxMachine
Box have size 4.085985295359718
Box have size 8.63682158248986
Box have size 6.027448124299726
Box have size 7.288317703877914
Box have size 1.106181659384694
Создание JAR-файлов
Чтобы программу было легко переносить и запускать, можно собрать скомпилированные классы в jar-файл, то есть в архив классов. Главное отличие от zip или rar-архивов это наличие файла манифеста. В этом манифесте указывается главный класс, который будет запускаться при выполнении jar-файла, classpath, а также много дополнительной информации.
В главном каталоге создадим файл manifest.mf. Его содержимое будет следующим:
main-class: src.BoxMachine
class-path: bin/
main-class указывает класс, который содержит метод
main и будет выполнен при запуске.
class-path: путь к скомпилированным классам или дополнительным библиотекам.
Настало время собрать настоящую программу без IDE с помощью команды jar:
jar -cmf manifest.mf box-machine.jar -C bin .
-cmf: это сразу три флага.
c создает архив,
m подставляет манифест,
f задает имя выходного файла. Аргументы идут в том же порядке, в каком стоят буквы: сначала манифест, потом имя jar.
manifest.mf: путь к манифесту.
box-machine.jar: название выходного jar-файла.
-C: флаг, после которого указывается путь к скомпилированным классам. В
документации он описан так: «changes the specified directory and includes the files specified at the end of the command line».
.: то, что нужно положить в архив. Точка здесь означает «все содержимое папки bin», в которую мы перешли предыдущим флагом. Сам jar-файл окажется не тут, а там, где мы его назвали флагом
f, то есть в текущем каталоге.
Теперь можно запустить. Запуск jar-файлов выполняется также с помощью команды java, но следом нужно указать флаг
-jar: он говорит о том, что запускается Jar-файл, а второй аргумент это
путь к jar-файлу, включая расширение:
java -jar box-machine.jar
Вывод:
Box have size 5.5495235762547965
Box have size 9.695870044165662
Box have size 2.3408385788129227
Box have size 7.2790741216674135
Box have size 2.3620854470160513
Компиляция в Java без IDE: обзор систем сборок
Несмотря на относительную простоту использования командной строки, с ее помощью очень сложно собирать средние и большие проекты. Это занимает много времени и чревато ошибками разной степени.
К счастью, есть системы сборки, которые в разы облегчают процесс работы. Несколькими командами эта система может собрать проект любой сложности, а обилие плагинов, созданных за время существования таких систем, может избавить практически от любой головной боли.
Ant, Maven и Gradle: чем они отличаются
Самые известные системы сборки на Java это Ant, Maven и Gradle. Среди них нет плохой или хорошей: каждая из них создана для решения определенных задач. Рассмотрим каждую подробнее.
| Система |
Файл сборки |
Подход |
Чем берет |
| Ant |
build.xml |
Сценарий: вы сами описываете последовательность действий |
Полный контроль над каждым шагом сборки |
| Maven |
pom.xml |
Описание проекта, а не шагов |
Управление зависимостями и стандартная структура каталогов |
| Gradle |
build.gradle |
Описание проекта плюс скрипт |
Граф задач, инкрементальные и многопроектные сборки |
Ant
Ant это инструмент сборки, который использует сценарий, описанный с помощью xml-файла.
Структура xml-файла:
<?xml version="1.0"?>
<project name="имяПроекта" default="сценарийПоУмолчанию">
<target name="имяСценария">
<echo>Hello, World!</echo>
</target>
</project>
Создадим в главном каталоге файл build.xml со следующим содержимым:
<?xml version="1.0"?>
<project name="BoxMachine" default="test">
<target name="test">
<echo>First build in Ant!</echo>
</target>
</project>
В этом же каталоге вызовем команду ant:
D:\Java>D:\Temp\ant\bin\ant
Buildfile: D:\Java\build.xml
test:
[echo] First build in Ant!
BUILD SUCCESSFUL
Total time: 0 seconds
В теге
<target> можно указывать различные задания, позволяющие управлять сборкой и файловой системой. У Ant есть более 150 доступных команд, которые указаны в документации. В примере ниже используем только 4:
mkdir: создание директорий
delete: удаление файлов и директорий
javac: компиляция Java-кода
java: запуск скомпилированного кода
Так выглядит простой сценарий компиляции, сборки или очистки:
<?xml version="1.0"?>
<project name="BoxMachine" default="compile">
<target name="compile">
<mkdir dir="result/classes"/>
<javac destdir="result/classes" includeantruntime="false">
<src path="src"/>
</javac>
</target>
<target name="run" depends="compile">
<java classname="src.BoxMachine" classpath="result/classes"/>
</target>
<target name="clean">
<delete dir="result"/>
</target>
</project>
В сценарии описано три действия:
compile,
run и
clean.
compile создает директорию result, в ней classes, затем с помощью javac компилирует классы в созданную директорию.
run запускает скомпилированные классы командой java.
clean удаляет директорию result.
Если в главном каталоге выполнить команду ant без аргументов, запустится действие compile. Если нужно выполнить определенное действие, его указывают в аргументе.
D:\Java>D:/Temp/ant/bin/ant compile
Buildfile: D:\Java\build.xml
compile:
[mkdir] Created dir: D:\Java\result\classes
[javac] Compiling 2 source files to D:\Java\result\classes
BUILD SUCCESSFUL
Total time: 1 second
Maven
Maven предлагает несколько другой подход к сборке проектов. Здесь разработчик скорее описывает свой проект и дополнительные инструменты, которые использует, в отличие от Ant, где сборка это последовательность действий.
Maven популярен среди разработчиков благодаря простому управлению зависимостями и удобной интеграции со всеми средами разработки. Подробный разбор самого инструмента есть в статье
Основы Maven.
При работе с Maven придерживаются такой структуры проекта:
![Скриншот панели Project в IDE: структура Maven-проекта с папками src/main/java, src/main/resources, src/test/java и файлом pom.xml в корне]()
Правила сборки, зависимости и прочее описывается в файле pom.xml. Как правило, он находится в главной папке проекта. При запуске Maven проверяет структуру и синтаксис файла, предупреждая об ошибках.
В главной директории рядом с папками bin и src создаем файл pom.xml, внутрь добавляем:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>ru.javarush.testmaven</groupId>
<artifactId>testMavenWithoutIde</artifactId>
<version>1.0.0</version>
<build>
<defaultGoal>compile</defaultGoal>
<sourceDirectory>src</sourceDirectory>
<outputDirectory>bin</outputDirectory>
<finalName>${project.artifactId}-${project.version}</finalName>
</build>
</project>
Далее в командной строке выполняем команду mvn:
D:\Java>mvn
[INFO] Scanning for projects...
[INFO]
[INFO] -------------< ru.javarush.testmaven:testMavenWithoutIde >--------------
[INFO] Building testMavenWithoutIde 1.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ testMavenWithoutIde ---
[WARNING] Using platform encoding (Cp1251 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory D:\Java\src\main\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ testMavenWithoutIde ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1251, i.e. build is platform dependent!
[INFO] Compiling 2 source files to D:\Java\bin
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 15.521 s
[INFO] Finished at: 2019-06-25T20:18:05+03:00
[INFO] ------------------------------------------------------------------------
Теперь в папке bin есть папка src, в которой находятся скомпилированные классы. В pom.xml в теге build определена цель сборки, то есть компиляция, директории файлов исходного кода и результата компиляции, а также имя проекта.
У Maven есть множество целей сборки и плагинов для запуска тестирования, создания Jar-файлов, сборки дистрибутивов и других задач.
Gradle
Это самая молодая система сборки, которая основывается на Ant и Maven. Главное отличие это работа на базе ациклического графа для определения порядка выполнения задач. Это очень полезно при более сложных задачах, например, инкрементальных и многопроектных сборках.
При сборке с помощью Gradle также рекомендуется придерживаться структуры папок проекта Maven. Отдельное
знакомство с Gradle у нас тоже есть.
Кстати, файл для сборки в Gradle называется build.gradle и выглядит гораздо меньше, чем у Maven. Пример для наших классов:
plugins {
id 'java'
id 'application'
}
sourceSets {
main {
java {
srcDirs 'src'
destinationDirectory = file('bin')
}
}
}
application {
mainClass = 'src.BoxMachine'
}
defaultTasks 'compileJava', 'run'
В файле происходит подключение плагинов, определение директории файлов исходного кода (если не используется структура проектов Maven), директория результатов сборки, имя главного класса, а также задачи по умолчанию.
Если вам попадется старый пример, где вместо этого написано
sourceSets.main.output.classesDir и
mainClassName, копировать его не стоит. Первое свойство
удалено в Gradle 5.0, второе
в Gradle 8.0, и сборка на них просто не запустится. Папку для скомпилированных классов теперь задают через
destinationDirectory, а главный класс через блок
application.
За запуск сборки отвечает команда gradle в директории, где лежит файл build.gradle:
d:\Java>D:\Temp\gradle\bin\gradle
Welcome to Gradle 5.4.1!
Here are the highlights of this release:
- Run builds with JDK12
- New API for Incremental Tasks
- Updates to native projects, including Swift 5 support
For more details see https://docs.gradle.org/5.4.1/release-notes.html
Starting a Gradle Daemon (subsequent builds will be faster)
> Task :help
Welcome to Gradle 5.4.1.
To run a build, run gradle <task> ...
To see a list of available tasks, run gradle tasks
To see a list of command-line options, run gradle --help
To see more detail about a task, run gradle help --task <task>
For troubleshooting, visit https://help.gradle.org
BUILD SUCCESSFUL in 52s
1 actionable task: 1 executed
В выводе видно строку
> Task :help: так Gradle отвечает, когда не получил ни задачи в аргументах, ни
defaultTasks в файле сборки. С нашим
build.gradle рядом та же команда выполнит уже
compileJava и
run.
Заключение
На первый взгляд, умение компиляции и сборки кода без IDE кажется бесполезным. Действительно, зачем мучиться с командными строками и гуглить все команды, когда есть уютная IDEшечка с плагинами, автопроверкой всего, что можно (современные IDE разве что уровень IQ не проверяют) и интеграцией с популярными системами.
Однако практика показывает, что умение собирать код без среды разработки и понимание каждого шага этого процесса это суровая необходимость. Этот навык сэкономит немало нервных клеток и времени вам и вашей компании.
Освоить работу в IDE, попрактиковаться в написании кода и, конечно же, получить фундаментальные основы по программированию на Java вы можете здесь, на JavaRush. Пора вернуться к обучению :)
Вопросы и ответы
Как скомпилировать Java-файл из командной строки?
Перейдите в папку с файлом и выполните javac ИмяКласса.java. Если в коде нет ошибок, рядом появится файл ИмяКласса.class с байт-кодом. Запускается он командой java ИмяКласса, уже без расширения.
Чем отличаются команды javac и java?
javac это компилятор: он читает исходный текст и создает файл с байт-кодом. java это запускающая программа, она берет готовый байт-код и выполняет его на виртуальной машине. Сначала всегда javac, потом java.
Что такое classpath и зачем он нужен?
Classpath это список мест, где java и javac ищут классы. Он похож на файловую систему, только роль папок в нем играют пакеты. Когда скомпилированные классы лежат не рядом, а в отдельной папке, путь к ней передают флагом -classpath, иначе запуск закончится ошибкой о ненайденном классе.
Почему при запуске пишут java Test, а не java Test.class?
Потому что аргумент команды java это имя класса, а не имя файла. Расширение тут лишнее. По той же причине класс из пакета нужно называть полностью, вместе с пакетом: не BoxMachine, а src.BoxMachine.
Как собрать программу в jar-файл и запустить его?
Сначала создается файл манифеста, в котором указан главный класс. Потом выполняется команда jar с флагами c, m и f: она создает архив, кладет в него манифест и скомпилированные классы. Запускается готовый архив командой java -jar имя.jar, и тут расширение как раз пишут, потому что это уже путь к файлу.
Зачем нужны Ant, Maven и Gradle, если есть javac?
Голыми командами удобно собирать учебные примеры, но на среднем проекте это долго и легко ошибиться. Системы сборки делают то же самое одной командой, подтягивают зависимости, запускают тесты и собирают дистрибутив. Ant описывает сборку как сценарий, Maven как описание проекта, Gradle совмещает оба подхода.
Читайте также
CHCP 1251тоже иногда помогает справиться с "иероглифами" вместо кириллицы :)