При компиляции программа на языке Java компилируется в набор классов, которые определены в программе и которые должны располагаться в определенных каталогов в соответствии с их пакетами. Рднако это усложняет распространение программы. И чтобы программу на Java было проще распространять, были разработаны файлы JAR (Java ARchive или АрхивыJava). При этом JAR-файл может содержать как файлы классов, так и другие типы файлов, например, изображения и звуковые файлы, которые могут применяться в программе.
По сути JAR-файл - это способ объединить множество файлов в один, что упрощает их распространение и использование. JAR-файлы часто используются для упаковки библиотек, приложений и плагинов.
JAR-файл использует формат ZIP для организации файлов и подкаталогов и содержит несколько файлов классов и подкаталогов в сжатом формате, что экономит место и повышает производительность. То есть по сути JAR-файл - это zip-архив, и для просмотра его содержимого можно использовать любую утилиту ZIP. Если нам необходимо подключить в проект какую-то внешнюю библиотеку с нужной нам функциональностью, то обычно библиотека предоставляется в виде JAR-файла.
Для работы с JAR-файлами применяется утилита jar, которая устанавливается в рамках JDK и располагается в каталоге jdk/bin. В общем случае эта команда имеет следующий формат:
jar options file1 file2 ... fileN
Команде jar сначала передаются опции (options), которые определяют выполняемые действия. А затем через пробел передаются файлы, над которыми надо произвести действия.
Список применяемых параметров (они аналогичны параметрам команды tar в UNIX):
c: создает новый или пустой архив и добавляет в него файлы. Если какие-либо из указанных имён файлов являются каталогами, программа jar обрабатывает их рекурсивно.
C: временно изменяет каталог. Например, следующая команда изменяет подкаталог classdir для добавления файлов классов
jar cvf jarFileName.jar -C classdir *.class
e: создает точку входа в манифесте.
f: устанавливает имя JAR-файла, которое передается в качестве второго аргумента командной строки.
i: создает индексный файл (для ускорения поиска в большом архиве).
m: добавляет манифест в JAR-файл. У каждого архива есть манифест по умолчанию, но можно установить свой собственный.
M: указывает, что не надо создаваит файл манифеста.
t: отображает оглавление.
u: обновляет существующий JAR-файл.
v: формирует подробный вывод.
x: извлекает файлы. Если указано одно или несколько имён файлов, извлекаются только эти файлы. В противном случае извлекаются все файлы.
0: сохраняет без сжатия ZIP.
Наиболее распространённая команда для создания нового JAR-файла использует следующий синтаксис:
jar cvf jarFileName file1 file2 ... fileN
Например, пусть у нас есть файл Program.java с простеньким кодом:
class Program{
public static void main(String[] args) {
System.out.println("Hello METANIT.COM");
}
}
Сначала скомпилируем файл с помощью компилятора javac:
javac Program.java
В итоге компилируется файл Program.class. Затем создадим из этого класса jar-файл:
jar cvf app.jar Program.class
В данном случае опция "cvf" фактически представляет объединение трех опций - "c", "v", и "f". В качестве имени выходного файла устанавливается "app.jar". Полный вывод команды:
eugene@Eugene:/workspace/java$ jar cvf app.jar Program.class added manifest adding: Program.class(in = 425) (out= 292)(deflated 31%) eugene@Eugene:/workspace/java$
И после выполнения команды в текущей папке появится файл "app.jar".
Стои отметить, что мы можем передать конкретные файлы классов на создание архива:
jar cvf app.jar Program.class Person.class Employee.class
Но если нам надо указать все класса, которые находятся в определенной папке, то мы можем использовать символ подстановки "*":
jar cvf app.jar *.class
В данном случае в jar-архив добавляются все классы, которые находятся в текущей папке. Если классы располагаются в какой-то определенной папке, например, в папке "com/metanit", то надо указать полный путь к классам:
jar cvf app.jar com/metanit/Program.class com/metanit/Person.class com/metanit/Employee.class // или так jar cvf app.jar com/metanit/*.class
Распакуем этот файл с помощью любой программы, которая разархивирует файлы zip. Например, на Linux обычно в системе установлена утилита unzip:
eugene@Eugene:/workspace/java$ unzip -l app.jar
Archive: app.jar
Length Date Time Name
--------- ---------- ----- ----
0 2025-09-19 14:23 META-INF/
62 2025-09-19 14:23 META-INF/MANIFEST.MF
425 2025-09-19 14:21 Program.class
--------- -------
487 3 files
eugene@Eugene:/workspace/java$
С помощью опции -l команде unzip указывается, что надо вывести содержимое архива на консоль. И в данном случае мы видим, что архив уже имеет некоторую структуру:
Папка META-INF
Файл MANIFEST.MF: файл манифеста
Файл Program.class: файл основного класса программы
Таким образом, если мы распакуем jar-файл, то мы увидим, что он также содержит файл манифеста.
Java позволяет напрямую запускать jar-файл как обычную программу. Для этого утилите java передается параметр -jar и путь к jar-архиву. Например, запустим выше созданный файл app.jar:
eugene@Eugene:/workspace/java$ java -jar app.jar no main manifest attribute, in app.jar eugene@Eugene:/workspace/java$
И мы видим, что java не запустила файл, так как в манифестве jar-архива отсутствует атрибут "Main-Class", о чем собственно говорит отображенное на консоли сообщение.
Однако, чтобы сделать jar-файл исполняемым, нам необязательно создавать или изменять манифест, для этого мы можем воспользоваться опцией "e" команды jar. В частности, выполним следующую команду:
jar cvfe app.jar Program Program.class
После названия jar-файла команде передается имя класса без расширения ".class", то есть в нашем случае Program. И таким образом, мы получим исполняемый jar, который мы сможем запустить с помощью
команды java -jar app.jar:
eugene@Eugene:/workspace/java$ java -jar app.jar Hello METANIT.COM eugene@Eugene:/workspace/java$
Стоит отметить, что имя класса указывается с учетом всех пакетов, в которых расположен данный класс.
Например, если бы класс Program располагался бы в пакете "com.metanit" (то есть полное имя класса было бы "com.metanit.Program"), то в качестве главного класса программы указывался бы com.metanit.Program
jar cvfe app.jar com.metanit.Program com/metanit/Program.class
В зависимости от конфигурации операционной системы пользователи могут даже запускать приложение, дважды щелкнув значок JAR-файла.
Например, в Windows установщик Java создаёт ассоциацию файла с расширением ".jar", которая запускает файл с помощью команды javaw -jar.
В отличие от команды java, команда javaw не открывает окно оболочки.
В Mac OS распознается расширение файла «.jar», и при двойном щелчке по JAR-файлу запускается программа Java, которая, в свою очередь, запускает jar-файл.
Манифест представляет описание содержимого архива. У каждого архива есть свой манифест. Можно предоставить свой файл манифеста. Если мы его не предоставим, манифест создается по умолчанию.
Файл манифеста называется MANIFEST.MF и находится в JAR-файле в специальном подкаталоге META-INF. По факту это текстовый файл,
который в самом минимальном виде содержит версию манифеста в виде параметра Manifest-Version:
Manifest-Version: 1.0
Но также могут содержать и большее количество записей. Например, при компиляции в создаваемый манифест также обычно добавляется версия использованного JDK:
Manifest-Version: 1.0 Created-By: 25 (Oracle Corporation)
Записи манифеста группируются по разделам. Первый раздел называется основным - он применяется ко всему файлу. В целом даже для основного раздела имется большее количество атрибутов. Все их можно посмотреть в документации. Отмечу основные из них:
Manifest-Version: Определяет версию файла манифеста. Значение — допустимый номер версии.
Created-By: Определяет версию и вендора реализации Java, на основе которой сгенерирован этот файл манифеста. Этот атрибут генерируется инструментом jar.
Signature-Version: Определяет версию подписи JAR-файла. Значение должно быть допустимой строкой с номером версии.
Class-Path: Значение этого атрибута указывает относительные URL-адреса библиотек, необходимых этому приложению. URL-адреса разделяются одним или несколькими пробелами.
Загрузчик классов приложения использует значение этого атрибута для построения своего внутреннего пути поиска. Подробнее см. в разделе «Атрибут Class-Path».
Multi-Release: Этот атрибут определяет, является ли этот JAR-файл многорелизным. Если значение равно "true", регистр игнорируется, JAR-файл будет обработан средой выполнения Java и инструментами как многорелизный JAR-файл. В противном случае, если значение отлично от "true", этот атрибут игнорируется.
Main-Class: Значение этого атрибута — имя основного класса приложения, который модуль запуска будет загружать при запуске. Имя класса указывается без расширения .class.
Например, атрибут Main-Class применяется для указания главного класса, который должен использоваться для запуска программы. Если мы хотим, чтобы jar-архив был исполняемым, то один из способов решения проблемы - указать этот атрибут в файле манифеста.
Допустим, у нас есть простая программка в файле Program.java:
class Program{
public static void main(String[] args) {
System.out.println("Hello METANIT.COM");
}
}
Скомпилируем эту программу:
javac Program.java
В итоге компилируется файл Program.class.
Затем создадим в той же папке текстовый файл manifest.mf, который будет служить файлом манифеста и который будет иметь следующее содержимое:
Manifest-Version: 1.0 Main-Class: Program
Здесь атрибуту "Main-Class" передаем название класса - Program без расширения .class. Стоит отметить, что имя класса указывается с учетом всех пакетов, в которых расположен данный класс.
Например, если бы класс Program располагался бы в пакете "com.metanit" (то есть полное имя класса было бы "com.metanit.Program"), то указывался бы атрибут Main-Class: com.metanit.Program
Далее создадим из класса Program.class и манифеста manifest.mf jar-файл:
jar cfm app.jar manifest.mf Program.class
В итоге мы получим файл app.jar. И поскольку мы указали в манифесте главный файл программы, мы можем запустить jar-файл на выполнение с помощью следующей команды java -jar app.jar:
eugene@Eugene:/workspace/java$ java -jar app.jar Hello METANIT.COM eugene@Eugene:/workspace/java$
Также отмечу, что мы можем обновить файл манифеста. Чтобы обновить манифест существующего JAR-файла, достаточно поместить дополнения в текстовый файл и использовать следующую команду:
jar ufm app.jar manifest.mf