Показаны сообщения с ярлыком javac. Показать все сообщения
Показаны сообщения с ярлыком javac. Показать все сообщения

3 апр. 2015 г.

Разбираемся с classpath в Java. Часть 2.

Теперь немого усложним задачу. Для экспериментов будем использовать проект 00005E_AdvancedHello. Давайте теперь сами скомпилируем этот файл без Eclipse.

Перейдем в каталог с исходником и дадим команду:

javac AdvancedHello.java

И увидим вот такую россыпь ошибок:

CP0021

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

CP0022

К сожалению все значение переменной не вошло, но вообще она выглядит так:

CLASSPATH=.;C:\Git\StudyingJava\ProJavaJarLibs\ProJava.jar

GrabliОбратите внимание на .; это обязательно должно быть. Так как мы переопределяем значение этой переменной, то необходимо указать и текущий каталог на что указывает точка. Точка с запятой это обычный разделитель. Чтобы все было еще более понятно и вы могли проверить что все нормально, значение этой переменной можно посмотреть через консоль командой

 

ECHO %CLASSPATH%

CP0023

Чтобы консоль увидела это значение ее надо запустить заново, так как консоль читает значения во время старта и потом уже их не обновляет.

Теперь снова попробуем скомпилировать наш класс AdvancedHello.jar

CP0024

Он удачно скомпилировался и запустился и все это только благодаря тому что мы создали системную переменную CLASSPATH.

Если бы мы не указали в ней точку, то программа бы не заработала, так как она бы не знала где искать метод main и искала бы его в ProJava.jar, но там его нет.

На этом с classpath можно закончить, поскольку, в принципе это уже раритет, и этим пользуются очень редко или вообще не пользуются. Но знать про это все равно надо, дабы было понимание как работают программы (классы) Java.

После этих экспериментов, если вы их делали, лучше удалите системную переменную CLASSPATH.

Во первых в нашем варианте компиляции мы получили просто класс AdvancedHello.class и если вы его переместите на другой комп и там не будет библиотеки ProJava.jar или же к ней не правильно будет прописан путь, то этот класс не запуститься. Поэтому обычно библиотеки встраивают в исполняемый jar файл. Но об этом чуть позже.

И так, более менее, базовый запас знаний как работает среда Java у нас есть и теперь можно переходить к изучению самого языка.

Разбираемся с classpath в Java. Часть 1.

Теперь быстренько разберемся classpath, так как это достаточно важная тема для разработки на Java. Естественно я тут не смогу разобрать все тонкости и хитрости, но постараюсь представить самое необходимое чтобы было понимание этой темы. Для желающих разобраться глубже приведу линки для самостоятельного изучения. Ну и как всегда гугль в помощь.

Чтобы понимать что происходит под капотом у любой Java IDE когда она собирает проект и запускает его надо хоть немного попрактиковаться в использовании компилятора javac, среды исполнения java и понять classpath.

По существу classpath указывает компилятору или виртуальной машине где искать классы необходимые для сборки проекта или же его запуска. Немного об этом мы уже узнали тут. Теперь разберемся с этим получше.

Указать где компилятору или виртуальной машине искать классы можно через ключ –classpath или же системную переменную окружения CLASSPATH. Мы рассмотрим оба этих варианта.

Начнем с простого. Вернемся к нашему проекту Hello World (00004E_HelloWorld), там где мы разделили его на два файла Hello.java и Word.java.

Теперь попробуем создать исполняемый (jar) файл этого проекта из среды Eclipse. Так как скомпилированные, читай готовые к исполнению, файлы в Java имеют расширение class, а классов в реальных программах, могут быть сотни и тысячи, то их собирают в один или несколько jar архивов и таким образом запускают. То есть уже существует не россыпь файлов с расширением class, а один или несколько jar файлов.

И так! Понеслась! Воспользуемся Export для создания jar

CP0001

CP0003

После этого мы получим файл HelloWorld.jar готовый к исполнению на виртуальной машине java. Запустим его из командной строки:

CP0004

Запускать jar файлы надо с ключом –jar как показано на скрине выше. Если этот ключ не использовать то вылетит ошибка:

CP0005

Нам сообщили что не знают где искать главный (main) класс для HelloWorld.jar. Но запустить все же можно и без ключа –jar, но уже воспользовавшись на ключом –classpath, для которого существует сокращенный вариант –cp. Вот как это можно сделать:

CP0006

Почему строчка запуска выглядит именно так? Вспоминаем что именно класс Hello.java содержит у нас метод main.

CP0007

Класс Word.java такого метода не имеет.

CP0008

