JavaRush /Java блог /Java Developer /Компиляция в Java
Автор
Aditi Nawghare
Инженер-программист в Siemens

Компиляция в Java

Статья из группы Java Developer
Программирование в IDE — прекрасно: связанность зависимостей кода, удобный дебаг, понятное тестирование, темная тема. Так вот, благодаря IDE разработка развивается семимильными шагами. Но она расслабляет. С каждым днем, погружаясь в функционал IDE, разработчик привыкает к коммиту одной кнопкой или сборке двумя кликами. Компиляция в Java - 1Гораздо хуже обстоит ситуация с новичками в программировании, которые с самого начала работают в IDE, игнорируя работу в командной строке. Например, в Intellij IDEA компиляция Java приложения демонстрируется загрузочным баром в нижней панели, а все параметры компиляции, обработка classpath и прочих прелестей Java-жизни остается за кадром. Предлагаем поговорить о компиляции в Java без IDE. Для запуска примеров в статье следует убедиться, что на вашей машине установлена JDK 1.7 и старше.

Как скомпилировать программу?

Компиляция в программировании — это приведение исходного кода в байт-код для последующего старта программы. Порядок действий от исходного кода до запуска программ выглядит так:
  1. Есть исходный код в файле с именем НазваниеКласса.java;
  2. Если в коде нет ошибок, он компилируется в байт-код (в файл НазваниеКласса.class);
  3. Программа запускается.
Обычно каждая программа содержится в отдельном каталоге. В качестве самого простого примера возьмем вывод в консоль:

class Test {
   public static void main(String[] args) {
      System.out.println("Это говорит приложение из командной строки");
   }
}

Для чего нужна команда javac

Окей, первый пункт выполнен. Идем дальше, чтобы понять: скомпилировать — это как? :) В этом нам поможет команда javac, в аргументе которой необходимо указать нужный файл:

javac Test.java
Если нет ошибок в коде, рядом с файлом Test.java появится файл Test.class. Это и есть скомпилированный байт-код. Теперь его нужно запустить. Здесь используется команда java, запускающая байт-код: Компиляция в Java - 2На скриншоте видно, что в выводе получаем какие-то иероглифы: очевидно, это сбитая кодировка. Как правило это происходит в системе Windows. Для корректного отображения кириллицы в консоли, есть следующие команды:

REM change CHCP to UTF-8
CHCP 65001
CLS
Они меняют текущую кодовую страницу командной консоли на время работы текущего окна. Попробуем еще раз:

D:\Java>java Test
Это говорит приложение из командной строки. Знать принцип работы команды javac очень полезно, так как эта команда лежит в основе любой системы сборки проектов.

Компиляция и выполнение нескольких классов

Для работы с несколькими классами нужен 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 BoxMachine
-classpath — флаг, после которого следует указать местоположение скомпилированных классов. Java будет искать главный класс и все сопутствующие именно в этой директории. ./bin — название папки, в которой находятся скомпилированные классы. BoxMachine — название главного класса. Как и в первом случае, не следует указывать .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 — флаг, после которого следует указать путь к файлу манифеста. manifest.mf — путь к манифесту. box-machine.jar — название выходного jar-файла. — флаг, после которого указывается путь к скомпилированным классам. . — путь, куда будет помещен jar-файл. В нашем случае —это главный каталог. Теперь можно запустить. Запуск 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: обзор систем сборок

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

Как скомпилировать Java?

Самые известные системы сборки на Java — это Ant, Maven и 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 доступных команд, которые указаны в документации. В примере ниже используем только 5:
  • 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="BoxMachine" classpath="result/classes"/>
   </target>
   <target name="clean">
       <delete dir="result"/>
   </target>
</project>
В сценарии описано три действия — compile, ,code>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 придерживаются такой структуры проекта: Компиляция в Java - 3Правила сборки, зависимости и прочее описывается в файле 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 он называется build.gradle и выглядит гораздо меньше, чем у Maven. Пример для наших классов:

apply plugin: 'java'
apply plugin: 'application'

sourceSets {
   main {
       java {
           srcDirs 'src'
       }
   }
}
sourceSets.main.output.classesDir = file("bin")

mainClassName = "src.BoxMachine"

defaultTasks 'compileJava', 'run'
В файле происходит подключение плагинов, определение директории файлов исходного кода (если не используется структура проектов Maven), директория результатов сборки, имя главного класса, а также задачи по умолчанию. За запуск сборки отвечает команда 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

Заключение

