Как написать программу jar

Время на прочтение
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

В результате получиться следующее
image

Можно подписать 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. Изображение с названием Create JAR File Step 1

    1

    Удостоверьтесь, что у вас установлен комплект разработчика Java-приложений (JDK). Если на компьютере JDK нет, скачайте и установите его.

  2. Изображение с названием Create JAR File Step 2

    2

    Откройте веб-сайт программы Eclipse. Перейдите на страницу https://www.eclipse.org/.

    • Если программа Eclipse уже установлена, перейдите в следующий раздел.
  3. 3

    Щелкните по DOWNLOAD (Скачать). Это кнопка в верхней части страницы.

  4. Изображение с названием Create JAR File Step 4

    4

    Щелкните по DOWNLOAD (Скачать). Эта оранжевая кнопка находится в верхней части открывшейся страницы. Начнется загрузка Eclipse на компьютер.

  5. Изображение с названием Create JAR File Step 5

    5

    Дождитесь, когда загрузка будет завершена. На это уйдет некоторое время.

  6. Изображение с названием Create JAR File Step 6

    6

    Запустите установочный файл Eclipse.

    • В Windows дважды щелкните по скачанному файлу, а затем нажмите «Да», когда появится запрос.
    • В Mac OS X дважды щелкните по скачанному архиву (ZIP-файлу) и дождитесь, когда он распакуется.
  7. Изображение с названием Create JAR File Step 7

    7

    Откройте окно мастера установки Eclipse. Для этого дважды щелкните по фиолетовому значку Eclipse, когда он появится.

    • Если вы щелкнули по ссылке «Change Mirror» (Поменять сервер), которая находится под оранжевой кнопкой «DOWNLOAD» (Скачать), возможно, откроется окно самой программы Eclipse. В этом случае перейдите к шагу «Нажмите “Обзор”».
  8. Изображение с названием Create JAR File Step 8

    8

    Нажмите Eclipse IDE for Java Developers (Eclipse IDE для разработчиков Java). Это верхняя опция в окне мастера установки Eclipse.

  9. Изображение с названием Create JAR File Step 9

    9

    Щелкните по Install (Установить). Эта желтая кнопка находится в нижней части окна. Начнется установка Eclipse на компьютер.

    • Если хотите, укажите папку, в которую будет установлена программа; для этого щелкните по значку в виде папки справа от опции «Installation Folder» (Папка для установки), а затем выберите конкретную папку на компьютере.
    • Если вы не хотите создавать ярлыки для Eclipse, сначала снимите флажки у соответствующих опций, а потом нажмите «Установить».
  10. Изображение с названием Create JAR File Step 10

    10

    Щелкните по Launch (Запустить). Эта зеленая кнопка находится в нижней части окна мастера установки. Откроется программа Eclipse.

  11. Изображение с названием Create JAR File Step 11

    11

    Нажмите Browse (Обзор). Эта опция находится в правой части окна. Откроется окно Проводника (в Windows) или Finder (в Mac OS X).

  12. Изображение с названием Create JAR File Step 12

    12

    Выберите папку рабочей области. Щелкните по нужной папке в левой части экрана. В этой папке будут храниться файлы проектов Eclipse.

  13. Изображение с названием Create JAR File Step 13

    13

    Щелкните по OK. Эта кнопка находится в нижнем правом углу окна.

  14. Изображение с названием Create JAR File Step 14

    14

    Нажмите Launch (Запустить). Вы найдете эту опцию в нижней части окна. Откроется главная страница Eclipse.

    Реклама

  1. Изображение с названием Create JAR File Step 15

    1

    Скопируйте нужные файлы в одну папку. Файлы, которые будут помещены в JAR-файл, должны быть в одной папке.

  2. Изображение с названием Create JAR File Step 16

    2

    Щелкните по Файл. Это меню находится в верхнем левом углу окна (Windows) или в верхнем левом углу экрана (Mac OS X). Откроется выпадающее меню.

  3. Изображение с названием Create JAR File Step 17

    3

    Щелкните по Открыть файл. Эта опция находится в верхней части выпадающего меню.

  4. Изображение с названием Create JAR File Step 18

    4

    Откройте папку с файлами. Найдите папку с нужными файлами, а затем дважды щелкните по папке, чтобы открыть ее. На экране отобразится список файлов.

  5. Изображение с названием Create JAR File Step 19

    5

    Выберите все файлы в папке. Щелкните по любому файлу, а затем нажмите Ctrl+A (Windows) или Command+A (Mac OS X), чтобы выбрать все файлы в папке.

  6. Изображение с названием Create JAR File Step 20

    6

    Нажмите Открыть. Эта кнопка находится в нижнем правом углу окна. Файлы откроются в Eclipse.

  7. Изображение с названием Create JAR File Step 21

    7

    Щелкните по Файл. Откроется выпадающее меню.

  8. Изображение с названием Create JAR File Step 22

    8

    Щелкните по Экспортировать. Эта опция находится в нижней части выпадающего меню.

  9. Изображение с названием Create JAR File Step 23

    9

    Дважды щелкните по Java. Эта опция находится в верхней части страницы.

  10. Изображение с названием Create JAR File Step 24

    10

    Нажмите JAR-файл. Вы найдете эту опцию под опцией «Java», по которой вы дважды щелкнули.

    • Если вы хотите, чтобы JAR-файл запускался как программа, выберите опцию «Исполняемый JAR-файл».
  11. Изображение с названием Create JAR File Step 25

    11

    Щелкните по Далее. Эта кнопка находится в нижней части окна.

  12. Изображение с названием Create JAR File Step 26

    12

    Выберите ресурсы для экспорта. Поставьте флажки у неотмеченных ресурсов, которые вы хотите добавить в свой JAR-файл. Сделайте это в окне в верхней части страницы.

    • Пропустите этот шаг, если вы решили создать исполняемый JAR-файл.
  13. Изображение с названием Create JAR File Step 27

    13

    Выберите папку для хранения JAR-файла. Нажмите «Обзор», введите имя файла, щелкните по папке, в которой JAR-файл будет храниться, а затем нажмите «Сохранить».

  14. Изображение с названием Create JAR File Step 28

    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».

