Время на прочтение
11 мин
Количество просмотров 617K
Сейчас уже никто не создает программы в консоли. Используя любимую IDE, разработчик чувствует себя неуютно за чужим компьютером, где её нет.
Решив разобраться в работе Ant и Maven, я поймал себя на том, что не смогу собрать приложение без них в консоли.
В данной статье я постарался уместить все этапы проектирования демонстрационного приложения, чтобы не искать справку по каждой команде на просторах Интернета.
От простого к …
Каждая программа обычно содержится в отдельном каталоге. Я придерживаюсь правила создавать в этом каталоге по крайней мере две папки: src и bin. В первой содержатся исходные коды, во второй — результат компиляции. В данных папках будет структура каталогов, зависящая от пакетов.
Один файл
Можно сделать и без лишних папок.
Берем сам файл HelloWorld.java.
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
Переходим в каталог, где лежит данный файл, и выполняем команды.
javac HelloWorld.java
В данной папке появится файл HelloWorld.class. Значит программа скомпилирована. Чтобы запустить
java -classpath . HelloWorld
Отделяем бинарные файлы от исходников
Теперь сделаем тоже самое, но с каталогами. Создадим каталог HelloWorld и в нем две папки src и bin.
Компилируем
javac -d bin src/HelloWorld.java
Здесь мы указали, что бинарные файлы будут сохраняться в отдельную папку bin и не путаться с исходниками.
Запускаем
java -classpath ./bin HelloWorld
Используем пакеты
А то, вдруг, программа перестанет быть просто HelloWorld-ом. Пакетам лучше давать понятное и уникальное имя. Это позволит добавить данную программу в другой проект без конфликта имен. Прочитав некоторые статьи, можно подумать, что для имени пакета обязательно нужен домен. Это не так. Домены — это удобный способ добиться уникальности. Если своего домена нет, воспользуйтесь аккаунтом на сайте (например, ru.habrahabr.mylogin). Он будет уникальным. Учтите, что имена пакетов должны быть в нижнем регистре. И избегайте использования спецсимволов. Проблемы возникают из-за разных платформ и файловых систем.
Поместим наш класс в пакет с именем com.qwertovsky.helloworld. Для этого добавим в начало файла строчку
package com.qwertovsky.helloworld;
В каталоге src создадим дополнительные каталоги, чтобы путь к файлу выглядел так: src/com/qwertovsky/helloworld/HelloWorld.java.
Компилируем
javac -d bin src/com/qwertovsky/helloworld/HelloWorld.java
В каталоге bin автоматически создастся структура каталогов как и в src.
HelloWorld
'---bin
' '---com
' '---qwertovsky
' '---helloworld
' '---HelloWorld.class
'---src
'---com
'---qwertovsky
'---helloworld
'---HelloWorld.java
Запускаем
java -classpath ./bin com.qwertovsky.helloworld.HelloWorld
Если в программе несколько файлов
Изменим программу.
HelloWorld.java
package com.qwertovsky.helloworld;
public class HelloWorld
{
public static void main(String[] args)
{
int a=2;
int b=3;
Calculator calc=new Calculator();
System.out.println("Hello World!");
System.out.println(a+"+"+b+"="+calc.sum(a,b));
}
}
Calculator.java
package com.qwertovsky.helloworld;
import com.qwertovsky.helloworld.operation.Adder;
public class Calculator
{
public int sum(int... a)
{
Adder adder=new Adder();
for(int i:a)
{
adder.add(i);
}
return adder.getSum();
}
}
Adder.java
package com.qwertovsky.helloworld.operation;
public class Adder
{
private int sum;
public Adder()
{
sum=0;
}
public Adder(int a)
{
this.sum=a;
}
public void add(int b)
{
sum+=b;
}
public int getSum()
{
return sum;
}
}
Компилируем
javac -d bin src/com/qwertovsky/helloworld/HelloWorld.java
srccomqwertovskyhelloworldHelloWorld.java:9: cannot find symbol
symbol : class Calculator
location: class com.qwertovsky.helloworld.HelloWorld
Calculator calc=new Calculator();
^
srccomqwertovskyhelloworldHelloWorld.java:9: cannot find symbol
symbol : class Calculator
location: class com.qwertovsky.helloworld.HelloWorld
Calculator calc=new Calculator();
^
2 errors
Ошибка возникла из-за того, что для компиляции нужны файлы с исходными кодами классов, которые используются (класс Calculator). Надо указать компилятору каталог с файлами с помощью ключа -sourcepath.
Компилируем
javac -sourcepath ./src -d bin src/com/qwertovsky/helloworld/HelloWorld.java
Запускаем
java -classpath ./bin com.qwertovsky.helloworld.HelloWorld
Hello Word
2+3=5
Если удивляет результат
Есть возможность запустить отладчик. Для этого существует jdb.
Сначала компилируем с ключом -g, чтобы у отладчика была информация.
javac -g -sourcepath ./src -d bin src/com/qwertovsky/helloworld/HelloWorld.java
Запускаем отладчик
jdb -classpath bin -sourcepath src com.qwertovsky.helloworld.HelloWorld
Initializing jdb ...
>
Отладчик запускает свой внутренний терминал для ввода команд. Справку по последним можно вывести с помощью команды help.
Указываем точку прерывания на 9 строке в классе Calculator
> stop at com.qwertovsky.helloworld.Calculator:9
Deferring breakpoint com.qwertovsky.helloworld.Calculator:9.
It will be set after the class is loaded.
Запускаем на выполнение.
> run
run com.qwertovsky.helloworld.HelloWorld
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
>
VM Started: Set deferred breakpoint com.qwertovsky.helloworld.Calculator:9
Hello World!
Breakpoint hit: "thread=main", com.qwertovsky.helloworld.Calculator.sum(), line=9 bci=0
9 Adder adder=new Adder();
Чтобы соориентироваться можно вывести кусок исходного кода, где в данный момент находится курссор.
main[1] list
5 public class Calculator
6 {
7 public int sum(int... a)
8 {
9 => Adder adder=new Adder();
10 for(int i:a)
11 {
12 adder.add(i);
13 }
14 return adder.getSum();
Узнаем, что из себя представляет переменная а.
main[1] print a
a = instance of int[2] (id=340)
main[1] dump a
a = {
2, 3
}
main[1] stop at com.qwertovsky.helloworld.operation.Adder:19
Deferring breakpoint com.qwertovsky.helloworld.operation.Adder:19.
It will be set after the class is loaded.
Продолжим исполнение.
main[1] cont
> Set deferred breakpoint com.qwertovsky.helloworld.operation.Adder:19
Breakpoint hit: "thread=main", com.qwertovsky.helloworld.operation.Adder.add(), line=19 bci=0
19 sum+=b;
main[1] list
15 }
16
17 public void add(int b)
18 {
19 => sum+=b;
20 }
21
22 public int getSum()
23 {
24 return sum;
main[1] print sum
sum = 0
main[1] print b
b = 2
Выполним код в текущей строке и увидим, что sum стала равняться 2.
main[1] step
>
Step completed: "thread=main", com.qwertovsky.helloworld.operation.Adder.add(), line=20 bci=10
20 }
main[1] print sum
sum = 2
Поднимемся из класса Adder в вызвавший его класс Calculator.
main[1] step up
>
Step completed: "thread=main", com.qwertovsky.helloworld.Calculator.sum(), line=10 bci=36
10 for(int i:a)
Удаляем точку прерывания
main[1] clear com.qwertovsky.helloworld.operation.Adder:19
Removed: breakpoint com.qwertovsky.helloworld.operation.Adder:19
main[1] step
>
Step completed: "thread=main", com.qwertovsky.helloworld.Calculator.sum(), line=12 bci=30
12 adder.add(i);
Можно избежать захода в методы, используя команду next.
main[1] next
>
Step completed: "thread=main", com.qwertovsky.helloworld.Calculator.sum(), line=10 bci=36
10 for(int i:a)
main[1] next
>
Step completed: "thread=main", com.qwertovsky.helloworld.Calculator.sum(), line=14 bci=42
14 return adder.getSum();
Проверяем значение выражения и завершаем выполнение.
main[1] eval adder.getSum()
adder.getSum() = 5
main[1] cont
> 2+3=5
The application exited
Хорошо бы протестировать
Используем JUnit.
package com.qwertovsky.helloworld;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized.Parameters;
@RunWith(value=org.junit.runners.Parameterized.class)
public class TestCalculator
{
int expected;
int[] arg;
@Parameters
public static Collection<int[][]> parameters()
{
return Arrays.asList(new int[][][]{
{{4}, {2, 2}}
,{{-1},{4, -5}}
,{{0},{0,0,0}}
,{{0},{}}
});
}
public TestCalculator(int[] expected, int[] arg)
{
this.expected=expected[0];
this.arg=arg;
}
@Test
public void testSum()
{
Calculator c=new Calculator();
assertEquals(expected,c.sum(arg));
}
}
Компилируем
mkdir test_bin
javac -classpath lib/path/junit-4.8.2.jar -sourcepath ./src -d test_bin test/com/qwertovsky/helloworld/TestCalculator.java
Запускаем. В качестве разделителя нескольких путей в classpath в Windows используется ‘;’, в Linux — ‘:’. В консоли Cygwin не работают оба разделителя. Возможно, должен работать ‘;’, но он воспринимается как разделитель команд.
java -classpath lib/path/junit-4.8.2.jar:./test_bin org.junit.runner.JUnitCore com.qwertovsky.helloworld.TestCalculator
JUnit version 4.8.2
....
Time: 0,031
OK (4 tests)
Создадим библиотеку
Класс Calculator оказался полезным и может быть использован во многих проектах. Перенесем всё, что касается класса Calculator в отдельный проект.
HelloWorld
'---bin
'---src
'---com
'---qwertovsky
'---helloworld
'---HelloWorld.java
Сalculator
'---bin
'---src
' '---com
' '---qwertovsky
' '---calculator
' '---Calculator.java
' '---operation
' '---Adder.java
'---test
'---com
'---qwertovsky
'---calculator
'---TestCalculator.java
Измените также назавания пакетов в исходных текстах. В HelloWorld.java нужно будет добавить строку
import com.qwertovsky.calculator.Calculator;
Компилируем.
cd Calculator
javac -sourcepath src -d bin src/com/qwertovsky/calculator/Calculator.java
Делаем архив jar
jar cvf calculator.jar -C bin .
added manifest
adding: com/(in = 0) (out= 0)(stored 0%)
adding: com/qwertovsky/(in = 0) (out= 0)(stored 0%)
adding: com/qwertovsky/calculator/(in = 0) (out= 0)(stored 0%)
adding: com/qwertovsky/calculator/Calculator.class(in = 497) (out= 373)(deflated 24%)
adding: com/qwertovsky/calculator/operation/(in = 0) (out= 0)(stored 0%)
adding: com/qwertovsky/calculator/operation/Adder.class(in = 441) (out= 299)(deflated 32%)
С помощью ключа -C мы запустили программу в каталоге bin.
Надо узнать, что у библиотеки внутри
Можно распаковать архив zip-распаковщиком и посмотреть, какие классы есть в библиотеке.
Информацию о любом классе можно получить с помощью дизассемблера javap.
javap -c -classpath calculator.jar com.qwertovsky.calculator.Calculator
Compiled from "Calculator.java"
public class com.qwertovsky.calculator.Calculator extends java.lang.Object{
public com.qwertovsky.calculator.Calculator();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public int sum(int[]);
Code:
0: new #2; //class com/qwertovsky/calculator/operation/Adder
3: dup
4: invokespecial #3; //Method com/qwertovsky/calculator/operation/Adder."<init>":()V
7: astore_2
8: aload_1
9: astore_3
10: aload_3
11: arraylength
12: istore 4
14: iconst_0
15: istore 5
17: iload 5
19: iload 4
21: if_icmpge 42
24: aload_3
25: iload 5
27: iaload
28: istore 6
30: aload_2
31: iload 6
33: invokevirtual #4; //Method com/qwertovsky/calculator/operation/Adder.add:(I)V
36: iinc 5, 1
39: goto 17
42: aload_2
43: invokevirtual #5; //Method com/qwertovsky/calculator/operation/Adder.getSum:()I
46: ireturn
}
Из результата видно, что класс содержит кроме пустого конструктора, ещё один метод sum, внутри которого в цикле вызывается метод add класса Adder. По завершении метода sum, вызывается Adder.getSum().
Без ключа -c программа выдаст только список переменных и методов (если использовать -private, то всех).
javap -private -classpath calculator.jar com.qwertovsky.calculator.operation.Adder
Compiled from "Adder.java"
public class com.qwertovsky.calculator.operation.Adder extends java.lang.Object{
private int sum;
public com.qwertovsky.calculator.operation.Adder();
public com.qwertovsky.calculator.operation.Adder(int);
public void add(int);
public int getSum();
}
Лучше снабдить библиотеку документацией
Изменим для этого класс калькулятора.
package com.qwertovsky.calculator;
import com.qwertovsky.calculator.operation.Adder;
/**
* Калькулятор, который умеет складывать
* @author Qwertovsky
*
*/
public class Calculator
{
/**
* Определение суммы слагаемых
* @param a массив слагаемых
* @return сумма
*/
public int sum(int... a)
{
Adder adder=new Adder();
for(int i:a)
{
adder.add(i);
}
return adder.getSum();
}
}
Документацию можно создать следующей командой. При ошибке программа выдаст список возможных опций.
mkdir doc
javadoc -d doc -charset utf-8 -sourcepath src -author -subpackages com.qwertovsky.calculator
В результате получиться следующее
Можно подписать jar-архив
Если требуется подписать свою библиотеку цифровой подписью, на помощь придут keytool и jarsigner.
Генерируем подпись.
keytool -genkey -keyalg rsa -keysize 2048 -alias qwertokey -keystore path/to/qwerto.keystore
Enter keystore password:
Re-enter new password:
What is your first and last name?
[Unknown]: Valery Qwertovsky
What is the name of your organizational unit?
[Unknown]: Qwertovsky
What is the name of your organization?
[Unknown]: Qwertovsky
What is the name of your City or Locality?
[Unknown]: Tver
What is the name of your State or Province?
[Unknown]: Tverskaya obl.
What is the two-letter country code for this unit?
[Unknown]: RU
Is CN=Valery Qwertovsky, OU=Qwertovsky, O=Qwertovsky, L=Tver, ST=Tverskaya obl., C=RU correct?
[no]: y
Enter key password for <qwertokey>
(RETURN if same as keystore password):
Re-enter new password:
Генерируем Certificate Signing Request (CSR)
keytool -certreq -file path/to/qwertokey.crt -alias qwertokey -keystore path/to/qwerto.keystore
Содержимое полученного файла отправляем в центр сертификации. От центра сертификации получаем сертификат. Сохраняем его в файле (например, qwertokey.cer) и импортируем в хранилище
keytool -import -trustcacerts -keystore path/to/qwert.keystore -alias qwertokey -file path/to/qwertokey.cer
Подписываем jar-архив
jarsigner -keystore path/to/qwerto.keystore calculator.jar qwertokey
Файл qwertokey.cer отправляем всем, кто хочет проверить архив. Проверяется он так
jarsigner -verify -verbose -certs -keystore path/to/qwerto.keystore calculator.jar
Использование библиотеки
Есть программа HelloWorld, которая использует библиотечный класс Calculator. Чтобы скомпилировать и запустить программу, нужно присоединить библиотеку.
Компилируем
cd HelloWorld
javac -sourcepath src -d bin -classpath path/to/calculator.jar src/com/qwertovsky/helloworld/HelloWorld.java
Запускаем
java -classpath bin:path/to/calculator.jar com.qwertovsky.helloworld.HelloWorld
Собираем программу
Это можно сделать по-разному.
Первый способ
cd HelloWorld
echo main-class: com.qwertovsky.helloworld.HelloWorld>manifest.mf
echo class-path: lib/calculator.jar >>manifest.mf
mkdir lib
cp path/to/calculator.jar lib/calculator.jar
jar -cmf manifest.mf helloworld.jar -C bin .
Здесь есть тонкости.
В строке
main-class: com.qwertovsky.helloworld.HelloWorld
не должно быть пробелов в конце.
Вторая тонкость описана в [3]: в этой же строке должен стоять перенос на следующую строку. Это если манифест помещается в архив сторонним архиватором.
Программа jar не включит в манифест последнюю строку из манифеста, если в конце не стоит перенос строки.
Ещё момент: в манифесте не должно быть пустых строк между строками. Будет выдана ошибка «java.io.IOException: invalid manifest format».
При использовании команды echo надо следить только за пробелом в конце строки с main-class.
Второй способ
cd HelloWorld
echo class-path: lib/calculator.jar >manifest.mf
mkdir lib
cp path/to/calculator.jar lib/calculator.jar
jar -cmef manifest.mf com.qwertovsky.helloworld.HelloWorld helloworld.jar -C bin .
В данном способе избегаем ошибки с пробелом в main-class.
Третий способ
cd HelloWorld
mkdir lib
cd lib
jar -xvf path/to/calculator.jar com/
created: com/
created: com/qwertovsky/
created: com/qwertovsky/calculator/
inflated: com/qwertovsky/calculator/Calculator.class
created: com/qwertovsky/calculator/operation/
inflated: com/qwertovsky/calculator/operation/Adder.class
cd ..
cp -r bin/* lib/
jar -cef com.qwertovsky.helloworld.HelloWorld helloworld.jar -C lib .
rm -r lib
Включили код нужной библиотеки в исполняемый файл.
Запуск исполняемого jar-файла
Файл calculator.jar исполняемым не является. А вот helloworld.jar можно запустить.
Если архив был создан первыми двумя способами, то рядом с ним в одном каталоге должна находится папка lib с файлом calculator.jar. Такие ограничения из-за того, что в манифесте в class-path указан путь относительно исполняемого файла.
cd Calculator
ls ../HelloWorld/lib
calculator.jar
java -jar ../HelloWorld/helloworld.jar
При использовании третьего способа нужные библиотеки включаются в исполняемый файл. Держать рядом нужные библиотеки не требуется. Запускается аналогично.
java -jar ../HelloWorld/helloworld.jar
Как быть с приложениями JavaEE
Аналогично. Только библиотеки для компиляции нужно брать у сервера приложений, который используется. Если я использую JBoss, то для компиляции сервлета мне нужно будет выполнить примерно следующее
javac -classpath path/to/jboss/common/lib/jboss-servlet*.jar -d ./classes src/com/qwertovsky/app/servlets/MenuSt.java
Структура архива JavaEE-приложения должна соответствовать определенному формату. Например
my.ear
`---META-INF
| `---manifest.mf
`---lib
| `---mylib.jar
`---my.war
| `---META-INF
| | `---manifest.mf
| `---WEB-INF
| | `---lib
| | | `---myweblib.jar
| | `---classes
| | | `---com
| | | `---...
| | `---web.xml
| `---index.html
| `---<остальное веб-содержимое (страницы, изображения)>
`---myejb.jar
Способы запуска приложения на самом сервере с помощью командной строки для каждого сервера различны.
Надеюсь, данная статья станет для кого-нибудь шпаргалкой для работы с Java в командной строке. Данные навыки помогут понять содержание и смысл Ant-скриптов и ответить на собеседовании на более каверзные вопросы, чем «Какая IDE Вам больше нравится?».
Ещё почитать
1. Elliotte Rusty Harold. «Рекомендации по управлению classpath в UNIX и Mac OS X»
2. Elliotte Rusty Harold. «Рекомендации по управлению classpath в Windows»
3. Евгений Матюшкин aka Skipy. «Ликбез»
4. Lesson: Packaging Programs in JAR Files
5. Brian Goetz. «Теория и практика Java: Мне нужно задокументировать ЭТО?»
6. Евгений Матюшкин aka Skipy. «Создание собственных тегов javadoc»
7. Создание и использование архивов Java
8. Sun Java Signing
9. javac — Java programming language compiler
10. java — the Java application launcher
11. jdb — The Java Debugger
12. javap — The Java Class File Disassembler
13. javadoc — The Java API Documentation Generator
14. jarsigner — JAR Signing and Verification Tool
15. jar — The Java Archive Tool
16. keytool — Key and Certificate Management Tool
Загрузить PDF
Загрузить PDF
Из этой статьи вы узнаете, как создать Java-архив, а именно JAR-файл, с помощью бесплатной Java-программы Eclipse. Это можно сделать на компьютере с Windows и с Mac OS X.
-
1
Удостоверьтесь, что у вас установлен комплект разработчика Java-приложений (JDK). Если на компьютере JDK нет, скачайте и установите его.
-
2
Откройте веб-сайт программы Eclipse. Перейдите на страницу https://www.eclipse.org/.
- Если программа Eclipse уже установлена, перейдите в следующий раздел.
-
3
Щелкните по DOWNLOAD (Скачать). Это кнопка в верхней части страницы.
-
4
Щелкните по DOWNLOAD (Скачать). Эта оранжевая кнопка находится в верхней части открывшейся страницы. Начнется загрузка Eclipse на компьютер.
-
5
Дождитесь, когда загрузка будет завершена. На это уйдет некоторое время.
-
6
Запустите установочный файл Eclipse.
- В Windows дважды щелкните по скачанному файлу, а затем нажмите «Да», когда появится запрос.
- В Mac OS X дважды щелкните по скачанному архиву (ZIP-файлу) и дождитесь, когда он распакуется.
-
7
Откройте окно мастера установки Eclipse. Для этого дважды щелкните по фиолетовому значку Eclipse, когда он появится.
- Если вы щелкнули по ссылке «Change Mirror» (Поменять сервер), которая находится под оранжевой кнопкой «DOWNLOAD» (Скачать), возможно, откроется окно самой программы Eclipse. В этом случае перейдите к шагу «Нажмите “Обзор”».
-
8
Нажмите Eclipse IDE for Java Developers (Eclipse IDE для разработчиков Java). Это верхняя опция в окне мастера установки Eclipse.
-
9
Щелкните по Install (Установить). Эта желтая кнопка находится в нижней части окна. Начнется установка Eclipse на компьютер.
- Если хотите, укажите папку, в которую будет установлена программа; для этого щелкните по значку в виде папки справа от опции «Installation Folder» (Папка для установки), а затем выберите конкретную папку на компьютере.
- Если вы не хотите создавать ярлыки для Eclipse, сначала снимите флажки у соответствующих опций, а потом нажмите «Установить».
-
10
Щелкните по Launch (Запустить). Эта зеленая кнопка находится в нижней части окна мастера установки. Откроется программа Eclipse.
-
11
Нажмите Browse (Обзор). Эта опция находится в правой части окна. Откроется окно Проводника (в Windows) или Finder (в Mac OS X).
-
12
Выберите папку рабочей области. Щелкните по нужной папке в левой части экрана. В этой папке будут храниться файлы проектов Eclipse.
-
13
Щелкните по OK. Эта кнопка находится в нижнем правом углу окна.
-
14
Нажмите Launch (Запустить). Вы найдете эту опцию в нижней части окна. Откроется главная страница Eclipse.
Реклама
-
1
Скопируйте нужные файлы в одну папку. Файлы, которые будут помещены в JAR-файл, должны быть в одной папке.
-
2
Щелкните по Файл. Это меню находится в верхнем левом углу окна (Windows) или в верхнем левом углу экрана (Mac OS X). Откроется выпадающее меню.
-
3
Щелкните по Открыть файл. Эта опция находится в верхней части выпадающего меню.
-
4
Откройте папку с файлами. Найдите папку с нужными файлами, а затем дважды щелкните по папке, чтобы открыть ее. На экране отобразится список файлов.
-
5
Выберите все файлы в папке. Щелкните по любому файлу, а затем нажмите Ctrl+A (Windows) или ⌘ Command+A (Mac OS X), чтобы выбрать все файлы в папке.
-
6
Нажмите Открыть. Эта кнопка находится в нижнем правом углу окна. Файлы откроются в Eclipse.
-
7
Щелкните по Файл. Откроется выпадающее меню.
-
8
Щелкните по Экспортировать. Эта опция находится в нижней части выпадающего меню.
-
9
Дважды щелкните по Java. Эта опция находится в верхней части страницы.
-
10
Нажмите JAR-файл. Вы найдете эту опцию под опцией «Java», по которой вы дважды щелкнули.
- Если вы хотите, чтобы JAR-файл запускался как программа, выберите опцию «Исполняемый JAR-файл».
-
11
Щелкните по Далее. Эта кнопка находится в нижней части окна.
-
12
Выберите ресурсы для экспорта. Поставьте флажки у неотмеченных ресурсов, которые вы хотите добавить в свой JAR-файл. Сделайте это в окне в верхней части страницы.
- Пропустите этот шаг, если вы решили создать исполняемый JAR-файл.
-
13
Выберите папку для хранения JAR-файла. Нажмите «Обзор», введите имя файла, щелкните по папке, в которой JAR-файл будет храниться, а затем нажмите «Сохранить».
-
14
Нажмите Завершить. Будет создан JAR-файл (на сжатие файлов уйдет некоторое время).
Реклама
Об этой статье
Эту страницу просматривали 49 205 раз.
Была ли эта статья полезной?
1. Инструментальные средства разработки
Методические указания
Платформа разработки JDK
Java Development Kit (JDK) — комплект инструментальных средств для разработки приложений на языке Java. Включает компилятор javac, утилиты, библиотеки классов Java API, документацию и примеры, среду исполнения приложений — Java Runtime Environment (JRE).
JRE включает виртуальную машину Java Virtual Machine (VM) и откомпилированные библиотеки классов Java API. Позволяет запускать Java приложения на любых устройствах или операционных системах.
Существует три редакции JDK:
- SE (Standard Edition) — стандартная редакция JDK, которая используется для разработки консольных и настольных приложений.
- EE (Enterprise Edition) — редакция для разработки распределенных приложений масштаба предприятий.
- ME (Micro Edition) — редакция для разработки приложений мобильных устройств и встраиваемых систем.
Интегрированная среда разработки
Integrated Development Environment (IDE) — комплекс программных средств, используемый программистами для разработки программного обеспечения (ПО), в том числе, текстовый редактор, средства автоматизации сборки, отладчик.
Существует несколько популярных IDE для разработки Java приложений:
- IntelliJ IDEA
- Eclipse
- NetBeans
Для выполнение заданий необходимо установить следующие инструментальные средства:
- Oracle JDK SE или OpenJDK (версию 8 или выше).
- IntelliJ IDEA Community Edition
Main-класс
Main-класс — класс, в котором есть main-метод. Main-метод запускает поток исполнения приложения, вызывается вирутальной машиной Java VM:
public class MyMainClass { // main-метод public static void main(String[] args) { // инструкции } }
Вывод текста в командную строку осуществляется через встроенный поток вывода:
public class MyMainClass { // main-метод public static void main(String[] args) { // Вывести сообщение "my text" в командную строку System.out.println("my text"); } }
Создание, сборка и запуск проекта
Создать новый проект
Запустить «IntelliJ IDEA». Если открыт приветственный экран «Welcome to IntelliJ IDEA», нажать кнопку «New Project». Если открыто основное окно, выбрать пункт меню «File | New | Project».
Выбрать пункт «New Project» из списка слева. Задать имя нового проекта «Name» и изменить его месторасположение «Location», если необходимо. В списке «Languages» должен быть выбран язык «Java». В списке «JDK» должна быть выбрана JDK версии 8 или выше.
Создать пакет и класс
В окне «Project» щелкнуть правой кнопкой мыши папку «src», выбрать пункт «New | Java Class» (Alt+Insert).
Задать полное имя класса следующим образом: «ru.isu.math.example.MyMainClass» и нажать кнопку «OK». В результате будет создан пакет «ru.isu.math.example» и класс «MyMainClass» внутри этого пакета.
package ru.isu.math.example; public class MyMainClass { }
Написать код
Добавить в созданный класс main-метод с необходимыми инструкциями.
package ru.isu.math.example; public class MyMainClass { public static void main(String[] args) { System.out.println("Hello World!"); } }
Собрать и запустить приложение
Выбрать пункт меню «Run | Run…» или нажать кнопку «Run» на панели инструментов сверху — «зеленый треугольник» (Alt+Shift+F10). В раскрывшемся списке выбрать «MyMainClass».
Запустится компиляция исходного кода программы в байт-код и затем исполнение приложения в Java VM. Результат выполнения программы, сообщение «Hello World!», будет выводиться во встроенной командной строке.
Компиляция и запуск из командной строки
Для того чтобы откомпилировать класс из командной строки, необходимо перейти в директорию, в которой находится файл с исходным кодом:
cd <path to the scr directory>
запустить компилятор javac, указав в качестве параметра название файла с исходным кодом с расширением:
javac -d target ru/isu/math/example/MyMainClass.java
В результате компилятор создаст файл с байт-кодом с тем же именем класса, но расширением «class» — «MyMainClass.class» в папке «target/ru/isu/math/example».
Для того чтобы исполнить откомпилированный класс необходимо перейти в директорию, в которой находится файл с байт-кодом:
cd <path to the target directory>
и запустить Java VM, указав в качестве параметра название файла с байт-кодом без расширения:
java ru.isu.math.example.MyMainClass
Когда переменная среды JAVA_HOME не настроена, следует указывать полный путь к компилятору javac и виртуальной машине java соответственно:
<path to JDK>/bin/javac -d target ru/isu/math/example/MyMainClass.java <path to JDK>/bin/java ru.isu.math.example.MyMainClass
Дизассемблирование байт-кода
Для того чтобы дизассемблировать откомпилированный класс необходимо перейти в директорию, в которой находится файл с байт-кодом:
cd <path to the target directory>
запустить дизассемблер javap, указав в качестве параметра название файла с байт-кодом:
javap -c ru/isu/math/example/MyMainClass.class
Когда переменная среды JAVA_HOME не настроена, следует указывать полный путь к дизассемблеру javap:
<path to JDK>/bin/javap -c ru/isu/math/example/MyMainClass.class
Результат дизассемблирования байт-кода выводится в командную строку.
Инструкции и блоки кода
Инструкция кода — команда для выполнения в Java VM. Инструкция заканчивается точкой с запятой ;
.
// Объявить и инициализировать переменную целочисленного типа int x = 1;
Блок кода — набор инструкций, заключенный в пару симметричных фигурных скобок.
{ int x = 1; x = x + 2; }
Локальные переменные
Локальная переменная — это переменная, объявленная внутри блока кода. Область видимости такой переменной ограничена блоком кода, в котором она объявлена.
public static void main(String[] args) { int x; // Объявить локальную переменную x = 1; // Присвоить значение локальной переменной System.out.println("x = " + x); // Вывести значение переменной }
При объявлении переменной необходимо указать ее тип данных. Например, если переменная имеет тип int
, то в качестве своих значений она может принимать только целые числа:
Переменная, объявленная с типом double
, содержит только вещественные числа:
Переменная с типом String, будет ссылаться на строки
Цикл for
Цикл — управляющая инструкция, которая определяет выполнение сопряженного блока кода (тела цикла) множество раз.
Цикл for имеет следующий синтаксис:
for (инициализация счетчика; условие завершения; итерация) блок кода
Например, следующий цикл выполняет инструкции тела десять раз:
for (int i = 0; i < 10; i++) { System.out.println("i = " + i); }
Аргументы командной строки
Аргументы командной строки передаются приложению в виде единственного параметра main-метода — массива строк:
public class MyMainClass { public static void main(String[] args) { // Вывести аргументы командной строки for (int i = 0; i < args.length; i++) { String arg = args[i]; System.out.printf("Argument %d: %s%n", i, arg); } } }
При запуске приложения из командной строки аргументы указываются после имени main-класса:
java ru.isu.math.example.MyMainClass p0 p1 p2
Для того чтобы задать аргументы командной строки в IDE, необходимо создать новую конфигурацию запуска следующим образом: выбрать пункт меню «Run | Edit Configurations…», добавить новую конфигурацию, выбрав в списке слева пункт «Application», задать имя при необходимости, указать main-класс, ввести значения аргументов в поле «Program Arguments», нажать кнопку «OK».
Затем запустить программу с помощью созданной конфигурации, выбрав ее по имени в выпадающем списке слева от кнопки запуска «Run».
Евклидово расстояние между заданными точками
Для двух точек двухмерного пространства
и
евклидово расстояние вычисляется следующим:
Для реализации этой формулы в Java можно использовать класс Math (является частью Java API) который обеспечивает основные метаматематические операции над числами, например:
double x = 4; Math.pow(x, 2); // Возведение x в степень 2 Math.sqrt(x); // Квадратный корень из x
Вышеприведенная формула может быть реализована следующим образом:
double d = Math.sqrt(Math.pow(x1-x2, 2) + Math.pow(y1-y2, 2));
Другой способ реализации вычисления евклидова расстояния в Java состоит в том, чтобы воспользоваться готовым методом, поставляемом в составе сторонней библиотеки, например Apache Commons Math.
Создание и сборка проекта Maven
Создать новый проект
Если открыт приветственный экран «Welcome to IntelliJ IDEA», нажать кнопку «New Project». Если открыто основное окно, выбрать пункт меню «File | New | Project».
В качестве системы сборки «Build system» выбрать «Maven». Изменить имя проекта «Name» и месторасположение его файлов «Location» при необходимости, а также «GroupId» и «ArtifactId» в скрытой панели «Advanced Settings».
Добавить класс и собрать проект
Добавить пакет и класс с исходным кодом в папку «src/main/java». Раскрыть панель «Maven» справа, затем дважды щелкнуть пункт «install» в списке «Lifecycle». Запустится процесс сборки проекта. На панели «Run» внизу можно увидеть сообщение об успешном выполнении сборки «[INFO] BUILD SUCCESS». На панели «Project» (слева) можно увидеть результаты сборки — JAR файлы, раскрыв папку target.
Добавить зависимость и импортировать класс
Открыть файл pom.xml, выбрав его на панели структуры проекта «Project». Добавить пару тегов <dependencies></dependencies>
в конце перед закрывающимся тегом следующим образом:
<?xml version ="1.0" encoding="UTF-8"?> <project ...> ... <dependencies> </dependencies> </project>
Добавить координаты зависимости от библиотеки Apache Commons Math в раздел dependencies следующим образом:
<?xml version="1.0" encoding="UTF-8"?> <project ...> ... <dependencies> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-math3</artifactId> <version>3.6.1</version> </dependency> </dependencies> </project>
Актуальные координаты зависимости можно найти на веб-сайте mvnrepository.com.
После добавления зависимости, пакеты и классы библиотеки Apache Commons Math становятся доступными для импорта в разрабатываемые классы. Например, для того чтобы использовать готовую реализацию вычисления Евклидового пространства следует импортировать класс из данной библиотеки org.apache.commons.math3.ml.distance.EuclideanDistance как показано ниже:
package ru.isu.math.example; import org.apache.commons.math3.ml.distance.EuclideanDistance; public class MyMainClass { public static void main(String[] args) { EuclideanDistance ed = new EuclideanDistance(); double d = ed.compute(new double[]{10,20},new double[]{30.,40.}); System.out.println("Euclidean distance: " + d); } }
Собрать запускаемый JAR файл
Для того чтобы собрать запускаемый, в файл pom.xml необходимо добавить следующий код:
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>single</goal> </goals> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <mainClass>ru.isu.math.example.MyMainClass</mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> </execution> </executions> </plugin> </plugins> </build>
Обновить Maven проект, нажав кнопку «Reload All Maven Projects», удалить предыдущую сборку — «clean» в списке «Lifecycle» и запустить новую сборку — «install» в списке «Lifecycle».
В случае ошибки сборки «Source option 5 is no longer supported. Use 6 or later» добавьте в pom.xml версию Java в секции «properties», например, для Java 8, это будет выглядеть следующим образом:
<properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties>
Создание и сборка проекта Gradle
Создать новый проект
Если открыт приветственный экран «Welcome to IntelliJ IDEA», нажать кнопку «New Project». Если открыто основное окно, выбрать пункт меню «File | New | Project».
В качестве системы сборки «Build system» выбрать «Gradle». Изменить имя проекта «Name» и месторасположение его файлов «Location» при необходимости, а также «GroupId» и «ArtifactId» в скрытой панели «Advanced Settings».
Добавить класс и собрать проект
Добавить пакет и класс с исходным кодом в папку src/main/java. Раскрыть панель «Gradle» справа, затем дважды щелкнуть пункт «build» в списке «Tasks | build». Запустится процесс сборки проекта. На панели «Run» внизу можно увидеть сообщение об успешном выполнении сборки «BUILD SUCCESSFUL». На панели «Project» (слева) можно увидеть результаты сборки — JAR файлы, раскрыв папку build/libs.
Добавить зависимость и импортировать класс
Открыть файл build.gradle, выбрав его на панели структуры проекта «Project». Добавить строку с координатами зависимости от библиотеки Apache Commons Math:
implementation group: 'org.apache.commons', name: 'commons-math3', version: '3.6.1'
в раздел «dependencies» следующим образом:
plugins {
id 'java'
}
group 'ru.isu.math'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
implementation group: 'org.apache.commons', name: 'commons-math3', version: '3.6.1'
}
test {
useJUnitPlatform()
}
Актуальные координаты зависимости можно найти на веб-сайте mvnrepository.com.
После этого можно импортировать пакеты и классы из библиотеки Apache Commons Math в разрабатываемые классы.
Собрать запускаемый JAR файл
Для того чтобы собрать запускаемый, в файл build.gradle необходимо добавить следующий код:
jar { manifest { attributes "Main-Class": "ru.isu.math.example.MyMainClass" } from { configurations.runtimeClasspath.collect {it.isDirectory() ? it: zipTree(it)} } }
Обновить Gradle проект, нажав кнопку «Reload All Gradle Projects», удалить предыдущую сборку — «clean» в списке «Tasks | build» и запустить новую сборку — «build» в списке «Tasks | build».
Запуск JAR файла в командной строке
Необходимо перейти в папку с запускаемым JAR файлом.
cd <path to jar directory>
Запустить Java VM с флагом -jar, после которого указать имя JARфайла, например:
Командная строка отобразит результаты выполнения JAR файла.
Задание
Часть I
- Разработать Java программу: вычислить евклидово расстояние между заданными точками.
- Создать, собрать и запустить Java проект в IDE.
Часть II
- Скомпилировать Java программу (исходный код) в командной строке.
- Запустить файл Java программу (байт код) в командной строке.
- Считать параметры командной строки (некоторые точки евклидова пространства)
- Дизассемблировать Java программу (байт код)
Часть III
- Создать, собрать и запустить Maven проект с зависимостью
«Apache Commons Math» - Создать, собрать и запустить Gradle проект с зависимостью «Apache Commons Math»
Вопросы
- Платформа разработки JDK
- Платформа исполнения JRE
- Интерпретация байт-кода (Just-in-Time компиляция)
- Main-метод и main-класс
- Литералы
- Идентификаторы
- Локальные переменные
- Блоки кода
- Сборка мусора
- Управляющие инструкции
Ресурсы
- Г. Шилдт. Java Руководство для начинающих. Глава 1
- Компилятор javac
- Создание проекта в IntelliJIDEA
- Main-класс и main-метод
- Евклидово расстояние
- Библиотека «Apache Commons Math»
- Класс Class EuclideanDistance из «Apache Commons Math»
- Maven репозиторий
- IntelliJIDEA на русском
- Maven в IntelliJ IDEA
- Gradle в IntelliJ IDEA
Для удобства распространения приложения, в Java существует такой механизм как JAR (Java Archive) файлы. JAR-файлы используются для архивации и сжатия данных.
Базовый формат команды для создания JAR-файла такой:
jar -cf jar-файл входной-файл(ы)
Сгерированный JAR-файл будет помещен в текущий каталог.
Рассмотрим ключи и аргументы, использованные в этой команде:
- Ключ
c
показывает, что необходимо создать (create) JAR-файл. - Ключ
f
показывает, что необходимо направить вывод в файл, а не в стандартный поток вывода. jar-файл
— это имя, которое необходимо дать результирующему JAR-файлу. Вы можете использовать любое имя для JAR-файл. По соглашению, имени JAR-файла дается расширение .jar, хотя это и необязательно.- Аргумент
входной-файл(ы)
— список с разделителем пробелом из одного или более файлов, которые вы хотите поместить в ваш JAR-файл. Аргументвходной-файл(ы)
может содержать также символ джокер *. Если любые из входных файлов являются каталогами, содержимое этих каталогов рекурсивно добавляется в архив JAR.
Правила относительно структуры JAR-файла.
- Команда
jar
автоматически создаст META-INF каталог. - Команда
jar
автоматически создаст MANIFEST.MF и поместит его в META-INF каталог. - Сохраняется точная структура каталогов.
- Команды
java
иjavac
могут использовать JAR-файл как нормальное дерево каталогов. - Поиск JAR-файлов с помощью ключа
-cp
аналогичен поиску пакетных файлов. Единственная разница — путь к JAR-файлу должен содержать имя JAR-файла (например classes/project1.jar).
Рассмотрим на примере как создаются и используются JAR файлы.
Допустим, что наше приложение имеет следующую структуру каталогов:
Создадим JAR-файл project1.jar, который будет содержать пакеты first
и second
:
cd project1/classes
jar –cf project1.jar first second
Можем просмотреть содержимое project1.jar файла с помощью следующей команды:
jar -tf project1.jar
Результат выполнения будет приблизительно такой:
META-INF/
META-INF/MANIFEST.MF
first/
first/Example1.class
second/
second/Example2.class
Перенесем созданный project1.jar файл в каталог lib и запустим на выполнение программу first.Example
:
cd lib
java -cp project1.jar first.Example1
Презентацию с видео можно скачать на Patreon.
Команды java
и javac
редко используются Java-программистами. Такие инструменты, как Maven и Gradle делают их почти не нужными. Однако Maven и Gradle до сих пор не предоставляют полную поддержку для Java 9, поэтому, если вы хотите начать использовать её уже сейчас или просто хотите узнать некоторые полезные тонкости до официального релиза, стоит научиться вызывать java
, javac
и jar
для управления своим кодом.
Статья призвана показать примеры использования этих команд, а также то, как эти команды изменились по сравнению с прошлыми версиями Java. Дополнительно будут рассмотрены новые инструменты: jdeps
и jlink
. Предполагается, что вы хоть немного знакомы с предыдущими версиями команд java
/javac
/jar
и с модульной системой Java 9.
Сперва необходимо установить Java 9. Вы можете скачать её с сайта Oracle, но рекомендуется использовать SdkMAN!, так как в будущем он позволит вам с легкостью переключаться между разными версиями Java.
Можно установить SdkMAN! с помощью этой команды:
curl -s "https://get.sdkman.io" | bash
Посмотрите, какая сборка является последней:
sdk list java
Затем установите Java 9:
sdk install java 9ea163
Теперь, если у вас установлены другие версии Java, вы можете переключаться между ними с помощью команды:
sdk use java <version>
Компиляция и запуск «по-старому»
Для начала напишем какой-нибудь код, чтобы проверить наши инструменты. Если не использовать модульный дескриптор, то все выглядит так же, как и раньше.
Возьмем этот простой Java-класс:
package app;
public class Main {
public static void main( String[] args ) {
System.out.println( "Hello Java 9" );
}
}
Теперь, так как мы не использовали никаких особенностей Java 9, мы можем скомпилировать всё как обычно:
javac -d out src/app/Main.java
Команда создаст файл класса out/app/Main.class
. Запустить его можно так же, как и в прошлых версиях:
java -cp out app.Main
Программа выведет Hello Java 9
.
Теперь создадим библиотеку Greeting также без особенностей Java 9, чтобы посмотреть, как это работает.
Создадим файл greeting/ser/lib/Greeting.java
со следующим кодом:
package lib;
public class Greeting {
public String hello() {
return "Hi there!";
}
}
Изменим класс Main
для использования нашей библиотеки:
package app;
import lib.Greeting;
public class Main {
public static void main( String[] args ) {
System.out.println( new Greeting().hello() );
}
}
Скомпилируем эту библиотеку:
javac -d greeting/out greeting/src/lib/Greeting.java
Чтобы показать, как работают оригинальные Java-библиотеки, мы превратим эту библиотеку в jar-файл без дескрипторов модулей Java 9:
mkdir libs
jar cf libs/lib.jar -C greeting/out .
Команда создаст файл libs/lib.jar
, содержащий класс lib.Greeting
.
Просмотреть информацию о jar-файле можно с помощью опции tf
:
jar tf libs/lib.jar
Команда должна вывести:
META-INF/
META-INF/MANIFEST.MF
lib/
lib/Greeting.class
Теперь для компиляция app.Main
нам необходимо указать компилятору, где найти класс lib.Greeting
.
Используем для этого cp
(classpath):
javac -d out -cp libs/lib.jar src/app/Main.java
И то же самое для запуска программы:
java -cp out:libs/lib.jar app.Main
Мы можем упаковать приложение в jar-файл:
jar cf libs/app.jar -C out .
И затем запустить его:
java -cp 'libs/*' app.Main
Вот так выглядит структура нашего проекта на данный момент:
.
├── greeting
│ ├── out
│ │ └── lib
│ │ └── Greeting.class
│ └── src
│ └── lib
│ └── Greeting.java
├── libs
│ ├── app.jar
│ └── lib.jar
├── out
│ └── app
│ └── Main.class
└── src
└── app
└── Main.java
Модуляризация проекта
Пока что ничего нового, но давайте начнем модуляризацию нашего проекта. Для этого создадим модульный дескриптор (всегда называется module-info.java
и размещается в корневой директории src/
):
module com.app {
}
Команда для компиляции модуля в Java 9 отличается от того, что мы видели раньше. Использование старой команды с добавлением модуля к списку файлов приводит к ошибке:
javac -d out -cp 'libs/lib.jar' src/module-info.java src/app/Main.java # не работает
src/app/Main.java:3: error: package lib does not exist
import lib.Greeting;
^
src/app/Main.java:7: error: cannot find symbol
System.out.println( new Greeting().hello() );
^
symbol: class Greeting
location: class Main
2 errors
Чтобы понять, почему наш код не компилируется, необходимо понять, что такое безымянные модули.
Любой класс, который загружается не из именованного модуля, автоматически выполняет часть безымянного модуля. В примере выше перед созданием модульного дескриптора наш код не был частью какого-либо модуля, следовательно, он был ассоциирован с безымянным модулем. Безымянный модуль — это механизм совместимости. Проще говоря, это позволяет разработчику использовать в приложениях Java 9 код, который не был модуляризирован. По этой причине код, относящийся к безымянному модулю, имеет правила сродни Java 8 и ранее: он может видеть все пакеты, экспортируемые из других модулей, и все пакеты безымянного модуля.
Когда модульный дескриптор добавляется к модулю, его код больше не является частью безымянного модуля и не может видеть код других модулей, пока не импортирует их. В случае выше модуль com.app
не требует никаких модулей, поэтому модуль библиотеки Greeting для него не виден. Он может видеть только пакеты модуля java.base
.
Модули в Java 9, за исключением неуловимого безымянного модуля описанного выше, должны объявлять, какие другие модули им необходимы. В случае с модулем com.app
единственным требованием является библиотека Greeting. Но, как вы могли догадаться, эта библиотека (как и другие библиотеки, не поддерживающие Java 9) не является модулем Java 9. Как же нам включить её в проект?
В таком случае вам нужно знать имя jar-файла. Если у вас есть зависимость от библиотеки, которая не была конвертирована в модуль Java 9, вам надо знать, какой jar-файл вызывается для этой библиотеки, потому что Java 9 переведёт имя файла в валидный модуль.
Это называется автоматический модуль.
Так же, как и безымянные модули, автоматические модули могут читать из других модулей, и все их пакеты являются экспортируемыми. Но, в отличие от безымянных модулей, на автоматические можно ссылаться из явных модулей.
Чтобы узнать имя автоматического модуля, компилятор конвертирует неальфанумерические, поэтому что-то вроде slf4j-api-1.7.25.jar
превратится в имя модуля sl4j.api
.
У нас есть библиотека с именем lib.jar
. Давайте переименуем jar-файл в greetings-1.0.jar
:
mv libs/lib.jar libs/greetings-1.0.jar
Это более стандартное имя файла, и теперь мы можем сказать Java включить автоматический модуль с приемлемым именем greetings
. И можем вызывать его из com.app
модуля:
module com.app {
requires greetings;
}
Модули не добавлены в classpath
. Как и обычные jar-файлы, они используют новый флаг --module-path (-p)
. Теперь мы можем скомпилировать наши модули следующей командой:
javac -d out -p 'libs/greetings-1.0.jar' src/module-info.java src/app/Main.java
Чтобы запустить app.Main
командой java
мы можем использовать новый флаг --module (-m)
, который принимает либо имя модуля, либо шаблон module-name/main-class
:
java -p 'libs/greetings-1.0.jar:out' -m com.app/app.Main
И мы получим вывод Hi, there
.
Для создания и использования app.jar
в качестве исполняемого jar-файла выполните следующие команды:
jar --create -f libs/app.jar -e app.Main -C out .
java -p libs -m com.app
Следующим шагом будет модуляризация библиотек, которые используются нашим приложением.
Модуляризация библиотек
Для модуляризации библиотеки нельзя сделать ничего лучше, чем использовать jdeps
— инструмент для статистического анализа, который является частью Java SE.
Например, команда, которая позволяет увидеть зависимости нашей небольшой библиотеки, выглядит так:
jdeps -s libs/greetings-1.0.jar
А вот результат её выполнения:
greetings-1.0.jar -> java.base
Как и ожидалось, библиотека зависит только от java.base
модуля.
Мы знаем, что com.app
зависит от модуля greetings
. Давайте попробуем использовать jdeps
, чтобы он подтвердил нам это. Для этого нужно удалить файлы module-info.calss
и app.jar
и затем запустить jdeps
:
zip -d libs/app.jar module-info.class
Результат:
deleting: module-info.class
Команда:
jdeps -s -cp libs/greetings-1.0.jar libs/app.jar
Результат:
app.jar -> libs/greetings-1.0.jar
app.jar -> java.base
Хорошо, но можно лучше. Мы можем попросить jdeps
автоматически сгенерировать модульный дескриптор для набора jar-файлов. Просто укажите ему, куда сохранять сгенерированные файлы (например, в папку generated-mods
), и где находятся jar-файлы:
jdeps --generate-module-info generated-mods libs/greetings-1.0.jar libs/app.jar
Команда создаст два файла: generated-mods/app/module-info.java
и generated-mods/greetings/module-info.java
со следующим содержимым:
module app {
requires greetings;
exports app;
}
module greetings {
exports lib;
}
Теперь мы можем добавить сгенерированный дескриптор для нашей библиотеки в её исходный код, переупаковать её, и у нас получится полностью модульное приложение:
javac -d greeting/out $(find greeting/src -name *.java)
jar cf libs/greetings-1.0.jar -C greeting/out .
Теперь у нас есть полностью модуляризированные библиотека и приложение. После удаления сгенерированных и бинарных файлов, структура нашего приложения выглядит следующим образом:
├── greeting
│ └── src
│ ├── lib
│ │ └── Greeting.java
│ └── module-info.java
├── libs
│ ├── app.jar
│ └── greetings-1.0.jar
└── src
├── app
│ └── Main.java
└── module-info.java
Обратите внимание, что для получения хороших данных от jdeps
вы должны предоставить местоположение всех jar-файлов, которые используются в приложении, чтобы он мог составить полный граф модуля.
Наиболее простым способом получить список всех jar-файлов, которые используются в библиотеке, является использование скрипта Gradle. Он выведет пути локальных jar-файлов для всех зависимостей библиотек, которые вы добавите в секцию зависимостей, и скачает их, если необходимо:
apply plugin: 'java'
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
compile 'io.javaslang:javaslang:2.0.6'
}
task listDeps {
println configurations.compile*.absolutePath.join(' ')
}
Если у вас нет Gradle, вы можете использовать SdkMAN! для его установки:
sdk install gradle
Для получения списка зависимостей используйте следующую команду:
gradle listDeps
Полученную информацию передайте jdeps
для анализа и автоматической генерации метаданных.
Это файл, который jdeps
выводит для javaslang.match
:
module javaslang.match {
requires transitive java.compiler;
exports javaslang.match;
exports javaslang.match.annotation;
exports javaslang.match.generator;
exports javaslang.match.model;
provides javax.annotation.processing.Processor with
javaslang.match.PatternsProcessor;
}
Создание собственного образа среды выполнения
С помощью jlink Java-приложения могут распространяться как образы, которые не требуют установки JVM.
Следующая команда создает образ для нашего com.app
модуля без оптимизации, сжатия или отладочной информации:
jlink -p $JAVA_HOME/jmods:libs --add-modules com.app
--launcher start-app=com.app
--output dist
Меньший размер может быть достигнут использованием некоторых флагов jlink
, таких как --strip-debug
и --compress
:
jlink -p $JAVA_HOME/jmods:libs --add-modules com.app
--launcher start-app=com.app
--strip-debug --compress=2
--output small-dist
Размер пакетов можно посмотреть с помощью команды du -sh
:
du -sh small-dist dist
21M small-dist
35M dist
Для запуска приложения используйте предоставляемый лаунчер в директории bin
:
dist/bin/start-app
Вы должны увидеть сообщение Hi there
.
На этом всё. Разбор нововведений в Java 9 предлагаем прочитать в нашей статье.
Перевод статьи «A practical guide to Java 9 — compile, jar, run»