На первый взгляд, умение компиляции и сборки кода без IDE кажется бесполезным. Действительно, зачем мучиться с командными строками и гуглить все команды, когда есть уютная IDEшечка с плагинами, автопроверкой всего что можно (современные IDE разве что уровень IQ не проверяют) и интеграцией с популярными системами. Однако практика показывает, что умение собирать код без среды разработки и понимание каждого шага этого процесса — суровая необходимость. Этот навык сэкономит немало нервных клеток и времени вам и вашей компании. Освоить работу в IDE, попрактиковаться в написании кода и, конечно же, получить фундаментальные основы по программированию на Java вы можете здесь — на JavaRush. Пора вернуться к обучению :)
Комментарии (21)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Anonymous #2053422 Уровень 16
24 января 2024
Есть файл nm.java в папке nm В файле нет строки package. Компилируется командой javac nm.java и запускается командой java nm Файл класса при этом появляется в папке nm рядом с другими файлами. Добавляю в файл строку package nm;. компилирую командой javac nm.java Пытаюсь запустить через java nm и получаю: Error: Could not find or load main class nm. Caused by: java.lang.NoClassDefFoundError: nm (wrong name: nm/nm) Почему, чёрт его дери?
Anton-V-K Уровень 4
27 ноября 2022
Кстати , CHCP 1251 тоже иногда помогает справиться с "иероглифами" вместо кириллицы :)
Maria-Lipina Уровень 1
14 октября 2022
Статья помогла понять самые основы, что происходит при сборке программы (и почему у меня постоянно ломалось). Но! Мне как новичку пришлось гуглить ещё 100500 статей как с JavaRush так и с других сайтов, чтобы выполнить инструкции по компиляции и сборке через Maven (Ant и Gradle не пробовала). Основные замечания: 1) Здесь опечатка в команде java -classpath ./bin BoxMachine. Правильно указать, что выполняется пакет : java -classpath ./bin src.BoxMachine 2) Не хватает краткого прямого указания, в каком редакторе можно составлять и редактировать файлы (тот же NotePad, я вообще делала в терминальном редакторе nano) - вроде бы и понятно, но все-таки. Для полноты текста и возможно у автора будет для новичков полезный совет. 3) В каком редакторе можно посмотреть байт-код? Для образовательных целей. Раз залезаем под капот и разбираем по кирпичикам, то почему бы и нет? Jetbrains декомпилирует обратно и это уже не так интересно 4) ! Для выполнения инструкций в этой статье (чтобы по чесноку, без IDE) Ant, Maven, Gradle должны устанавливаться и настраиваться отдельно. Не хватает хотя бы прямого указания на этот факт и ссылки на статью с инструкцией установки 5) pom.xml, который предлагает статья - неполный. Вот совсем. Мне пришлось добавлять properties и то проект скомпилировался, но правильно в jar не собрался.
Psiho5at Уровень 2
5 сентября 2022
Доброго времени суток, комрады! Возник вопрос выполнение программы происходит через следующую команду: "java hello.java", Когда пишу "java hello", получаю сообщение "Error: Could not find or load main class hello" "Caused by: java.lang.ClassNotFoundException: hello"
Anonymous #2372013 Уровень 26
27 февраля 2021
Напишите пожалуйста статью, как компилировать Java - программы в терминале Linux.
Дмитрий Уровень 29
4 июня 2020
Стать я так хорошо началась, а на манифестах пошла каша-малаша
Смол Уровень 19
11 апреля 2020
Интересно было попробовать, но у меня не сработало. Кажется, пару моментов упущено в описании. Вот здесь более основательно будет. https://habr.com/ru/post/125210/
25 октября 2019
Добрый день. Вопрос по кодировке utf8. 1) устанавливаю кодовую страницу "chcp 65001" 2) Пишу в кодировке utf-8 следующий класс public class WordI { public static void main (String[] args) { System.out.println("буква И"); } } 3) компиляция происходит с ошибкой. По умолчанию компилятор всё равно пытается сделать в кириллице(cp1251). При этом у него возникает проблема с буквой "И". >javac WordI.java WordI.java:3: error: unmappable character for encoding Cp1251 System.out.println("буква ?"); ^ 1 error 4) Если компилировать так: javac -encoding UTF-8 WordI.java , то компиляция проходит успешно, но русские символы просто не печатаются: вызов >java WordI даёт на выходе только " "(пробел). Помогите разобраться, где можно ещё настроить компилятор, чтобы вывод в кодировке utf-8 был корректен?
Денис Уровень 37
18 июля 2019
Очень интересная статья, как раз недавно интересовался системой сборки Maven потому как у моего "инструмента" появилась первая зависимость в виде JDBC, а Мавен очень удобно это умеет подтягивать. Интересно было бы подобное по git почитать, например Best Practices в составлении проекта, правильный gitignore и т.д.