Снимок экрана: результат запуска приложения

Евклидово расстояние между заданными точками

Для двух точек двухмерного пространства

p_1=left(x_1,y_1right) и p_2=left(x_2,y_2right)

евклидово расстояние вычисляется следующим:

dleft( p1,p2 right)=sqrt[]{left( x_1 - x_2 right)^{2}+left( y_1 - y_2 right)^{2}}

Для реализации этой формулы в 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».

Снимок экрана: создание Maven проекта

Добавить класс и собрать проект

Добавить пакет и класс с исходным кодом в папку «src/main/java». Раскрыть панель «Maven» справа, затем дважды щелкнуть пункт «install» в списке «Lifecycle». Запустится процесс сборки проекта. На панели «Run» внизу можно увидеть сообщение об успешном выполнении сборки «[INFO] BUILD SUCCESS». На панели «Project» (слева) можно увидеть результаты сборки — JAR файлы, раскрыв папку target.

Снимок экрана: сборка Maven проекта

Добавить зависимость и импортировать класс

Открыть файл 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.

Снимок экрана: сборка Gradle проекта

Добавить зависимость и импортировать класс

Открыть файл 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»

Вопросы

  1. Платформа разработки JDK
  2. Платформа исполнения JRE
  3. Интерпретация байт-кода (Just-in-Time компиляция)
  4. Main-метод и main-класс
  5. Литералы
  6. Идентификаторы
  7. Локальные переменные
  8. Блоки кода
  9. Сборка мусора
  10. Управляющие инструкции

Ресурсы

  • Г. Шилдт. 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»

Понравилась статья? Поделить с друзьями:
  • Как написать программный код
  • Как написать программку к спектаклю
  • Как написать прогноз погоды на английском языке
  • Как написать провокационный пост
  • Как написать проверочный код