Как я уже говорил метод main – это точка входа в программу, то есть место от куда начинается ее выполнение и поэтому виртуальной машине java надо знать, от куда надо начинать выполнять программу. Если она не может найти метод main, то она начинает ругаться, как это было показано выше.

И так в нашей строчке

java -cp HelloWorld.jar Hello

мы указали что искать метод main надо в классе Hello (расширение .class опускается) по пути HelloWorld.jar. Jar и zip архивы рассматриваются виртуальной машиной как каталоги, поэтому их надо указывать по полному имени с расширением.

Теперь скомпилируем наши классы Hello.java и Word.java самостоятельно из командной строки без помощи Eclipse, чтобы еще глубже понять как все работает. Для чистоты эксперимента рекомендую удалить все файлы с расширением .class и .jar из каталога bin.

Для начал просто скомпилируем исходники в class файлы без упаковки их в jar, чтобы было понятнее.

Переходим в коневой каталог 00004E_HelloWorld и от туда даем команду компиляции

javac -encoding utf8 -d bin src\Hello.java src\Word.java

CP0009

Поскольку у нас программа состоит из двух классов Hello и Word, то их обоих сразу надо указать компилятору. Кроме того так же надо указать и кодировочку исходников. Так же мы указали папку bin – это то куда будут складываться откомпилированные файлы.

Теперь у нас в каталоге bin два файла Hello.class и Word.class. Перейдем в него чтобы запустить программу.

CP0010

Все работает. Но файлики у нас там лежат россыпью классво .class

CP0011

Теперь упакуем эти файлики .class в jar архив командой

jar cf HelloWorld.jar Hello.class Word.class

и попробуем запустить HelloWorld.jar

CP0012

И вылетела ошибочка. Почему так? Ведь у нас уже есть jar файл в который упакованы оба класса.

CP0013

Но все равно не работает. Это происходит потому, что внутри jar файла мы не указали какой файл у нас имеет метод main.

Запустить наш jar файл все таки можно указав дополнительно, какой класс содержит метод main.

java -cp HelloWorld.jar Hello

CP0014

Теперь все работает. Но согласитесь так запускать jar файл не удобно, так как всегда надо знать какой класс содержит метод main. Если вы смотрели внимательно, то видимо заметили внутри архива HelloWorld.jar папку META-INF. В ней содержится файл MANIFEST.MF

CP0015

Вот в нем и должна содержаться информация о классе содержащем метод main, но пока в нем ее нет.

CP0016

Исправим эту ошибочку. Удалим файлик HelloWorld.jar и создадим его заново, но уже с добавлением информации о классе содержащим метод main. Сделаем это следующей командой

jar cfe HelloWorld.jar Hello Hello.class Word.class

И запустим файл HelloWorld.jar уже как полагается без танцев с бубном.

CP0017

Как видим все работает нормально. Это произошло потому, что файл MANIFEST.MF уже содержит информацию о классе содержащем метод main.

CP0018

Ну вот теперь мы имеем хоть какое-то представление о том что происходит когда какая-либо IDE создает исполняемый jar файл, а так же получили представление о classpath. В следующей статье мы немного углубим его.

P.S. Так же стоит знать что по умолчанию для виртуальной машины java доступны все классы стандартной библиотеки java, а так же все классы в текущем каталоге от куда запускается главный класс содержащий метод main.

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

23 мар. 2015 г.

Лексическая структура Java. Часть 1 – Unicode и другие кодировки

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

GrabliСразу скажу, что программы на Java лучше создавать в кодировке Unicode (UTF-8), так как если вы будете писать их в других кодировках, то могут быть проблемы с отображением символов национальных алфавитов, отличных от латинского. Хотя они могут быть даже если вы их пишете в Unicode и сейчас мы в этом убедимся.

Теперь в бой! Посмотрим все на примерах. Возьмем классическую программу Hello World и допишем в нее строчку выводящую сообщение “Привет Мир!” на русском языке. Текст программы запишем как и полагается в кодировке UTF-8.

E00001

И теперь откомпилируем ее с параметрами по умолчанию. То есть просто дадим команду

javac HelloWorld.java

И выполним нашу программу командой

java HelloWorld

И смотрим результат

E00002

Текст на русском языке “Привет Мир!” отобразился кракозябрами. Что же случилось? Почему такая не справедливость? Ведь мы же записали текст программы в Unicode! Сделали все как положено!

Но дело в том, что компилятор javac, по умолчанию, компилирует программу в кодовой странице операционной системы.

Теперь откомпилируем эту же программу, но уже укажем в какой кодировке у нас исходный код программы c помощью следующей команды

javac -encoding utf8 HelloWorld.java

И запустим программу на выполнение

E00003

Ну вот теперь русский текст “Привет Мир!” отображается правильно.

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

