Отделяем бинарные файлы от исходников

Один файл


Можно сделать и без лишних папок.
Берем сам файл.

010203040506070809 public class HelloWorld{ public static void main(String[] args) { System.out.println("Hello World!"); Calculator calc=new Calculator(); System.out.println("2+2="+calc.sum(2,2)); }}

Переходим в каталог, где лежит данный файл, и выполняем команды.

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

01020304050607080910111213 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

01020304050607080910111213141516 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

0102030405060708091011121314151617181920212223242526 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 src\com\qwertovsky\helloworld\HelloWorld.java:9: cannot find symbol symbol : class Calculator location: class com.qwertovsky.helloworld.HelloWorld Calculator calc=new Calculator(); ^ src\com\qwertovsky\helloworld\HelloWorld.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.

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


Можно подписать 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

Включили код нужной библиотеки в исполняемый файл.


Ещё почитать


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

· java

· , командная строка

· , консоль

· , разработка

+67

29 июля 2011, 14:00

Qwertovsky 18,0

Комментарии (22)

+3

Отделяем бинарные файлы от исходников - student2.ru cypok29 июля 2011, 14:43#

Сейчас уже никто не создает программы в консоли.

Кажется я — никто :)

Отделяем бинарные файлы от исходников - student2.ru t_rex29 июля 2011, 14:58#↑

никто не создает


Вы создаете? Значит под описание не подходите ;)

Отделяем бинарные файлы от исходников - student2.ru ramilexe29 июля 2011, 15:06#

Спасибо за статью! Как раз собираюсь переключиться плавно с пхп на яву и не охота писать хеллоу ворлд в монструозном нетбинсе, не понимаю как оно работает внутри.
Начинать изучение с командной строки это самое оптимальное, имхо.

+2

Отделяем бинарные файлы от исходников - student2.ru KilgortTraut29 июля 2011, 15:18#↑

Мирончика лекции посмотри, там хоть и долго, но довольно доходчиво объясняется, как организованы пакеты Java.

Отделяем бинарные файлы от исходников - student2.ru mr_locke30 июля 2011, 09:24#↑

А ссылку на лекцию можно? Спасибо.

+1

Отделяем бинарные файлы от исходников - student2.ru gshock29 июля 2011, 19:41#↑

Кроме нетбинса есть еще Eclipse. И скажите, почему много php-разработчиков, которых я знаю, любят пользоваться именно нетбинсом притом что бооольшинство java-разработчиков им не пользуются? Вопрос из моего жизненного наблюдения

+4

Отделяем бинарные файлы от исходников - student2.ru stas_agarkov30 июля 2011, 11:24#↑

Потому что для Java есть Intellij IDEA.

Отделяем бинарные файлы от исходников - student2.ru FeliX133729 июля 2011, 15:07#

Спасибо. В избранное, однозначно.

Отделяем бинарные файлы от исходников - student2.ru iPavel29 июля 2011, 15:31#

Благодарствую

+2

Отделяем бинарные файлы от исходников - student2.ru kiRach29 июля 2011, 16:04#

Отличная статья! Old school жив:)

+1

Отделяем бинарные файлы от исходников - student2.ru calx29 июля 2011, 16:11#

Шикарная статья. Автор, пишите, пожалуйста, ещё.

Отделяем бинарные файлы от исходников - student2.ru netslow29 июля 2011, 17:02#

Очень хорошо. Вы молодец. Много полезной и структурированной информации

+9

Отделяем бинарные файлы от исходников - student2.ru gribozavr29 июля 2011, 17:40#

> Сейчас уже никто не создает программы в консоли.

Как-то меня попросили помочь одному человеку с программированием на Си. Спрашиваю: какой компилятор используете? — Что? — Ну, чем компилируете? — F8!

С тех пор я считаю, что пока человек не разобрался с тулчейном в консоли, в IDE пускать его ещё рано.

–2

Отделяем бинарные файлы от исходников - student2.ru Foror29 июля 2011, 18:53#

Интересно, но блин как всё это сложно, столько нужно делать телодвижений, чтобы банально собрать и запустить проект. И с популярными IDE теже проблемы. В иной раз, в этих монстрах, страшно вынести классы в отдельную библиотеку…

Отделяем бинарные файлы от исходников - student2.ru Artm31 июля 2011, 22:16#↑

Непонятные минуса у человека. Что он не так сказал?

+6

Отделяем бинарные файлы от исходников - student2.ru muromec29 июля 2011, 19:30#

теперь осталось только сделать сборку через make и вас выгонят из секты.

Отделяем бинарные файлы от исходников - student2.ru zokotuhaFly31 июля 2011, 20:58#↑

ну, ant же тожно можно использовать из консоли. мы напишем для вас руководство, не беспокойтесь.

Отделяем бинарные файлы от исходников - student2.ru iZENfire29 июля 2011, 23:05#

Без воды. Уважаю.

Отделяем бинарные файлы от исходников - student2.ru uglock30 июля 2011, 14:44#

Вот и выросло поколение программистов, которые не могут собрать программу на C/Java/.Net/etc без IDE. Мрак.

Отделяем бинарные файлы от исходников - student2.ru Artm31 июля 2011, 22:15#↑

А дрова рубить умеют? А в лесу прожить 3 дня «как есть»? А Рыбу ловить? Вот и выросло поколение мегаполис-жителей.
Что удивляемся, для того проги и пишут. Это называется прогресс.

Отделяем бинарные файлы от исходников - student2.ru schaan28 декабря 2011, 14:21#↑

Поддерживаю.
Хотя знать основы рубки дров и запуска джавы из консоли пригодятся.

Отделяем бинарные файлы от исходников - student2.ru backinblack3 августа 2011, 10:34#

Спасибо огромное! Очень полезная статья.
И за полезные линки отдельное спасибо.

Один файл


Можно сделать и без лишних папок.
Берем сам файл.

010203040506070809 public class HelloWorld{ public static void main(String[] args) { System.out.println("Hello World!"); Calculator calc=new Calculator(); System.out.println("2+2="+calc.sum(2,2)); }}

Переходим в каталог, где лежит данный файл, и выполняем команды.

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

Наши рекомендации