Java — Java и компеляция

Компиляция и выполнение Java-программы

Java , будучи независимым от платформы языком программирования, не работает с одношаговой компиляцией. Вместо этого он включает в себя двухэтапное выполнение, сначала через независимый от ОС компилятор; и во-вторых, в виртуальной машине (JVM), созданной для каждой операционной системы. Два основных этапа объясняются ниже:

компиляция

Сначала исходный файл «.java» передается через компилятор, который затем кодирует исходный код в машинно-независимую кодировку, известную как байт-код. Содержимое каждого класса, содержащегося в исходном файле, хранится в отдельном файле .class. При преобразовании исходного кода в байт-код компилятор выполняет следующие шаги:

  • Анализ : Считывает набор исходных файлов * .java и отображает полученную последовательность токенов в узлы AST (Абстрактное синтаксическое дерево).
  • Enter : ввод символов для определений в таблицу символов.
  • Обрабатывать аннотации : если требуется, обрабатывает аннотации, найденные в указанных единицах компиляции.
  • Атрибут : Атрибуты деревьев синтаксиса. Этот шаг включает в себя разрешение имен, проверку типов и постоянное свертывание.
  • Поток : выполняет анализ потока данных на деревьях из предыдущего шага. Это включает в себя проверки назначений и достижимости.
  • Desugar : перезаписывает AST и переводит немного синтаксического сахара.
  • Generate : генерирует файлы .Class.
  • выполнение

    Файлы классов, сгенерированные компилятором, не зависят от компьютера или ОС, что позволяет запускать их в любой системе. Для запуска файл основного класса (класс, который содержит метод main) передается в JVM, а затем проходит три основных этапа, прежде чем будет выполнен окончательный машинный код. Эти этапы:

    Класс Loader Основной класс загружается в память, передавая его файл .class в JVM, вызывая последний. Все остальные классы, на которые есть ссылки в программе, загружаются через загрузчик классов.
    Загрузчик классов, сам объект, создает плоское пространство имен тел классов, на которые ссылается строковое имя. Определение метода:

    ссылка на сайт
    brightness_4
    код

    Существует два типа загрузчиков классов: первичные и не первичные. Первичный загрузчик классов встроен во все JVM и является загрузчиком классов по умолчанию. Не первичный загрузчик классов — это определяемый пользователем загрузчик классов, который может быть закодирован для настройки процесса загрузки классов. Загрузчик классов, не являющийся первичным, если он определен, предпочтительнее загрузчика классов по умолчанию. Проверка байт-кода После загрузки байт-кода класса загрузчиком классов его должен проверить верификатор байт-кода, задачей которого является проверка того, что инструкции не выполняют вредоносных действий. Ниже приведены некоторые из проведенных проверок:

    • Переменные инициализируются перед использованием.
    • Вызовы методов соответствуют типам ссылок на объекты.
    • Правила доступа к личным данным и методам не нарушаются.
    • Доступ к локальным переменным находится в стеке времени выполнения.
    • Стек времени выполнения не переполняется.

    Если любая из вышеперечисленных проверок завершается неудачей, верификатор не позволяет загружать класс. Just-In-Time Compiler Это последний этап, с которым столкнулась java-программа, и ее задача — преобразовать загруженный байт-код в машинный код. При использовании JIT-компилятора аппаратное обеспечение может выполнять собственный код, в отличие от того, чтобы JVM неоднократно интерпретировал одну и ту же последовательность байт-кода и влекло за собой наказание за относительно длительный процесс перевода. Это может привести к повышению производительности в скорости выполнения, если методы не выполняются реже.

    Процесс может быть хорошо проиллюстрирован следующей диаграммой:

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

    Компиляция файла .java в . >01.01.2011, 00:00. Просмотров 25646. Ответов 14

    есть файл М.java.
    вопрос как файл М.java. компилировать в такой формат файла М.class .

    01.01.2011, 00:00

    Компиляция JAVA в консоли: подключение jar библиотеки
    Здравствуйте! Такая ситуация, есть файл, который я компилирую, в нём написано import.

    Компиляция *.java файла
    Привет всем. Такой вопрос. Есть у меня jdk 1.6.0. есть файл example.java, что и как делать.

    Компиляция java файла
    Пытаюсь собрать декомпилированный java в class файл, при это выдает 17 ошибок. Помогите.

    Ошибка в компиляции файла из формата .java в формат .class
    Здравствуйте формучане. Сразу скажу, здесь я новичек, в программировании так себе, можно сказать на.

    Java — Java и компеляция

    В этом уроке мы создадим нашу первую программу на языке Java.
    Создание приложения на языке Java состоит из трех следующих шагов:

    Создание исходного файла

    Для начала нужно написать текст программы на языке Java и сохранить его. Это и будет нашим исходным файлом. Для создания исходного файла подойдет любой текстовый редактор, например стандартный «Блокнот». Однако, существуют и другие текстовые редакторы, которые более удобны для написания кода. Можно воспользоваться например, Notepad++ . Это бесплатный текстовый редактор, который поддерживает синтаксис большинства языков программирования, в том числе и Java.

    Итак, открываем текстовый редактор и пишем в нем код программы Hello World, цель которой — вывод на экран сообщения Hello World!

    После написания этого кода, файл нужно сохранить под именем HelloWorld.java.
    Для этого в вашем текстовом редакторе нужно выбрать пункт меню Файл-> Сохранить как… Если вы пользуетесь стандартным Блокнотом Windows, то для того, чтобы сохранить файл с расширением .java необходимо при сохранении выбрать Тип файла: Все файлы и ввести Имя файла: HelloWorld.java (рис 2.1).

    Если вы пользуетесь Notepad++ то нужно выбрать Тип файла:Java source file (*.java)

    Будьте внимательны! файл должен называться в точности так, как называется наш класс — HelloWorld. Так же важно учитывать регистр букв. HelloWorld и helloworld в данном случае это разные слова!

    Обратите также внимание на кодировку в которой сохраняете файл. Должно быть выбрано ANSI . В Notepad++ кодировку можно установить в меню Кодировки.

    Компиляция исходного файла

    Исходный файл с кодом программы создан, теперь перейдем к компиляции. Для компиляции Java предназначен компилятор javac, который входит в состав установленного нами в первом уроке пакета JDK.

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

    Теперь в нем нужно изменить текущий каталог на тот, в котором находится наш исходный файл (например C:\studyjava\). Для этого вводим следующую команду:

    и нажимаем Enter.

    После того, как директория изменилась, вводим команду компиляции

    После этого, окно командной строки должно выглядеть следующим образом (рис 2.2):

    То есть, мы не получим никакого подтверждения, о том, что программа скомпилировалась успешно. Однако, в папке с нашим исходным файлом, должен появиться файл HelloWorld.class. Это можно проверить с помощью команды

    Эта команда выводит на экран список всех файлов, находящихся в выбранной директории (рис 2.3).

    Если файл HelloWorld.class присутствует в этом списке, то это значит, что программа скомпилировалась успешно.

    Если в коде программы есть ошибка, то компилятор Java при компиляции нам об этом сообщит.

    Проведем эксперимент: Откроем в текстовом редакторе наш файл HelloWorld.java и удалим последнюю закрывающуюся фигурную скобку «>». Сохраним файл и попробуем его еще раз скомпилировать. В итоге получаем сообщение об ошибке (рис 2.4).

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

    Запуск программы

    Переходим к последней стадии — запуску программы.

    Вводим в командном окне:

    и если все перед этим было сделано правильно, то получаем результат — вывод сообщения «Hello World!» (рис 2.5).

    Еще раз обратите внимание на чувствительность к регистру в Java. Если вы напишете helloworld вместо HelloWorld, то программа запущена не будет, потому что Java попросту не найдет файл с таким именем.

    В качестве домашнего задания можете поэкспериментировать и выводить на экран какое-либо свое сообщение вместо Hello World!.

    Конечно, для написания, компилирования и запуска программ на языке Java существуют более удобный инструмент, нежели Блокнот и командная строка . Этот так называемая Интегрированная среда обработки IDE. Об этом мы поговорим в следующем уроке.

    Java: магия отражений. Часть III. Компиляция Java средствами Java

    Архив номеров / 2003 / Выпуск №2 (3) / Java: магия отражений. Часть III. Компиляция Java средствами Java

    Java: магия отражений

    Часть III. Компиляция Java средствами Java

    В предыдущих частях статьи мы познакомились с технологией отражений (Java Reflection). Это мощнейший механизм Java, позволяющий делать с .class-файлами практически все что угодно – загружать из произвольных файлов, анализировать набор членов класса, обращаться к этим членам, при необходимости обходя стандартную защиту «private»/«protected». При желании можно даже подменить стандартный механизм загрузки Java-классов и взять этот процесс под полный контроль, например, разрешить перезагружать изменившиеся версии .class-файлов без полной перезагрузки Java-машины (эта техника подробно рассматривалась в части II, см. №1(2) журнала «Системный администратор»).

    В этой части статьи мы научимся компилировать Java-код в .class-файлы. Совместно с технологией отражений это позволит в процессе исполнения программы «на лету» создавать новые классы в виде исходного текста, компилировать их, загружать и использовать. Столь мощные возможности обычно присущи лишь чисто интерпретируемым, сравнительно медленным языкам типа JavaScript или Perl или Ассемблеру (точнее, машинному языку).

    Всюду далее, если не оговорено обратное, мы будем подразумевать последнюю (на момент написания статьи) версию Java фирмы Sun: Sun Java SDK 1.4.

    Как скомпилировать Java-файл с исходным текстом

    Решение, вообще говоря, совершенно банально – вызвать стандартный компилятор javac!

    На первый взгляд подобное решение кажется неизящным, сопряженным с массой проблем. Например, компилятор придется поставлять всем вашим пользователям. Другой пример – интеграция компилятора с вашей системой: сообщения компилятора должны выдаваться не на системную консоль, а «внутрь» вызывающей программы для обработки и визуализации в рамках вашего пользовательского интерфейса.

    Разработчики Java позаботились о том, чтобы эти проблемы легко решались.

    Прежде всего, стандартный компилятор javac входит в комплект поставки Sun Java SDK, распространяемого совершенно бесплатно. Правда, здесь есть одна тонкость.

    При формировании дистрибутива Java-приложения обычно принято включать в этот дистрибутив некий фрагмент Java SDK, так называемый JRE (Java2 TM Runtime Environment) – набор файлов, достаточный для запуска Java-приложения. В комплекте Sun Java SDK этот набор оформлен в виде подкаталога jre/. По умолчанию JRE не содержит компилятора javac (и ряда других полезных утилит из Java SDK). Включать в дистрибутив полный пакет Java SDK запрещено лицензионным соглашением фирмы Sun.

    Однако в том же лицензионном соглашении специально оговорено, что компилятор байт-кода javac вместе с необходимым вспомогательным JAR-файлом tools.jar можно включать в дистрибутив в дополнение к стандартному JRE, точнее, распространять совместно с Java-приложением. См. файл jre/license в комплекте поставки Sun Java SDK 1.4.1, раздел «JavaTM 2 runtime environment (j2re), standard edition, version 1.4.1_x supplemental license terms», пункт 3 и файл jre/README.txt, раздел «Redistribution of Java 2 SDK Files».

    Но самое приятное заключается в том, что в действительности компилятор фирмы Sun реализован на том же языке Java – в виде класса com.sun.tools.javac.Main и пакета вспомогательных классов, размещенных в архиве tools.jar. Утилита javac является всего-навсего «оболочкой», стартующей виртуальную машину Java и запускающей указанный класс. Архив tools.jar, как и саму утилиту javac, разрешается свободно распространять (в дополнение к стандартному JRE) совместно с Java-приложением.

    Это означает, что для компиляции Java-класса из Java-приложения нет необходимости обращаться к внешней утилите javac средствами операционной системы (методами Runtime.getRuntime().exec(. )). Можно напрямую воспользоваться классом com.sun.tools.javac.Main.

    Использование класса com.sun.tools.javac.Main предельно просто. Вот полный интерфейс этого класса (конструктор и public-методы):

    public static void main(String[] p0)

    public static int compile(String[] p0)

    public static int compile(String[] p0, PrintWriter p1)

    Для вызова компилятора нужно обратиться к к одному из двух его static-методов compile.

    В качестве аргумента p0 нужно передать массив строк-параметров, которые обычно передаются утилите javac, например:

    В качестве аргумента p1 во второй из этих методов нужно передать поток вывода, который будет использоваться для вывода всех сообщений компилятора. Таким образом можно «перехватить» все сообщения компилятора и перенаправить их в свой собственный буфер, который впоследствии можно проанализировать или показать пользователю.

    В качестве результата оба метода возвращают стандартный код возврата утилиты javac (errorlevel в терминах MS-DOS). 0 сигнализирует об успешном завершении, другие значения – о каких-либо ошибках.

    Приведем пример вызова компилятора путем обращения к классу com.sun.tools.javac.Main:

    String[] args= параметры утилиты javac;

    CharArrayWriter writer= new CharArrayWriter();

    int result= com.sun.tools.javac.Main.compile(

    /* — произошла какая-то ошибка */

    анализируем и, возможно, показываем пользователю строку сообщений компилятора writer.toString()

    Описанное решение очень просто, удобно и эффективно. Но у него есть серьезный недостаток.

    Класс com.sun.tools.javac.Main, как и все классы из пакетов com.sun.* и sun.*, является недокументированным. Приведенное выше описание использования класса опирается на здравый смысл и эксперименты, а не на официальную документацию фирмы Sun. Фирма Sun имеет полное право в очередной версии Java SDK изменить поведение или интерфейс этого класса или даже вообще исключить его.

    Указанная проблема не надумана.

    Действительно, все сказанное выше справедливо лишь для версии Sun Java SDK 1.4. В предыдущей версии, Sun Java SDK 1.3, тот же самый класс com.sun.tools.javac.Main имел совершенно другой интерфейс:

    public static void main(String[] p0)

    public int compile(String[] p0)

    Метод compile не был статическим, т.е. нуждался в создании экземпляра класса. Также отсутствовала версия метода, позволяющая указать собственный поток для сообщений компилятора.

    В версии Java SDK 1.3 для компиляции исходных текстов Java обычно использовался другой класс, sun.tools.javac.Main (расположенный все в том же архиве tools.jar). Этот класс также позволяет указать свой поток для сообщений компилятора. Причем этот класс полностью сохранил свой интерфейс при переходе от версии SDK 1.3 к SDK 1.4.

    Класс sun.tools.javac.Main имеет нестатический синхронизованный метод compile:

    public synchronized boolean compile(String[] p0)

    Поток для сообщений компилятора указывается в качестве первого параметра конструктора:

    public Main(OutputStream p0, String p1)

    (не PrintWriter, а более «архаичный» OutputStream). Смысл второго параметра конструктора я так и не выяснил, но найденные мной в Интернете примеры использования данного класса передавали в качестве p1 строку «javac».

    Код возврата утилиты javac возвращается отдельным методом:

    public int getExitStatus()

    Факт успешности компиляции можно также узнать по boolean-результату метода compile (false означает неудачу).

    Но и этот класс, несмотря на сохранение интерфейса, в действительности изменил свое поведение при переходе от версии SDK 1.3 к SDK 1.4.

    Во-первых, он был объявлен как устаревший («deprecated»). Конечно, предупреждение компилятора, появляющееся при попытке использовать данный класс («warning: sun.tools.javac.Main in sun.tools.javac has been deprecated»), можно и проигнорировать. Но фирма Sun почему-то решила добавлять аналогичное предупреждение в любое сообщение, выдаваемое самим компилятором sun.tools.javac.Main! Если попытаться использовать sun.tools.javac.Main для компиляции любого, даже совершенно корректного Java-файла, в любом случае будет выдано предупреждение «sun.tools.javac.Main has been deprecated». Чтобы избавиться от него, sun.tools.javac.Main придется использовать с ключом «-nowarn», но тогда вообще теряется возможность получать и анализировать предупреждения компилятора.

    Во-вторых, в версии Sun Java SDK 1.4 компилятор sun.tools.javac.Main попросту не всегда адекватно работает. На простых тестах это трудно обнаружить. Но когда я попытался скомпилировать с помощью этого компилятора все исходные тексты большого Java-проекта, обнаружилось, что некоторые сложные, но вполне корректные классы, прекрасно компилируемые «штатными» компиляторами и классом com.sun.tools.javac.Main, не компилируются с помощью sun.tools.javac.Main. В частности, компилятор sun.tools.javac.Main «сломался» на некоторых нетривиальных случаях перегрузки методов с аргументами примитивных типов, а также при попытке объявить метод toString() у некоторого вложенного класса. Это очень похоже на внутреннюю ошибку компилятора, которую фирма Sun не сочла нужным исправлять в устаревшем наборе классов.

    Есть также мелкие отличия в самом синтаксисе языка Java, понимаемом компиляторами sun.tools.javac.Main и стандартным com.sun.tools.javac.Main. (Ошибка, о которой ранее шла речь, не связана с этими мелкими отличиями – там действительно имела место явная ошибка компилятора.) Например, sun.tools.javac.Main разрешает импортировать (предложением import) конкретные классы, расположенные в корневом пакете – т.е. непосредственно в корне одного из каталогов, перечисленных в путях поиска CLASSPATH. Стандартный компилятор в современных версиях Java не допускает такого экзотического импорта – все импортируемые классы должны лежать внутри какого-либо пакета.

    Также можно заметить, что в версии Sun Java SDK 1.4 компилятор sun.tools.javac.Main работает примерно вдвое медленнее, чем com.sun.tools.javac.Main.

    Все сказанное означает, что использование для компиляции Java-файлов конкретных классов типа com.sun.tools.javac.Main или sun.tools.javac.Main – рискованное занятие. Соответствующий код придется заново тестировать при выпуске каждой новой версии Java SDK и, возможно, в какой-то момент его придется радикально переписывать.

    В случае com.sun.tools.javac.Main лично мне риск не кажется слишком большим. Похоже, что в этом классе фирма Sun наконец «довела до ума» решения, существовавшие в предыдущих версиях Java в этом же классе и в sun.tools.javac.Main. Трудно представить, чтобы возможности класса com.sun.tools.javac.Main в какой-то версии исчезли или радикально поменялись. Скорее всего, этот класс либо сохранится, либо превратится в легальный документированный класс, например, в пакете java.*, тогда необходимые изменения будут минимальны.

    Если необходимо надежное документированное решение, то на сегодня единственный доступный вариант – вызвать внешнюю утилиту javac одним из методов Runtime.getRuntime().exec(. ).

    Для этого, правда, эту утилиту нужно еще найти в файловой системе. Если данная утилита входит в состав дистрибутива и инсталлируется вместе с Java-приложением, то инсталлятор может сам позаботиться о том, чтобы полный путь к утилите javac передавался в Java-программу. Если же приложение дожно работать под управлением «чужого» Java SDK, инсталлируемого пользователем независимо от приложения, то можно поискать файлы:

    • bin/javac.exe (случай Microsoft Windows);
    • bin/javac (случай Unix/Linux);
    • bin/sparcv9/javac (случай Solaris SPARC)

    в каталоге System.getProperty(«java.home») и содержащем его каталоге. (Чаще всего System.getProperty(«java.home») соответствует подкаталогу jre/ в главном каталоге Sun Java SDK. Соответственно, утилита javac расположена в подкаталоге bin/ содержащего его каталога.)

    Для получения сообщений компилятора в данном случае можно использовать стандартную технику – чтение из потоков, возвращаемых методами getInputStream() и getErrorStream() объекта Process, полученного в результате обращения к методу Runtime.getRuntime().exec(. ).

    Вот как примерно это выглядит:

    Process p= Runtime.getRuntime().exec(массив_аргументов);

    /* первый элемент в массиве должен содержать полное имя файла утилиты javac, остальные элементы – параметры этой утилиты */

    final InputStreamReader is=new InputStreamReader(p.getInputStream());

    final InputStreamReader es=new InputStreamReader(p.getErrorStream());

    final StringBuffer out= new StringBuffer();

    final StringBuffer err= new StringBuffer();

    public void run() <

    char[] buf= new char[32768];

    > catch (Exception e) <

    public void run() <

    . (аналогичный цикл для es и err)

    int result= p.waitFor();

    /* дожидаемся завершения утилиты javac и получаем ее код завершения */

    /* — произошла какая-то ошибка */

    анализируем и, возможно, показываем пользователю сообщения компилятора out и err

    Обратите внимание: чтение потоков вывода и ошибок в отдельных параллельных потоках совершенно необходимо. Если этого не сделать, то вызов метода waitFor() может привести к зависанию в случае, когда внешняя программа выводит хоть что-нибудь в свои потоки вывода и ошибок.

    Как скомпилировать исходный текст Java, заданный в виде строки

    Итак, мы научились вызывать компилятор Java. Таким образом можно скомпилировать любой Java-файл – достаточно следовать инструкциям по использованию компилятора javac. Но что делать, если у нас нет готового Java-файла, размещенного где-то в файловой системе? Допустим, мы располагаем просто исходным текстом Java-программы в виде строки типа String – загруженным, скажем, из базы данных, или сгенерированным автоматически. Как скомпилировать такой текст?

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

    В стандартных библиотеках Java есть средства для создания временных файлов – это 2 статических метода createTempFile класса File, создающие файл с уникальным «случайным» именем. К сожалению, воспользоваться ими в данном случае невозможно. Новый Java-файл, создаваемый для сохранения заданного исходного Java-кода, не может иметь произвольное имя – его имя обязательно должно совпадать с именем public-класса, объявленного в этом исходном коде (если, конечно, таковой имеется). Кроме того, нужен какой-то каталог, куда будут записаны .class-файлы, полученные в результате компиляции.

    Все это означает, что нужна специальная функция, создающая временный подкаталог – так же, как File.createTempFile создает временный файл. Внутри этого подкаталога можно создать серию вложенных каталогов, соответствующую пакету, в котором должен располагаться компилируемый класс. Затем в самый внутренний каталог нужно записать Java-файл с исходным текстом, присвоив этому файлу имя, соответствующее имени компилируемого класса. Эту же структуру каталогов можно использовать для размещения результирующих .class-файлов, передав соответствующие инструкции компилятору javac.

    Написать функцию создания временного каталога не очень сложно. Достаточно использовать в качестве образца реализацию File.createTempFile в исходном тексте класса java.io.File. Основная идея – циклически генерировать более или менее случайные имена подкаталогов внутри каталога временных файлов операционной системы, для каждого подкаталога пытаться его создать (методом File.mkdir) и выйти из цикла, как только очередная попытка будет удачной (mkdir вернет true). Цикл генерации имен и создания подкаталога нужно синхронизовать относительно какого-либо глобального объекта точно так же, как это сделано в методе File.createTempFile. Чтобы найти умолчательный каталог временных файлов операционной системы, можно обратиться к переменной среды:

    В версии Sun Java SDK 1.4.1 эта переменная является документированной (в отличие от Sun Java SDK 1.3).

    Возникает также проблема автоматического удаления созданного временного подкаталога при выходе из Java-программы. В случае файла эта задача решалась бы методом deleteOnExit класса File. Но непустые подкаталоги этот метод удалять не умеет. Самое простое решение – написать функцию, рекурсивно удаляющую созданный временный каталог вместе со всеми файлами и подкаталогами, и вызвать ее в потоке, зарегистрированном методом Runtime.getRuntime().addShutdownHook(. ).

    Следующий вопрос, требующий некоторого внимания – как правильно записать Java-файл с исходным текстом. Исходный текст Java (представленный по условию в виде строки String) может содержать произвольные символы Unicode, а компилятор javac обычно используется с ASCII-файлами.

    Здесь есть два решения. Во-первых, начиная с версии Sun Java SDK 1.4, компилятор javac «понимает» дополнительный параметр «-encoding». Можно, например, сохранить файл в кодировке «UTF-8» и указать такую же кодировку в качестве параметра «-encoding». Чтобы сохранить текст в файле с заданной кодировкой, используется объект java.io.Writer, создаваемый вызовом:

    OutputStreamWriter writer= new OutputStreamWriter(new FileOutputStream(file),encoding)

    Во-вторых, компилятор javac – и в SDK 1.4, и в предыдущих версиях – поддерживает специальный способ кодирования Unicode-символов. А именно, цепочка символов вида uNNNN, где N – шестнадцатеричные цифры, в любом месте Java-файла воспринимается компилятором как Unicode-символ с кодом NNNN. Можно перед записью Java-файла заменить все символы с кодами і128 такими цепочками и записать полученный «чистый» ASCII-файл.

    После того как исходный текст скомпилирован, если компилятор сообщил об отсутствии ошибок (нулевой код возврата) и если параметры компилятора были заданы правильно (точнее, параметр «-d»), можно ожидать, что в структуре каталогов появятся все необходимые .class-файлы. Сколько их появится, заранее сказать невозможно (без полного синтаксического анализа исходного кода). Так, каждый анонимный класс породит отдельный .class-файл. Но, по крайней мере, должен появиться .class-файл, соответствующий главному public-классу, объявленному в исходном тексте (если, конечно, исходный текст не состоял из описаний одного или нескольких не-public-классов). В качестве последней «страховки» имеет смысл проверить существование этого .class-файла – его отсутствие говорит о неверных настройках компилятора.

    Затем остается только загрузить сгенерированный .class-файл. Для этого нужен собственный загрузчик классов – наследник ClassLoader, умеющий загружать классы из нестандартного каталога (в нашем случае из временного каталога, созданного внутри каталога временных файлов операционной системы). Эта задача подробно рассматривалась во второй части статьи: «ClassLoader – скрытые возможности». Здесь мы не будем на ней останавливаться.

    В завершение стоит добавить, что скомпилированные и загруженные классы имеет смысл кэшировать в оперативной памяти с помощью таблицы HashMap, ставящей каждому исходному тексту в соответствие результирующий объект Class – загруженный Java-класс. Если нужно скомпилировать исходный текст, идентичный тексту, который когда-то уже компилировался, то в большинстве случаев нет смысла компилировать его повторно – достаточно извлечь готовый класс из кэша.

    Eval на Java: интерпретатор формул

    Теперь мы располагаем чрезвычайно мощным инструментом. Мы умеем компилировать произвольные исходные тексты Java, заданные в виде строковой переменной, и загружать получаемые при этом классы. Обычно такую технику называют самопрограммированием. Эта возможность традиционно присутствует в медленных интерпретируемых скриптовых языках типа JavaScript или Perl, но отсутствует в высокоэффективных компилируемых языках (к которым относится и Java), исключая разве что Ассемблер. Фактически, мы снабдили язык Java самопрограммированием.

    Но пока что пользоваться этой техникой не очень удобно.

    var result= eval(formula);

    Мы попробуем реализовать аналогичную технику в рамках Java.

    Требуется реализовать статический метод некоторого класса-библиотеки (допустим, Evaluator), имеющий следующий интерфейс:

    public Object eval(String javaExpression, Object context) throws Exception

    В качестве javaExpression передается произвольное выражение Java, например то же «a+b». В качестве context передается объект, предоставляющий «пространство имен». Это значит, что в выражении javaExpression должна быть возможность без дополнительных уточнений обращаться ко всем public- и, может быть, protected-членам объекта context. Так, в случае формулы «a+b» объект context должен содержать числовые (или строчные) поля a и b. В своем результате eval возвращает объект Java, получаемый в результате интерпретации выражения javaExpression.

    public static class Context <

    public void НекоторыйМетод() <

    Context c= new Context();

    String formula= «new Integer(a+b)»;

    Integer result= (Integer)Evaluator.eval(formula,c);

    Язык Java по обыкновению создает затруднения при попытке работать с примитивными типами на общих основаниях – их приходится заменять соответствующими классами-оболочками. Специально для упрощения работы с примитивными типами имеет смысл дополнить основной метод eval версиями evalInt, evalLong, evalFloat, evaDouble, evalBoolean, возвращающими результат соответствующего примитивного типа. Тогда последние 2 строки примера выглядели бы проще:

    String formula= «a+b»;

    int result= Evaluator.evalInt(formula,c);

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

    public class НекоторыйКласс <

    public void НекоторыйМетод() <

    String formula= «a+b»;

    int result= Evaluator.evalInt(formula,this);

    Это практически так же удобно, как и eval в скриптовых языках.

    Как решить поставленную задачу – реализовать описанные методы eval, evalInt и прочие?

    Существует достаточно элементарное частичное решение.

    Потребуем, чтобы все обращения к членам объекта context в формуле javaExpression производились не напрямую, а через некоторую дополнительную переменную – ссылку на объект context. Формула в этом случае приобретает примерно такой вид: «c.a+c.b» (имя ссылки «c» могло бы быть дополнительным аргументом метода eval).

    В этом варианте задачу решить легко. Конструируем «на лету» текст Java-класса:

    какие-нибудь еще полезные import, которые могут пригодиться внутри формулы

    public class ___ExpressionNNN <

    public static int ___performEval(

    int returnValue= текст_формулы;

    Вместо NNN подставляется некоторый уникальный индекс – свой для каждого текста формулы. (Если разные формулы будут интерпретироваться с помощью разных классов, то эти классы можно будет кэшировать и не компилировать повторно – смотри о кэшировании в конце предыдущего раздела.) Вместо «имя_класса_context» подставляется context.getClass().getName(), вместо «текст_формулы» – значение javaExpression.

    Тип результата int метода ___performEval и тип переменной returnValue соответствуют варианту метода evalInt. Другие варианты – eval, evalDouble и прочие – должны использовать другой тип (соответственно Object, double, и т. д.).

    Имя «c» аргумента ___performEval – это имя, под которым объект context будет доступен внутри формулы. Оно может быть дополнительным аргументом методов eval, evalInt, .

    Компилируем полученный текст класса и загружаем скомпилированный класс, как описано в предыдущих разделах. Затем средствами отражений вызываем его статический метод ___performEval, передавая ему в качестве аргумента наш объект context, и возвращаем полученный результат. Задача решена.

    Дополнительная переменная returnValue позволяет передавать в качестве формулы javaExpression фрагменты кода, состоящие более чем из одного оператора Java. В этом случае по умолчанию результатом вызова eval окажется результат первого оператора, но всегда можно будет в последнем операторе написать что-нибудь вроде

    и тем самым вернуть другой результат.

    Приведенное решение, разумеется, неизящно. В формуле приходится ссылаться на члены класса-контекста через громоздкую запись типа «c.a». Попробуем избавиться от явной ссылки «c.». Язык Java разрешает ссылаться непосредственно, без дополнительных уточнений, на члены текущего класса, его предков, члены класса, по отношению к которому текущий является вложенным, и члены предков этого класса. Если класс, которому принадлежит метод ___performEval, унаследовать от класса context.getClass().getName() или вложить в другой класс, унаследованный от context.getClass().getName(), то к членам этого предка можно будет обращаться из нашей формулы непосредственно. Метод ___performEval, разумеется, нужно будет сделать нестатическим.

    Таким способом формулу типа «a+b» скомпилировать удастся – синтаксически все будет соблюдено. Но как добиться, чтобы a и b ссылались именно на члены данного экземпляра context, переданного в качестве аргумента в метод eval (или evalInt, evalDouble, . )? Все, что можно сделать «легально» для исполнения формулы – создать новый экземпляр для нашего нового класса (или по экземпляру для серии вложенных классов) и указать именно этот новый экземпляр при вызове метода ___performEval через отражения. Виртуальная машина Java не позволит «подменить» экземпляр нового временного класса объектом context, так же context является предком нашего нового объекта, и его нельзя использовать там, где декларировано использование объекта-потомка.

    Приходит в голову банальное решение – перед вызовом ___performEval скопировать все поля объекта context в заново созданный экземпляр наследника context.getClass().getName(), а в конце скопировать все поля обратно (на случай, если формула их изменяла). Все это в принципе осуществимо средствами отражений, но вряд ли такое решение можно назвать качественным. Если полей много, такое копирование может занять много времени. Кроме того, в сложных случаях такое поведение попросту может оказаться ошибочным. Представьте себе, что некоторый внешний поток постоянно наблюдает за состоянием экземпляра context, и каждое изменение его состояния, в том числе внутри формулы (в результате вызова методов context), должно быть немедленно обнаружено. Очевидно, при описанном подходе изменения вообще обнаружены не будут. Все изменения будут произведены с другим объектом – копией context, а в момент присваивания новых полей полям экземпляра context наш метод eval «не будет знать», как сообщить об этих изменениях.

    Хотелось бы попытаться все-таки получить доступ непосредственно к экземпляру context.

    Решение существует. Для этого нам придется «обмануть» компилятор – создать .class-файл, который не может быть создан «законным» компилятором javac.

    Рассмотрим следующий код Java:

    какие-нибудь еще полезные import, которые могут пригодиться внутри формулы

    public class ___ExpressionNNN

    public class ___Performer <

    public int ___performEval() <

    int returnValue= текст_формулы;

    Все, как в прошлый раз, но теперь добавился вложенный класс ___Performer. Метод ___performEval теперь нестатический и не обладает аргументом, а внешний класс ___ExpressionNNN унаследован от context.getClass().getName().

    Во что компилируется такой исходный код?

    В результате компиляции получается 2 .class-файла – ___ExpressionNNN.class и ___ExpressionNNN$___Per-former.class. Рассмотрим их внимательно с помощью какого-нибудь дизассемблера, например утилиты javap (с ключами -private и -c).

    Класс ___ExpressionNNN здесь – «пустышка». Он не содержит ни одного члена, кроме пустого (автоматически добавленного) конструктора. Этот класс – наследник context.getClass().getName().

    Класс ___Performer имеет следующий вид:

    public class ___ExpressionNNN$___Performer <

    private final ___ExpressionNNN this$0;

    реализация: копирует p0 в this$0

    public int ___performEval()

    реализация, содержащая скомпилированный текст нашей формулы: для обращений к a,b и другим членам context используется ссылка this$0

    Обратите внимание на поле this$0. Это «скрытый механизм» языка Java, позволяющий добираться из вложенных нестатических классов до текущих экземпляров содержащих их внешних классов. Заметьте: хотя идентификатор this$0 является корректным с точки зрения синтаксиса Java, компилятор javac не позволит объявить поле с таким именем в обычном классе – идентификатор «зарезервирован для внутреннего использования».

    Вызов ___performEval через отражения выглядит следующим образом:

    • Вначале нужно создать экземпляр класса ___ExpressionNNN (обычным вызовом Class.newInstance).
    • Затем нужно отыскать (единственный) конструктор класса ___ExpressionNNN$___Performer и вызвать его (через java.lang.reflect.Constructor), передав в качестве аргумента ссылку на созданный экземпляр ___ExpressionNNN. Будет создан экземпляр ___ExpressionNNN$___Performer.
    • Затем нужно обычным образом (через java.lang.reflect.Method) отыскать и вызвать метод ___performEval, передав методу invoke в качестве параметра ссылку на только что созданный экземпляр ___ExpressionNNN$___Performer.

    Если бы на шаге 2 удалось вместо нового экземпляра ___ExpressionNNN «подсунуть» конструктору наш экземпляр context, задача была бы решена. Метод ___performEval работал бы (через ссылку this$0) с нашим экземпляром, т.е. выполнил бы нашу формулу в требуемом контексте.

    Как уже говорилось выше, виртуальная машина Java не допускает передачи в качестве аргумента типа ___ExpressionNNN экземпляра его предка context.getClass().getName().

    Но можно слегка изменить класс ___Performer. Действительно, если в классе ___Performer везде заменить имя класса ___ExpressionNNN на context.getClass().getName() (прежде всего тип поля this$0 и тип аргумента конструктора), то этот класс останется с точки зрения виртуальной машины вполне корректным. Класс ___ExpressionNNN не добавляет к своему предку ни одного нового члена, к которому метод ___performEval мог бы попытаться обратиться через this$0. При работе с таким скорректированным классом через отражения на шаге 2 требования к аргументу конструктора были бы слабее: можно было бы передать ссылку на экземпляр класса context.getClass().getName(), в частности на наш экземпляр context.

    Такую коррекцию невозможно сделать «легальным» путем, изменяя исходный текст и вызывая компилятор javac. Стандартный компилятор при генерации вложенного класса непременно придаст ссылке this$0 точный тип того класса, в который вложен данный. Но можно скорректировать уже скомпилированный .class-файл ___ExpressionNNN$___Performer.class.

    Это не так сложно, как кажется. Формат .class-файлов спроектирован очень грамотно и удобен для наших целей.

    В начале файла идет так называемый «пул констант», в котором собраны все символьные идентификаторы (в кодировке UTF-8), в том числе имена всех упоминаемых классов. Весь .class-файл, в частности пул констант, организован в виде последовательности секций примерно такого вида:

    • код_типа_секции
    • длина_секции
    • содержимое_переменной_длины

    Если в произвольной секции изменить содержимое и соответствующим образом подправить длину секции (размер файла при этом тоже поменяется), то файл сохранит свою корректность. В .class-файле не встречаются прямые смещения от начала файла или длины в байтах каких-то фрагментов файла, за исключением длин элементарных секций.

    Имя класса ___ExpressionNNN может встречаться внутри файла ___ExpressionNNN$___Performer.class в виде строковых констант трех видов:

    • непосредственно строка «___ExpressionNNN» (используется не всеми компиляторами);
    • строка «L___ExpressionNNN;» – внутреннее имя типа ___ExpressionNNN: именно таким образом виртуальная машина «именует» классы «внутри себя» (для примитивных типов и массивов используются другие обозначения);
    • строка «(L___ExpressionNNN;)V» – сигнатура конструктора или любого другого метода с единственным аргументом типа ___ExpressionNNN.

    Если бы наш класс ___ExpressionNNN был вложен внутрь какого-либо некорневого пакета (в наших примерах он размещается в корневом пакете), то в его полном имени нужно было бы заменить точки символами “/”.

    Строковые константы, в частности перечисленные выше, представлены в пуле констант в виде следующих цепочек байтов:

    • 1 байт: 1 (код строкового типа);
    • 2 байта: длина length строковой константы в кодировке UTF-8 (сначала старший байт, потом младший);
    • length байтов: содержимое строковой константы в кодировке UTF-8.

    Все, что нам нужно сделать – загрузить файл ___ExpressionNNN$___Performer.class в виде массива байтов, отыскать в нем все такие цепочки байтов для строк «___ExpressionNNN», «L___ExpressionNNN;», «(L___ExpressionNNN;)V» и заменить их соответствующими цепочками для строк «XXXX», «LXXXX;», (LXXXX;)V». Здесь XXXX – полное имя класса context, в котором точки (разделители имени пакета) заменены знаком «/»:

    Полученный новый массив байтов (другой длины) нужно записать обратно в файл ___ExpressionNNN$___Performer.class.

    Если имя ___ExpressionNNN не используется в Java-приложении ни для каких других целей – для максимальной уверенности можно заменить его чем-нибудь вроде ___Expression_Asj5Sjl3_NNN, – то полученный .class-файл будет вполне корректным. Остается загрузить его, создать экземпляр, передав конструктору в качестве аргумента наш объект context, и выполнить метод ___performEval(). Задача решена полностью.

    Возможно, в формулах имеет смысл открыть непосредственный доступ (без уточняющего имени класса) к какому-либо набору стандартных функций или констант. Например, в математических формулах естественнее смотрелась бы запись «sin(a+PI/4)», а не «Math.sin(a+Math.PI/4)». Для этого достаточно автоматически добавить желаемый набор функций и констант во вложенный класс ___Performer.

    Некоторое неудобство приведенного решения связано с тем, что класс объекта context обязан быть public, а в случае локального класса – public static. В частности, недопустимо использовать анонимные классы или локальные классы, объявленные внутри методов. В противном случае нам попросту не удастся унаследовать от него класс ___ExpressionNNN, расположенный, вообще говоря, в совершенно другом пакете (в наших примерах – в корневом пакете).

    Очевидно также, что для достижения хорошей эффективности все скомпилированные формулы необходимо кэшировать, чтобы одна и та же формула не компилировалась повторно. Имеет смысл для каждой пары «формулы, экземпляр context» сохранить (в таблице HashMap) готовый объект java.lang.reflect.Method для метода ___performEval и экземпляр вложенного класса ___Performer, чтобы при повторном обращении к eval осталось просто вызвать метод invoke. Также есть смысл проверить, что одна и та же формула с одним и тем же context вызывается повторно, и в этом случае перейти на особо быструю ветку – не обращающуюся к таблице HashMap. Подобная оптимизация позволяет достичь чрезвычайно высокого быстродействия, недостижимого для интерпретируемых скриптовых языков: накладные расходы будут укладываться в доли микросекунды (на компьютерах класса Pentium-III 800).

    Идея описанного выше изящного решения принадлежит моему коллеге, Алексею Вылегжанину (anv@siams.com).

    Разумеется, это решение (в отличие от первого варианта, требующего использования в формулах ссылки «с.»), не является стопроцентно переносимым. Оно зависит от особенностей компилятора javac, которые в принципе могут измениться в следующей версии Java. Например, компилятору ничто не мешает вставить в класс ___Performer явную проверку, что поле this$0 принадлежит нужному типу, причем сформировать имя типа динамически путем сложения строк «___Expres» и «sionNNN». Тогда скорректированный нами класс работать не будет. Кажется маловероятным, что подобное произойдет. Но на всякий случай описанное решение желательно заново тестировать с каждой новой версией Sun Java SDK.

    Технология интерпретации формул на Java может оказаться чрезвычайно полезной в сложных приложениях, гибко настраиваемых пользователем. Скажем, процедура построения графика или статистического анализа может принимать на вход не только массив чисел, но и некоторую набранную пользователем аналитическую формулу. При заполнении сложной формы с множеством параметров можно разрешить в некоторых полях указывать формулы, оперирующие другими полями формы (например, «предыдущее_поле + 1»). Можно снабдить пользователя простейшим формульным калькулятором, который всегда под рукой и обладает функциями, специфичными для данного приложения.

    Единственной серьезной проблемой может оказаться безопасность. Если разрешить пользователю Java-приложения самостоятельно писать формулы, исполняемые системой, то пользователь сможет, вообще говоря, выполнить любой законный оператор языка Java – скажем, добраться до содержимого диска. Если это недопустимо, то придется разрабатывать специальный менеджер безопасности («песочницу») для исполнения формул или жестко контролировать текст формулы.

    Нашу экскурсию по миру отражений можно считать законченной. Мы рассмотрели основные задачи по управлению классами: доступ к произвольным классам и их членам, загрузку классов из произвольного источника и, наконец, компиляцию новых классов из Java-файлов либо «на лету». Эти технологии позволяют профессионально решать по-настоящему сложные задачи, а также могут быть чрезвычайно удобны и в небольших утилитах.

    Я сознательно не стал приводить Java-реализацию большинства из описанных выше технологий. Кроме того что это было бы чрезвычайно громоздко, мне кажется, что подобные задачи лучше разработчику решать самостоятельно, если только готовое решение не существует в виде стандартизованных и общеизвестных библиотек фирмы Sun или другой фирмы, заслуживающей не меньшего доверия. Разумеется, я располагаю законченной библиотекой, воплощающей все приведенные идеи, в том числе динамическую загрузку классов, описанную во второй части статьи. Но я никоим образом не берусь утверждать, что моя библиотека достаточно грамотно реализована и хорошо протестирована – настолько же, насколько это можно сказать о библиотеках фирмы Sun. Если вы реализуете необходимые функции самостоятельно, то, по крайней мере, вы сможете достаточно свободно отлаживать их и совершенствовать, что было бы гораздо труднее сделать с чужим кодом.

    При написании статьи были использованы официальные материалы фирмы Sun, приведенные на сайте http://java.sun.com, а также следующие книги:

    • Арнолд К., Гослинг Дж., Холмс Д. Язык программирования Java. Издательский дом «Вильямс», Москва – С.-Петербург – Киев, 2001.
    • Вебер Дж. Технология Java(TM) в подлиннике. «BHV – Санкт-Петербург», С.-Петербург, 1997.

    Все приведенные в статье фрагменты кода я старался тщательно тестировать. Если вы обнаружите в моих текстах ошибки или неточности, а также в случае любых вопросов буду рад вашим письмам.

    Java — Java и компеляция

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

    Вы должны были скачать компилятор Java или загрузить и установить IDE, NetBeans или Eclipse. Возможности компиляторов были ограничены.

    Сегодня существует ряд онлайн-компиляторов и IDE, имеющих ряд преимуществ над оффлайновыми версиями:

    • отсутствие загрузок;
    • отсутствие установок;
    • отсутствие переменных пути;
    • возможность программировать на телефоне или планшете;
    • быстрый запуск;
    • легкий обмен — преподаватели делятся кодом со студентами, студенты делятся своими заданиями, размещают любые вопросы. Это очень просто! Просто скопируйте ссылку и вставьте ее;

    Некоторые онлайн-компиляторы, такие как Codiva и JDoodle, подходят для обучения программированию. Они просты и легко начать. Функции, оптимизированные для этого конкретного случая использования.

    На другом конце спектра есть онлайн-IDE, такие как Codenvy и Cloud 9. Они обеспечивают полную среду разработки, которую могут использовать профессионалы. Многие стартапы используют его для обеспечения согласованной предварительно настроенной среды разработки, которую могут использовать все их разработчики, без обслуживания.

    В этой статье мы рассмотрим онлайн-компиляторы и IDE, подходящие как для студентов, так и для профессионалов.

    JDoodle — популярный онлайн-компилятор для Java. Он начинался для Java, но теперь поддерживает почти 70 языков.

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

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

    Недостаток — он поддерживает только один файл. Не получится использовать его при обучении концепции пакетов, инкапсуляции и подобного. Большой плюс – не надо регистрации.

    Codiva.io – отличный выбор для онлайн-компилятора Java.

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

    При наборе программы, будут показываться ошибки – как в обычной IDE на компьютере. Поддерживаются только три языка: Java, C и С++, нет тем пользовательского интерфейса, нет выбора компилятора.

    Требуется регистрация. Codiva поддерживает Java 9, но не поддерживает модули Java 9. В целом, это отличный инструмент для кода на Java.

    Rextester начинался как тестер регулярных выражений, позже вырос в онлайн-IDE. Он поддерживает около 30 языков, включая Java, но он более популярен среди пользователей C#.

    Rextester позволяет переключаться между несколькими виджетами редактора в соответствии с вашими предпочтениями.

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

    Rextester поддерживает только один файл, и класс должен иметь имя Rextester. На момент написания этой статьи поддерживалась только Java 8.

    Online GDB — это онлайн-инструмент, который поддерживает встроенный отладчик. Если вы пишете какую-то сложную программу и должны отлавливать ошибки, то отладчик пригодится.

    Автозавершение кода и симпатичная консоль – неплохой онлайн компилятор для Java и некоторых других языков программирования

    Browxy когда-то был очень популярен, но начал отставать. Он поддерживает несколько файлов. В настоящее время только Java 8.

    Browxy — это единственный онлайн-компилятор, который практически не имеет ограничений. Можно даже отправлять сетевые запросы на внешние URL-адреса.

    Это будет очень полезно, если вы хотите практиковать вызов вызовов API. Он также поддерживает апплеты (хотя их уже вряд ли кто-то использует).

    Browxy компилируется и работает быстро, мы получаем ответ в течение доли секунды.

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

    Он поддерживает около 60 + языков. Ideone не был обновлен для поддержки Java 9. IDEOne предоставляет API для компиляции в качестве службы, которую можно использовать для создания собственной интегрированной среды разработки для использования на веб-сайте.

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

    Заметки разработчика

    узнал сам – поделись с другими

    27 октября 2012 г.

    Компиляция java-проекта вручную

    Чтобы до конца разобраться в том, как собирается ваш проект при помощи IDE или специальных сборщиков вроде Ant, прежде нужно хотя бы один раз собрать проект вручную.

    Вкратце процесс сборки состоит из следующих шагов:

    1. Компиляция байт-кода в виде *.class-файлов из исходников.
    2. Упаковка полученных файлов с байт-кодом, а также необходимых конфигурационных файлов в jar-архив.
    3. Добавление в файл MANIFEST.MF внутри архива относительных путей до необходимых библиотек.

    Компиляция из исходников в байт-код производится при помощи программы javac, которая входит в JDK. Перейдите в директорию вашего java-проекта и выполните следующую команду, подставляя, где необходимо, ваши значения:

    javac -d директория_для_байт_кода -classpath пути_до_необходимых_библиотек -sourcepath директория_с_исходниками путь_до_класса_с_main

    Опция с — создать архив
    Опция v — отображать служебную информацию в процессе создания архива
    Опция f — производить вывод не в консоль, а в файловую систему
    В качестве имени файла архива можно указать любое имя. Архиву, как правило, даётся расширение jar.

    Обратите внимание на точку в конце — это указание того, что в текущей директории находятся файлы байт-кода. Если бы jar запускался из другой директории, вместо точки следовало бы указать конкретный путь.

    В результате вы получите jar-архив, в который буду запакованы файлы байт-кода, дополнительные конфигурационные файлы, а также будет создан стандартный файл /META-INF/MANIFEST.MF — нам его необходимо модифицировать.

    Откройте архиватором созданный архив и модифицируйте файл MANIFEST.MF, добавив в него две строки:

    Class-Path: пути_до_файлов_библиотек
    Main-Class: полное_имя_класса_с_main


    В Class-Path следует перечислить все необходимые файлы библиотек с относительными путями до них. Если библиотек несколько, то пути разделяются пробелами. Также рекомендуется положить все эти библиотеки в одну директорию.
    В Main-Class нужно указать полное имя класса с методом main (т.е. с указанием пакета, например org.test.SimpleMain).

    Внимание! Проследите за тем, чтобы в конце файла MANIFEST.MF была пустая строка (ещё один перевод строки) — это требование спецификации. В противном случае возможна некорректная обработка этого файла.

    Сохраните изменения в архиве. Теперь вы можете запустить полученный архив при помощи команды java -jar имя_архива и ваше приложение начнёт свою работу.

    Компилируем и запускаем Java-файлы из Notepad++

    Приветствую всех! Для начала хочу сказать, что я изучаю курс Java 1 и, по рекомендации преподавателя, решил воспользоваться Notepad++ для экспериментов с кодом и подготовки домашнего задания. Нужно было настроить Notepad++ таким образом, чтобы я мог запускать код и сразу видеть результат выполнения программы. В интернете я нашёл несколько способов настройки Notepad++ под работу с Java. Самый быстрый из них я немного дополнил и хочу показать вам. За основу я взял данный гайд на английском — там всё кратко и сжато. Я же попытаюсь подробно описать процесс настройки, расскажу еще о нескольких дополнительных возможностях и решении некоторых проблем программы и плагина.

    Если у вас что-то не получится или вы найдёте неточность в статье, напишите мне, и я с радостью попытаюсь помочь решить вашу проблему или устранить ошибку. Также буду рад вашим предложениям и дополнениям.

    Готовим Java-инструменты и Notepad++

    1. У вас должны быть установлены JDK + JRE и правильно настроена переменная среды PATH.
    2. Скачиваем и устанавливаем последнюю версию NotePad++. Выберите версию 32-bit или 64-bit. Я буду показывать пример с 64-bit.
    3. Скачиваем плагин NppExec. По ссылке представлены несколько версий. Я выбрал v0.5.9.9 dev, архив — NppExec20200628_dll_x64-2.zip. В имени архива указана версия — x64. Если вы скачали Notepad++ для 32-bit, вам нужен другой файл — NppExec20200628_dll.zip . Если не получится, попробуйте другую версию — главное чтобы там был NppExec.dll файл. Распакуйте архив в директорию, где у вас установлена Notepad++\plugins. В моём случае полный путь выглядит так: «C:\Program Files\Notepad++\plugins«. Чтобы плагин заработал, перезапустите Notepad++, если он был запущен ранее.

    Настраиваем плагин NppExec

    В меню выберите Плагины (Plugins) -> NppExec и выполните следующие действия:

    • отключите «Console Command History»;
    • включите «Save all files on execute»;
    • включите «Follow $(CURRENT_DIRECTORY)».

    4. Добавьте команду компиляции:

    • Перейдите в Плагины (Plugins) > NppExec > Execute. (или просто нажмите F6 — горячая клавиша).
    • В открывшемся окне введите следующие команды:
    • Нажмите кнопку Save. и введите имя скрипта, например — Compile, так как этот скрипт как раз будет выполнять компиляцию. Нажмите Save.

    5. Чтобы добавить новые команды, снова перейдите в Плагины (Plugins) > NppExec > Execute. У вас будет в окне “ Command(s): «cd “$(CURRENT_DIRECTORY)” javac $(FILE_NAME)». Здесь нет кнопки для добавления новой команды, поэтому вы просто заменяем код на новый:

    Затем Save. Обязательно измените имя скрипта, к примеру — «Run». Этот скрипт будет запускать выполнение программы. Нажмите Save.

    Повторите действия ещё раз, введите:

    cd «$(CURRENT_DIRECTORY)»
    javac $(FILE_NAME)
    if $(EXITCODE) !=0 goto exit
    java $(NAME_PART)
    :exit

    Сохраните и назовите скрипт — Compile and run, по смыслу уже понятно, что данный скрипт сначала будет компилировать, а затем запускать программу. Нажмите ОК.

    6. Добавьте ваши команды в меню Макросы (Macro). Для этого:

    1) Перейдите в Плагины (Plugins) > NppExec > Advanced options…

    2) Поставьте галочку напротив Place to the Macros submenu.

    3) Введите «Run» в поле » Item name: «, затем нажмите на всплывающий список » Associated script: » и выберите скрипт под названием Run.

    После этого нажмите на » Add/Modify «

    и в области Menu items * вы увидите, что появилась строка » Run :: Run «.

    Повторите эти действия с скриптами Compile и Compile and run, чтобы они тоже появились в окне Menu items *.

    Нажмите Ок и перезапустите Notepad++.

    7. Протестируйте созданные вами скрипты, открыв или создав java-файл. К примеру, создадим файл Test.java (при сохранении обязательно укажите тип — Java source file (*.java) ) и запишем в него:

    public static void main(String[] args) <

    8. Перейдите в меню Макросы (Macro) и нажмите Compile. Затем снова зайдите в меню и нажмите Run. Или вы можете выполнить два действия сразу, по кнопке Compile and run.

    9. Если вы получили ошибку при компиляции или выполнении программы, измените команду Compile или Run (в зависимости от того где у вас ошибка).

    Если ошибка при компиляции измените команду Compile:

    C:\Program Files\Java\jdk-9\bin\javac $(FILE_NAME)

    Если ошибка при выполнении, измените команду Run:

    C:\Program Files\Java\jdk-9\bin\java $(NAME_PART)

    Во второй строке укажите путь к файлу javac или java на вашем компьютере. Путь к этим файлам может отличаться, будьте внимательны! Ошибка может быть вызвана тем, что Windows не знает путь к программе javac, или тем, что вы используете Notepad++ Portable Version. После изменений в Compile и Run не забудьте также про Compile and run.

    Вот пример ошибки:

    По сообщениям из консоли видно, что компиляция прошла успешно, но при выполнении программы произошла ошибка версий, а также не были найдены java.dll и SE Runtime Environment. Поэтому мы указываем путь для выполнения программы в скриптах Run и Compile and run.

    После этого программа выполняется успешно:

    10. Чтобы было удобнее компилировать и выполнять программы, настроим горячие клавиши. Перейдите в меню Запуск (Run) и выберите «Изменить горячую клавишу/удалить команду (Modify Shortcut/Delete Command…)».

    Во вкладке Plugin Commands настройте горячие клавиши для быстрого запуска скриптов. Найдите имена Compile, Run, Compile and Run, по двойному клику задайте удобное вам сочетание клавиш для быстрого запуска. У меня, например, Compile and Run настроен на клавиши Ctrl + Shift + C.

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

    11. Если вы хотите изменить цветовую схему (тему) или выбрать другой шрифт, настроить его размер и начертание, выберите меню Опции (Settings) > Определение стилей (Style configurator…) :

    12. При написании кода мне очень помогает автовставка символов. Например, когда я ставлю круглую скобку “(“, закрывающая скобка “)“ ставится автоматически. Кто работал в средах разработки (наподобие IDEA IntelliJ), знаком с этим и согласится, что это, вроде бы, мелочь, а удобно. По умолчанию автовставка выключена. Для её включения зайдите в Опции (Settings) > Настройки (Preferences) > Автозавершение (Auto-Completion) и выберите, что вам нужно. Если нужных символов нет, можете сами создать “Заказную пару”(Matched pair).

    13. При выполнении программ вы можете столкнуться с проблемой кодировки в консоли. Пример:

    Решается это очень просто. Переходим в меню Плагины (Plugins) > NppExec > Console Output

    И здесь вы должны выставить в значении Console Output is — «UTF-8».

    Если вдруг это не помогло, попробуйте поставить галочку — Console Input is the same as Output.

    14. Если вы хотите отключить проверку орфографии, нажмите кнопку «Spell Check Document Automatically (ABC)» на панели задач. После этого

    кириллица не будет подчеркиваться красным. Вы также можете включить/выключить консоль по кнопке, которая расположена справа — «Show Console Dialog».

    В разделе Плагины (Plugins) > NppExec > Advanced Options вы можете настроить отображение консоли при запуске программы. Выберите нужный вариант (Yes/No/Auto) в разделе Console — опция Visible (on start).

    А ещё вы можете выбрать язык по умолчанию, для этого перейдите в Опции (Settings) > Настройки (Preferences) > Новый документ (New Document) и выберите Синтаксис по умолчанию (Default language). В нашем случае — Java. Теперь, когда вы открываете новую вкладку или запускаете Notepad++ с пустым файлом, типом по умолчанию будет Java и вам не придется выбирать его каждый раз.

    15. Для удобства восприятия вы можете отключить системные (внутренние) сообщения консоли. Перейдите по вкладке Плагины (Plugins) > NppExec > No internal messages. После того, как вы поставите галочку, выполнение программы в консоли будет выглядеть так :

    При этом ошибки будут всё равно выводиться в консоль, что весьма удобно:

    Надеюсь, эта инструкция была вам полезна. Если заметите ошибки или неточности, напишите, и я исправлю! Всем удачи!

    Приветствую всех! Для начала хочу сказать, что я изучаю курс Java 1 и, по рекомендации преподавателя, решил воспользоваться Notepad++ для экспериментов с кодом и подготовки домашнего задания. Нужно было настроить Notepad++ таким образом, чтобы я мог запускать код и сразу видеть результат выполнения программы. В интернете я нашёл несколько способов настройки Notepad++ под работу с Java. Самый быстрый из них я немного дополнил и хочу показать вам. За основу я взял данный гайд на английском — там всё кратко и сжато. Я же попытаюсь подробно описать процесс настройки, расскажу еще о нескольких дополнительных возможностях и решении некоторых проблем программы и плагина.

    Если у вас что-то не получится или вы найдёте неточность в статье, напишите мне, и я с радостью попытаюсь помочь решить вашу проблему или устранить ошибку. Также буду рад вашим предложениям и дополнениям.

    Готовим Java-инструменты и Notepad++

    1. У вас должны быть установлены JDK + JRE и правильно настроена переменная среды PATH.
    2. Скачиваем и устанавливаем последнюю версию NotePad++. Выберите версию 32-bit или 64-bit. Я буду показывать пример с 64-bit.
    3. Скачиваем плагин NppExec. По ссылке представлены несколько версий. Я выбрал v0.5.9.9 dev, архив — NppExec20200628_dll_x64-2.zip. В имени архива указана версия — x64. Если вы скачали Notepad++ для 32-bit, вам нужен другой файл — NppExec20200628_dll.zip . Если не получится, попробуйте другую версию — главное чтобы там был NppExec.dll файл. Распакуйте архив в директорию, где у вас установлена Notepad++\plugins. В моём случае полный путь выглядит так: «C:\Program Files\Notepad++\plugins«. Чтобы плагин заработал, перезапустите Notepad++, если он был запущен ранее.

    Настраиваем плагин NppExec

    В меню выберите Плагины (Plugins) -> NppExec и выполните следующие действия:

    • отключите «Console Command History»;
    • включите «Save all files on execute»;
    • включите «Follow $(CURRENT_DIRECTORY)».

    4. Добавьте команду компиляции:

    • Перейдите в Плагины (Plugins) > NppExec > Execute. (или просто нажмите F6 — горячая клавиша).
    • В открывшемся окне введите следующие команды:
    • Нажмите кнопку Save. и введите имя скрипта, например — Compile, так как этот скрипт как раз будет выполнять компиляцию. Нажмите Save.

    5. Чтобы добавить новые команды, снова перейдите в Плагины (Plugins) > NppExec > Execute. У вас будет в окне “ Command(s): «cd “$(CURRENT_DIRECTORY)” javac $(FILE_NAME)». Здесь нет кнопки для добавления новой команды, поэтому вы просто заменяем код на новый:

    Затем Save. Обязательно измените имя скрипта, к примеру — «Run». Этот скрипт будет запускать выполнение программы. Нажмите Save.

    Повторите действия ещё раз, введите:

    cd «$(CURRENT_DIRECTORY)»
    javac $(FILE_NAME)
    if $(EXITCODE) !=0 goto exit
    java $(NAME_PART)
    :exit

    Сохраните и назовите скрипт — Compile and run, по смыслу уже понятно, что данный скрипт сначала будет компилировать, а затем запускать программу. Нажмите ОК.

    6. Добавьте ваши команды в меню Макросы (Macro). Для этого:

    1) Перейдите в Плагины (Plugins) > NppExec > Advanced options…

    2) Поставьте галочку напротив Place to the Macros submenu.

    3) Введите «Run» в поле » Item name: «, затем нажмите на всплывающий список » Associated script: » и выберите скрипт под названием Run.

    После этого нажмите на » Add/Modify «

    и в области Menu items * вы увидите, что появилась строка » Run :: Run «.

    Повторите эти действия с скриптами Compile и Compile and run, чтобы они тоже появились в окне Menu items *.

    Нажмите Ок и перезапустите Notepad++.

    7. Протестируйте созданные вами скрипты, открыв или создав java-файл. К примеру, создадим файл Test.java (при сохранении обязательно укажите тип — Java source file (*.java) ) и запишем в него:

    public static void main(String[] args) <

    8. Перейдите в меню Макросы (Macro) и нажмите Compile. Затем снова зайдите в меню и нажмите Run. Или вы можете выполнить два действия сразу, по кнопке Compile and run.

    9. Если вы получили ошибку при компиляции или выполнении программы, измените команду Compile или Run (в зависимости от того где у вас ошибка).

    Если ошибка при компиляции измените команду Compile:

    C:\Program Files\Java\jdk-9\bin\javac $(FILE_NAME)

    Если ошибка при выполнении, измените команду Run:

    C:\Program Files\Java\jdk-9\bin\java $(NAME_PART)

    Во второй строке укажите путь к файлу javac или java на вашем компьютере. Путь к этим файлам может отличаться, будьте внимательны! Ошибка может быть вызвана тем, что Windows не знает путь к программе javac, или тем, что вы используете Notepad++ Portable Version. После изменений в Compile и Run не забудьте также про Compile and run.

    Вот пример ошибки:

    По сообщениям из консоли видно, что компиляция прошла успешно, но при выполнении программы произошла ошибка версий, а также не были найдены java.dll и SE Runtime Environment. Поэтому мы указываем путь для выполнения программы в скриптах Run и Compile and run.

    После этого программа выполняется успешно:

    10. Чтобы было удобнее компилировать и выполнять программы, настроим горячие клавиши. Перейдите в меню Запуск (Run) и выберите «Изменить горячую клавишу/удалить команду (Modify Shortcut/Delete Command…)».

    Во вкладке Plugin Commands настройте горячие клавиши для быстрого запуска скриптов. Найдите имена Compile, Run, Compile and Run, по двойному клику задайте удобное вам сочетание клавиш для быстрого запуска. У меня, например, Compile and Run настроен на клавиши Ctrl + Shift + C.

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

    11. Если вы хотите изменить цветовую схему (тему) или выбрать другой шрифт, настроить его размер и начертание, выберите меню Опции (Settings) > Определение стилей (Style configurator…) :

    12. При написании кода мне очень помогает автовставка символов. Например, когда я ставлю круглую скобку “(“, закрывающая скобка “)“ ставится автоматически. Кто работал в средах разработки (наподобие IDEA IntelliJ), знаком с этим и согласится, что это, вроде бы, мелочь, а удобно. По умолчанию автовставка выключена. Для её включения зайдите в Опции (Settings) > Настройки (Preferences) > Автозавершение (Auto-Completion) и выберите, что вам нужно. Если нужных символов нет, можете сами создать “Заказную пару”(Matched pair).

    13. При выполнении программ вы можете столкнуться с проблемой кодировки в консоли. Пример:

    Решается это очень просто. Переходим в меню Плагины (Plugins) > NppExec > Console Output

    И здесь вы должны выставить в значении Console Output is — «UTF-8».

    Если вдруг это не помогло, попробуйте поставить галочку — Console Input is the same as Output.

    14. Если вы хотите отключить проверку орфографии, нажмите кнопку «Spell Check Document Automatically (ABC)» на панели задач. После этого

    кириллица не будет подчеркиваться красным. Вы также можете включить/выключить консоль по кнопке, которая расположена справа — «Show Console Dialog».

    В разделе Плагины (Plugins) > NppExec > Advanced Options вы можете настроить отображение консоли при запуске программы. Выберите нужный вариант (Yes/No/Auto) в разделе Console — опция Visible (on start).

    А ещё вы можете выбрать язык по умолчанию, для этого перейдите в Опции (Settings) > Настройки (Preferences) > Новый документ (New Document) и выберите Синтаксис по умолчанию (Default language). В нашем случае — Java. Теперь, когда вы открываете новую вкладку или запускаете Notepad++ с пустым файлом, типом по умолчанию будет Java и вам не придется выбирать его каждый раз.

    15. Для удобства восприятия вы можете отключить системные (внутренние) сообщения консоли. Перейдите по вкладке Плагины (Plugins) > NppExec > No internal messages. После того, как вы поставите галочку, выполнение программы в консоли будет выглядеть так :

    При этом ошибки будут всё равно выводиться в консоль, что весьма удобно:

    Надеюсь, эта инструкция была вам полезна. Если заметите ошибки или неточности, напишите, и я исправлю! Всем удачи!

    Как компилировать и запускать программу Java с помощью командной строки

    Хотя многие среды программирования позволяют компилировать и запускать программы, их также можно скомпилировать и запустить с помощью командной строки. У Windows и Mac есть свои собственные версии командной строки, на Mac OS она называется Терминал. Процесс компиляции и запуска для Windows и для Mac практически идентичен.

    /.bash_profile и нажмите ↵ Enter . После этого перезапустите терминал.

    Записки программера

    Страницы

    четверг, 20 июля 2020 г.

    Руководство по Java 9: компиляция и запуск проекта

    Команды java и javac редко используются Java-программистами. Такие инструменты, как Maven и Gradle делают их почти не нужными. Однако Maven и Gradle до сих пор не предоставляют полную поддержку для Java 9, поэтому, если вы хотите начать использовать её уже сейчас или просто хотите узнать некоторые полезные тонкости до официального релиза, стоит научиться вызывать java , javac и jar для управления своим кодом.

    Статья призвана показать примеры использования этих команд, а также то, как эти команды изменились по сравнению с прошлыми версиями Java. Дополнительно будут рассмотрены новые инструменты: jdeps и jlink . Предполагается, что вы хоть немного знакомы с предыдущими версиями команд java / javac / jar и с модульной системой Java 9.

    Установка Java 9

    Сперва необходимо установить Java 9. Вы можете скачать её с сайта Oracle, но рекомендуется использовать SdkMAN!, так как в будущем он позволит вам с легкостью переключаться между разными версиями Java.

    Можно установить SdkMAN! с помощью этой команды:

    Посмотрите, какая сборка является последней:

    Затем установите Java 9:

    Теперь, если у вас установлены другие версии Java, вы можете переключаться между ними с помощью команды:

    Компиляция и запуск «по-старому»

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

    Возьмем этот простой Java-класс:

    Теперь, так как мы не использовали никаких особенностей Java 9, мы можем скомпилировать всё как обычно:

    Команда создаст файл класса out/app/Main.class . Запустить его можно так же, как и в прошлых версиях:

    Программа выведет Hello Java 9 .

    Теперь создадим библиотеку Greeting также без особенностей Java 9, чтобы посмотреть, как это работает.

    Создадим файл greeting/ser/lib/Greeting.java со следующим кодом:

    Изменим класс Main для использования нашей библиотеки:

    Скомпилируем эту библиотеку:

    Чтобы показать, как работают оригинальные Java-библиотеки, мы превратим эту библиотеку в jar-файл без дескрипторов модулей Java 9:

    Команда создаст файл libs/lib.jar , содержащий класс lib.Greeting .

    Просмотреть информацию о jar-файле можно с помощью опции tf :

    Команда должна вывести:

    Теперь для компиляция app.Main нам необходимо указать компилятору, где найти класс lib.Greeting .

    Используем для этого cp (classpath):

    И то же самое для запуска программы:

    Мы можем упаковать приложение в jar-файл:

    И затем запустить его:

    Вот так выглядит структура нашего проекта на данный момент:

    Модуляризация проекта

    Пока что ничего нового, но давайте начнем модуляризацию нашего проекта. Для этого создадим модульный дескриптор (всегда называется module-info.java и размещается в корневой директории src/ ):

    Команда для компиляции модуля в Java 9 отличается от того, что мы видели раньше. Использование старой команды с добавлением модуля к списку файлов приводит к ошибке:

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

    Любой класс, который загружается не из именованного модуля, автоматически выполняет часть безымянного модуля. В примере выше перед созданием модульного дескриптора наш код не был частью какого-либо модуля, следовательно, он был ассоциирован с безымянным модулем. Безымянный модуль — это механизм совместимости. Проще говоря, это позволяет разработчику использовать в приложениях Java 9 код, который не был модуляризирован. По этой причине код, относящийся к безымянному модулю, имеет правила сродни Java 8 и ранее: он может видеть все пакеты, экспортируемые из других модулей, и все пакеты безымянного модуля.

    Когда модульный дескриптор добавляется к модулю, его код больше не является частью безымянного модуля и не может видеть код других модулей, пока не импортирует их. В случае выше модуль com.app не требует никаких модулей, поэтому модуль библиотеки Greeting для него не виден. Он может видеть только пакеты модуля java.base .

    Модули в Java 9, за исключением неуловимого безымянного модуля описанного выше, должны объявлять, какие другие модули им необходимы. В случае с модулем com.app единственным требованием является библиотека Greeting. Но, как вы могли догадаться, эта библиотека (как и другие библиотеки, не поддерживающие Java 9) не является модулем Java 9. Как же нам включить её в проект?

    В таком случае вам нужно знать имя jar-файла. Если у вас есть зависимость от библиотеки, которая не была конвертирована в модуль Java 9, вам надо знать, какой jar-файл вызывается для этой библиотеки, потому что Java 9 переведёт имя файла в валидный модуль.

    Это называется автоматический модуль.

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

    Как именно происходит компиляция java?

    Смущает процесс компиляции java

    OK Я знаю это: мы пишем исходный код java, компилятор, который независим от платформы, переводит его в байт-код, тогда jvm, который зависит от платформы, переводит его в машинный код.

    Итак, с начала, мы пишем исходный код java. Компилятор javac.exe — это .exe файл. Что это за файл .exe? Является ли java-компилятор написанным в java, тогда как это происходит .exe файл, который его выполняет? Если код компилятора написан java, тогда как код компилятора выполняется на этапе компиляции, так как его работа jvm для выполнения Java-кода. Как сам язык может составить собственный код языка? Все это похоже на проблему с курицей и яйцом.

    Теперь, что именно содержит файл .class? Это абстрактное синтаксическое дерево в текстовой форме, это табличная информация, что это такое?

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

    Хорошо, я знаю это: мы пишем исходный код Java, компилятор, который не зависит от платформы, переводит его в байт-код,

    На самом деле сам компилятор работает как собственный исполняемый файл (отсюда и javac.exe). И правда, он преобразует исходный файл в байт-код. Байт-код не зависит от платформы, поскольку он нацелен на виртуальную машину Java.

    затем jvm, который зависит от платформы, переводит его в машинный код.

    Не всегда. Что касается Sun JVM, то здесь есть два jvms: клиент и сервер. Они оба могут, но не обязательно должны компилироваться в нативный код.

    Итак, с самого начала мы пишем исходный код Java. Компилятор javac.exe представляет собой файл .exe. Что именно это файл .exe? Разве Java-компилятор не написан на Java, тогда как получается .exe файл, который его выполняет?

    Этот файл exe представляет собой упакованный байт-код Java. Это для удобства — избегать сложных пакетных скриптов. Он запускает JVM и запускает компилятор.

    Если код компилятора написан на java, то почему код компилятора выполняется на этапе компиляции, так как это задача jvm — выполнять код java.

    Это именно то, что делает упаковочный код.

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

    Правда, сбивает с толку на первый взгляд. Хотя, это не только Java-идиома. Компилятор Ады также написан на самой Аде. Это может выглядеть как «проблема с курицей и яйцом», но на самом деле это всего лишь проблема с самозагрузкой.

    Теперь, что именно содержит файл .class? Это абстрактное синтаксическое дерево в текстовом виде, табличная информация, что это?

    Это не Абстрактное Синтаксическое Дерево. AST используется токенизатором и компилятором только во время компиляции для представления кода в памяти. Файл .class похож на сборку, но для JVM. JVM, в свою очередь, является абстрактной машиной, которая может работать на специализированном машинном языке, ориентированном только на виртуальную машину. В простейшем случае файл .class имеет структуру, очень похожую на обычную сборку. Сначала объявляются все статические переменные, затем идут несколько таблиц сигнатур внешних функций и, наконец, машинный код.

    Если Вам действительно любопытно, Вы можете покопаться в файле классов, используя утилиту «javap». Вот пример (запутанный) результат вызова javap -c Main :

    Итак, вы должны уже иметь представление о том, что это такое на самом деле.

    Кто-нибудь может мне рассказать, как мой исходный код Java преобразуется в машинный код?

    Я думаю, что это должно быть более понятно прямо сейчас, но здесь краткое резюме:

    Вы вызываете javac , указывая на ваш файл исходного кода. Внутренний считыватель (или токенизатор) javac читает ваш файл и создает из него реальный AST. Все синтаксические ошибки происходят с этой стадии.

    javac еще не закончил свою работу. Когда у него есть AST, может начаться истинная компиляция. Он использует шаблон посетителя для обхода AST и разрешает внешние зависимости, чтобы добавить смысл (семантику) в код. Готовый продукт сохраняется в виде файла .class , содержащего байт-код.

    Теперь пришло время запустить вещь. Вы вызываете java с именем файла .class. Теперь JVM запускается снова, но для интерпретации Вашего кода. JVM может или не может компилировать Ваш абстрактный байт-код в собственную сборку. Компилятор Sun HotSpot в сочетании с компиляцией Just In Time может сделать это при необходимости. Выполняемый код постоянно профилируется JVM и перекомпилируется в собственный код, если соблюдаются определенные правила. Чаще всего горячий код компилируется первым.

    Изменение: Без javac нужно было бы вызвать компилятор, используя что-то похожее на это:

    Как вы можете видеть, он вызывает частный API Sun, поэтому он связан с реализацией JDK Sun Это сделало бы системы сборки зависимыми от него. Если кто-то переключился на любой другой JDK (вики-списки 5, кроме Sun), приведенный выше код следует обновить, чтобы отразить это изменение (поскольку маловероятно, что компилятор будет находиться в пакете com.sun.tools.javac). Другие компиляторы могут быть написаны на нативном коде.

    Таким образом, стандартным способом является поставка оболочки javac с JDK.

    Является ли java-компилятор написанным в java, тогда как это происходит .exe файл, который его выполняет?

    Откуда вы получаете эту информацию? Исполняемый файл javac может быть написан на любом языке программирования, он не имеет значения, все, что важно, — это исполняемый файл, который превращает файлы .java в файлы .class .

    Подробнее о двоичной спецификации файла .class вы можете найти в этих главах в Java Language Specification (хотя, возможно, немного техническую ):

    Вы также можете посмотреть Спецификация виртуальной машины, которая охватывает:

    Компилятор javac.exe — это .exe файл. Что это за файл .exe? не java-компилятор, написанный в java, то как получится .exe файл, который выполняет его?

    Компилятор Java (по крайней мере тот, который поставляется с Sun/Oracle JDK) действительно написан на Java. javac.exe — это просто средство запуска, которое обрабатывает аргументы командной строки, некоторые из которых передаются JVM, который запускает компилятор, а другие — самому компилятору.

    Если код компилятора написан java, тогда как код компилятора выполненный на стадии компиляции, поскольку его работа jvm выполнить java-код. Как может язык сам компилирует свой собственный код языка? Все это похоже на курицу и яйцо проблема для меня.

    Многие (если не большинство) компиляторы написаны на языке, который они компилируют. Очевидно, что на ранней стадии компилятор должен был скомпилироваться кем-то другим, но после этого «начальной загрузки» любая новая версия компилятора может быть скомпилирована более старой версией.

    Теперь, что именно делает .class файл содержат? Это абстрактное синтаксическое дерево в текстовой форме, это табличное информация, что это такое?

    Подробности формата файла класса описаны в спецификации виртуальной машины Java.

    Ну, javac и jvm обычно являются родными двоичными файлами. Они написаны на С или как угодно. Конечно, можно написать их на Java, просто сначала вам нужна нативная версия. Это называется «обвязка багажника».

    Интересный факт: большинство компиляторов, которые компилируются в нативный код, написаны на своем родном языке. Однако им всем сначала нужно было написать нативную версию на другом языке (обычно C). Первый компилятор C, для сравнения, был написан на Ассемблере. Я предполагаю, что первый ассемблер был написан в машинном коде. (Или используя бабочек ;)

    Файлы .class — это байт-код, сгенерированный javac. Они не текстовые, они представляют собой двоичный код, похожий на машинный код (но с другим набором команд и архитектурой).

    Во время выполнения jvm имеет две опции: он может интерпретировать байтовый код (притворяясь самим процессором) или JIT (точно в срок) компилировать его в машинный код. Последнее, конечно, быстрее, но сложнее.

    Файл .class содержит байт-код, который очень похож на сборку высокого уровня. Компилятор может быть хорошо написан на Java, но JVM нужно будет скомпилировать в собственный код, чтобы избежать проблемы с курицей/яйцом. Я считаю, что он написан на языке C, как и на более низких уровнях стандартных библиотек. Когда JVM работает, он выполняет компиляцию «точно в момент времени», чтобы превратить этот байт-код в собственные инструкции.

    Краткое пояснение

    Запишите код в текстовом редакторе, сохраните его в формате, который понимает компилятор — «. Расширение файла java» , javac (java-компилятор) преобразует это значение в «. class» (файл байта — файл класса). JVM выполняет файл .class в операционной системе, в которой он находится.

    Длинное пояснение

    Всегда помните, что java не является базовым языком, который распознает операционная система. Исходный код Java интерпретируется операционной системой транслятором, называемым Java Virtual Machine (JVM). JVM не может понять код, который вы пишете в редакторе, ему нужен скомпилированный код. Здесь компилятор входит в картину.

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

    Как javac (java-компилятор) распознает сохраненный текст как скомпилированный?. У нас есть отдельный текстовый формат, который распознает компилятор, т.е. .java. Сохраните файл в расширении .java, и компилятор узнает его и скомпилирует его, когда его спросят.

    Что происходит при компиляции? — Компилятор — это второй переводчик (не технический термин), вовлеченный в процесс, он переводит понятный пользователю язык (java) в понятный язык JVM (байт-код — формат .class).

    Что происходит после компиляции? — Компилятор создает файл .class, который JVM понимает. Затем выполняется программа, то есть файл .class выполняется JVM в операционной системе.

    Факты, которые вы должны знать

    1) Java не является многоплатформенной независимой от платформы.

    2) JVM разработан с использованием C/С++. Одна из причин, почему люди называют Java более медленным языком, чем C/С++

    3) Байт-код Java (.class) находится в «Язык сборки» , единственный язык, понятный JVM. Любой код, который создает файл .class при компиляции или сгенерированный байт-код, может быть запущен на JVM.

    Цукерберг рекомендует:  Создание системы предметов на Unity
    Понравилась статья? Поделиться с друзьями:
    Все языки программирования для начинающих
    Рубрика: Программирование / Веб-программирование