В данном примере я намеренно не использовал ни какие среды разработки, а воспользовался простым текстовым редактором.

Писать любые программы лучше в средах разработки, предназначенных для этих языков, так как это очень удобно и быстро, но несет небольшие накладные расходы, так как среды разработки сохраняют в рабочих каталогах файлы со своими настройками, но это ни что по сравнению с удобствами которые они предоставляют.

Сейчас рассмотрим еще несколько примеров исходников в различных кодировках для лучшего понимания темы и на этом закончим с использованием текстового редактора, хотя часто он бывает нужен, чтобы просто заглянуть в текст программы не запуская “тяжелую” IDE.

И так я создал такую же простую программу как “Hello World”, но в разных кодировках (866, 1251 и UTF-8).

Код абсолютно одинаковый только создан в разных кодировках.

E00004

E00005

E00007

Теперь скомпилируем их с параметрами по умолчанию и запустим.

Но сперва отметим такую команду консоли Windows как chcp. Про нее мало кто знает. Она отображает текущую кодовую страницу консоли, а так же может ее устанавливать.

Посмотрим текущую кодовую страницу консоли, откомпилируем программу Example866.java и запустим ее.

E00008

Как видим вывод русского текста не правильный, одни кракозабры, зато слово Java и восклицательный знак (!) вывелись правильно. Это происходит потому что Unicod код латинских символов (первых 128) совпадает с ASCII и Latin-1. Поэтому если в вашей программе вы используете только латинский алфавит для отображения строк и т.п., то можно не беспокоится. Все всегда будет отображаться корректно, но с символами других алфавитов это не так.

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

Это происходит потому, что компилятор javac конвертирует все символы в Unicode. Давайте попробуем установить кодовую страницу консоли в Unicode и посмотрим может, тогда все отобразится правильно.

E00009

Опять все отобразилось не правильно, за исключением слова Java и восклицательного знака. Но кракозябры уже другие, что безусловно не может не радовать…

И так в чем же проблема? А проблема в том, что как уже я говорил, по умолчанию компилятор использует кодовую страницу операционной системы, в нашем случае это CP1251, так как Windows у нас настроен на использование кириллической кодировки CP1251.

Произошло следующее, компилятор, перевел символы кодировки 866 в UTF-8, полагая что это кодировка CP1251. Поэтому в данном случае, как бы мы не меняли кодовую страницу мы уже не получим правильного отображения русского текста. Давайте зададим для консоли кодировку cp1251 и посмотрим что будет.

E00010

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

Чтобы исправить эту ситуацию надо откомпилировать программу Exemple866.java с использованием кодовой страницы 866. И чтобы проверить это вернем консоли кодировку по умолчанию 866, дабы все было по честно по правилам.

E00011

Теперь у нас все правильно выводится.

Далее откомпилируем программу Example1251.java и запустим ее. Ее можно компилировать с параметрами по умолчанию, так как javac будет использовать для нее кодировку 1251, что нам и нужно.

E00013

Все тоже отображается правильно. Ну и тоже самое с программой ExampleUTF8.java

E00014

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

Теперь небольшой ликбез об UTF8. Данная кодировка использует ДВА БАЙТА для хранения кода символа, что позволяет представить 65535 символов – это покрывает почти все символы всех языков Земли. Кодировка ASCII может представить только 256 символов.

Теперь посмотрим нашу самую русскую букву Ё в кодировке 1251 (ASCII) и в кодировке UTF8.

В кодировке 1251 ASCII буква Ё представлена одним байтом (A8).

E00015

В кодировке UTF-8 буква Ё представлена двумя байтами (D0 81).

E00016

И теперь посмотрим бинарные файлы классов Example866.class и Example1251.class

E00017

E00018

Как видим наш текст на русском “Это Java программа!” в обоих бинарниках отображается одинаково, хотя один из них сделан из исходника в кодировке 866, а другой в кодировке 1251. Это произошло потому, что компилятор javac, перевел символы из этих кодировок в кодировку UTF-8.

Так же я подчеркнул байты CA FE BA BE – это так называемое магическое число java. По нему виртуальная машина определяет что перед ней именно класс Java, а не что-то еще. Эта комбинация присутствует в начале всех откомпилированных  файлов классов Java с расширением .class.

Ну и чтобы все было более наглядней приведу еще одни скрин сравнения этих файлов

 E00019

sber01Так вот! К чему я это все?

Граждане, храните деньги в сберегательной кассе!

Пишите исходники в UTF-8. Надежно, выгодно, удобно.

И да! Храните ваши исходники на bitbucket.org – надежно, выгодно, удобно!

P.S. Все исходники примеров тут: https://bitbucket.org/n0tb0dy/studyingjava