Содержание

Java и C++: тест на быстродействие | Java World

В те времена, когда все виртуальные машины Java представляли собой интерпретаторы байт-кода, эта технология вызывала справедливые нарекания за свою низкую производительность. Но по прошествии некоторого времени на рынке появились хорошие компиляторы just-in-time (JIT), и сегодня тесты показывают, что по быстродействию Java во многих областях практически сравнялась с C++. Наметившиеся тенденции позволяют надеяться на то, что уже в самое ближайшее время Java не уступит в скорости C++.

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

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

Мы решили провести самостоятельное исследование и выяснить, в чем Java уступает C++ (специалисты любят противопоставлять именно эти два языка). Были изучены компоненты архитектуры Java и определены сравнительные характеристики быстродействия идентичных программ, написанных на Java и на C++.

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

Любое повышение производительности какого-либо языка следует рассматривать в строго определенном контексте. С момента появления Java прошло уже немало времени, и это время не было потрачено впустую. Неслучайно сегодня по уровню быстродействия этогот язык в большинстве случаев сравним с C++ (а C++ славится своей высокой скоростью).

Программное обеспечение: Visual C++ 5.0 и Sun JDK 1.1.5.

В данной статье под платформой понимается комбинация процессора и ОС. Например, платформой можно считать комбинацию ОС Windows NT и процессора Intel, ОС Linux и процессора Intel или же ОС Linux и процессора Digital Alpha.

ЗАГРУЗКА ИСПОЛНЯЕМОГО КОДА

При создании нового приложения разработчики сначала записывают программный код в один или несколько исходных файлов. Компилятор/компоновщик транслирует исходный код в исполняемый. В готовом приложении работает именно исполняемый код. На первом этапе необходимо загрузить исполняемый код в память компьютера.

Загрузка исполняемого кода: Java против C++

Если программа находится на локальном диске, загрузка даже сложного приложения не займет много времени. Если же программа хранится на Web-узле в Internet или в корпоративной сети intranet, размер исполняемого кода может существенно повлиять на производительность.

Программы и ресурсы Java занимают значительно меньше места на диске по сравнению с программами C++ и загружаются из Internet и intranet гораздо быстрее. При определении скорости загрузки необходимо учитывать размер исполняемого кода и возможность избирательной загрузки.

Размер исполняемого кода

Исполняемые модули Windows NT, написанные на C++, занимают на диске значительно больше места, чем исполняемые модули Java. На размеры исполняемого кода оказывают влияние три фактора:

Во-первых, двоичный формат исполняемого кода C++ увеличивает его размеры по сравнению с кодом Java почти в два раза.

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

К сожалению, если программист захочет использовать какие-либо специальные функции, не имея доступа к базовому API C++, ему придется самому реализовывать поддержку библиотек для своей программы. Включение в программу библиотечных функций может удвоить или даже утроить размеры конечного кода.

В третьих, в комплект Java входят специальные библиотеки, позволяющие работать с графическими и звуковыми файлами, записанными в сжатом формате. Поддерживаются, в частности, графические форматы Joint Photographic Expert Group (JPEG) и Graphics Interchange Format (GIF), а также аудиоформат AU. Средства разработки программ на C++ для Windows NT поддерживают только несжатые форматы: bitmap (BMP) для изображений и wave (WAV) для аудиофайлов. Сжатие дает возможность на порядок уменьшить размеры графических файлов, и приблизительно в три раза — размеры аудиофайлов. Если вы хотите работать с другими форматами графических и звуковых файлов, можно воспользоваться дополнительными библиотеками классов.

Избирательная загрузка

Загрузчики программ, написанных на C++, перед началом выполнения должны полностью загрузить исполняемый файл. В среде Win32 имеется два способа связи с библиотеками DLL: статический и динамический. Статически связанные DLL (используемые в большинстве случаев) загружаются до начала выполнения программы. В случае динамического связывания библиотеки DLL загружаются по запросу. Однако такой вариант гораздо менее популярен, поскольку написание кода динамической загрузки и обращения к библиотечным функциям требует от разработчика значительных усилий.

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

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

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

Загрузка исполняемого кода реальных программ

В таблице 1 представлены размеры протестированных программ и их ресурсов. Огромная разница между размерами кода C++ и Java объясняется наличием библиотек, необходимых для выполнения программы C++. Объемы ресурсов отличаются вследствие того, что приложения Java работают со сжатыми файлами (в формате GIF), а программы C++ — с обычными файлами bitmap.

Таблица 1

Имя программыРазмеры программы, Кбайт (C++/Java)Ресурсы, Кбайт (C++/Java)
Simple Loop46/3,9-/-
Memory Allocation34/1,4-/-
Bouncing Globes103/21485/13

ВЫПОЛНЕНИЕ ПРОГРАММНЫХ ИНТСТРУКЦИЙ

После загрузки исполняемого кода процессор начинает выполнять программные инструкции. При традиционном программировании на C++ исполняемый файл содержит двоичные инструкции процессора выбранной платформы. Для переноса приложения на другую платформу разработчику потребуется создать новый исполняемый файл путем перекомпиляции исходного кода. Кроме того, особенности конкретной платформы каждый раз заставляют вносить определенные изменения в исходный код. Исполняемые же файлы, получаемые на выходе компилятора Java, представляют собой совокупность байт-кодов, которые не могут выполняться процессором без дополнительного преобразования в двоичный код. Это преобразование возлагается на JVM. Виртуальная машина Java может осуществлять преобразование двумя способами: с помощью интерпретатора байт-кода или посредством компилятора just-in-time (JIT).

Выполнение программных инструкций: Java против C++

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

Интерпретаторы байт-кода работали в несколько раз медленнее исполняемого кода программ C++, поскольку каждую инструкцию в ходе выполнения необходимо было преобразовать в двоичный код. Это, в свою очередь, приводило к неоправданным расходам. Приведем простейший пример. Каждый цикл состоит из набора многократно повторяющихся инструкций. При выполнении очередной итерации JVM снова и снова интерпретирует один и тот же байт-код, при этом на процедуру интерпретации уходит довольно много процессорного времени.

Но методы, на которых базировалась работа старых JVM, уже уходят в прошлое. Большинство современных виртуальных машин Java оснащается JIT-компиляторами. Эти компиляторы транслируют и преобразуют в двоичный код сразу весь исходный файл. Таким образом, отпадает необходимость повторной трансляции каждой инструкции байт-кода.

Производительность кода различных компиляторов

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

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

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

Таким образом, единственная причина, по которой обрабатываемая JIT-компилятором программа Java и приложение C++ работают с разной скоростью, — это необходимость первоначальной трансляции файла классов; она же, в свою очередь, зависит от типа проводимой оптимизации.

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

На начальном этапе большинство компаний, разрабатывавших компиляторы JIT, пытались определять, какие классы следует компилировать, а какие — нет. Это зависело от того, как часто появляются классы в программе. Впоследствии многие производители отказались от такого решения, и компиляторы стали транслировать весь исходный код, поскольку общее время выполнения при этом практически не изменялось.

Теория и практика

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

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

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

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

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

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

Первый и второй уровень оптимизации как правило повышают быстродействие на 10-15 % при минимальных затратах.

Третий уровень оптимизации позволяет увеличить производительность еще на 5 %, однако это обойдется значительно дороже.

Примеры реальных программ

В таблице 2 сравниваются результаты выполнения нескольких процедур Java и аналогичных программ, написанных на C++. Нетрудно заметить, что при отсутствии JIT-компилятора производительность Java в три-четыре раза уступает этому показателю для C++.

Таблица 2

ТестОписаниеВремя выполнения программы C++, сек.Время выполнения программы Java (JIT-
компилятор), сек.
Время выполнения программы Java (интер-
претатор байт-кода), сек.
Целочисленное делениеВ цикле 10 тыс. раз выполнялась операция целочисленного деления.1,81,84,8
Неиспользуемый кодВ цикле 10 млн. раз встречалось неиспользуемое выражение3,73,79,5
Комбинация неиспользуемого кода и целочисленного деленияВ цикл была встроена одна исполняемая команда и 10 млн. раз неиспользуемых выражений5,45,720
Деление с плавающей точкойВ цикле 10 млн. раз выполнялось деление с плавающей точкой1,61,68,7
Статический методВ цикле 10 млн. раз вызывался статический метод, реализующий целочисленное деление1,81,86,0
Компонентный методВ цикле 10 млн. раз вызывался компонентный метод, реализующий целочисленное деление1,81,810
Виртуальный компонентный метод*В данном тесте 10 млн. раз вызывался виртуальный компонентный метод, реализующий целочисленное деление1,81,810
Виртуальный компонентный метод с приведением типов и идентификацией типа в момент выполнения (RTTI)В данном тесте 10 млн. раз вызывался виртуальный метод класса, в котором выполнялась операция приведения типов114,312
Виртуальный компонентный метод с неправильным приведением типа и идентификацией типа в момент выполнения (RTTI)В данном тесте 10 млн. раз вызывался виртуальный метод класса, в котором выполнялась операция приведения типов-**-**-**
Примечания:
* Тест компонентного метода не вполне корректен, поскольку в Java все компоненты являются виртуальными.
** Аварийное завершение

В полном соответствии с теоретическими исследованиями быстродействие программ Java, обрабатываемых JIT-компилятором, практически не отличается от этого показателя для процедур C++. Единственное исключение — выполнение операции приведения типов, где скорость C++ значительно превосходит скорость Java.

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

В момент выполнения программы проверяется правильность преобразования родительского класса в дочерний (идентификация типа в момент выполнения — run-time type identification, RTTI). При эксплуатации сложных систем, в которых не реализованы методы RTTI, часто возникают ошибки. Это значительно снижает эффективность всей системы и подрывает доверие пользователей.

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

РАСПРЕДЕЛЕНИЕ ПАМЯТИ

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

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

Распределение памяти: Java против C++

Поскольку «сборщик мусора» Java должен автоматически выявить незадействованные программой области памяти, общие расходы Java на распределение памяти значительно превышают соответствующие расходы C++. Но при этом механизм освобождения памяти Java обеспечивает два очень важных преимущества.

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

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

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

Сложность реализации стратегии «сборки мусора» заключается в том, что механизм освобождения памяти должен самостоятельно выявить неиспользуемые объекты. Если программист твердо придерживается принципов объектно-ориентированной философии, процесс «сборки мусора» обойдется сравнительно недорого. В случае же использования процедурного подхода, сложность взаимосвязей значительно затруднит поиск неиспользуемых объектов.

Примеры программ

На выделение и освобождение памяти для 10 млн. 32-разрядных целых чисел программе на C++ потребовалось 0,812 сек., а программе на Java — 1,592 сек. На основании данного примера хорошо прослеживается снижение быстродействия Java. И все же несмотря на огромную дополнительную работу, выполненную «сборщиком мусора», производительность Java вполне соизмерима с производительностью C++.

ДОСТУП К СИСТЕМНЫМ РЕСУРСАМ

Помимо распределения памяти программе необходим доступ к другим системным службам, таким как вывод графических примитивов, обработка звука и управление окнами. Традиционные программы на C++ обращаются к системным функциям при помощи интерфейсов прикладных программ (API). Каждой платформе соответствует свой API, поэтому при переносе программы с одной платформы на другую разработчику приходится порой проводить существенные модификации.

Программы Java обращаются к системным службам любой платформы при помощи единственного API. Каждая платформа имеет сходные интерфейсы, которые в ответ на обращения Java выделяют программе необходимые ресурсы.

Доступ к системным ресурсам: Java против C++

В общем случае преимущества, которые дает однотипное обращение к системным службам, оправдывают стоимость разработки специальных интерфейсов Java. Программы Java, вызывающие системные функции, работают так же быстро, как программы C++.

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

Примеры программ

Мы протестировали программу, разработанную известным специалистом в области анимации и мультимедиа Джеем Бартотом. Быстродействие реализующего ее алгоритм апплета Java ничем не отличалось от этого показателя программы, написанной при помощи средств Win32 SDK.

Данный пример поможет служить подтверждением двух важнейших постулатов.

Во-первых, производительность Java зависит от производительности JVM. Тестовая программа, работавшая под управлением Internet Explorer 4.0 и Netscape Communicator в среде Windows NT, не отличалась высокой скоростью выполнения. Объекты перемещались на экране неестественно, с высокой дискретностью. Однако стоило запустить браузеры в среде Windows 95, как скорость анимации стала вполне приемлемой.

Разница в производительности объясняется огромной работой, проделанной с тем, чтобы оптимизировать быстродействие браузеров в среде Windows 95. Принципиальные архитектурные различия не позволяют обеспечить одинаковую производительность браузера в обеих операционных системах.

Во-вторых, пакет JDK for Win32, разработанный корпорацией Sun, является общепринятым стандартом, который обязаны поддерживать все браузеры и JVM.

КОМПЕТЕНТНОСТЬ РАЗРАБОТЧИКА — ГАРАНТИЯ ВЫСОКОЙ ПРОИЗВОДИТЕЛЬНОСТИ

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

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

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

Остается лишь перечислить эти преимущества:

Пакетирование и распространение настольных приложений Java

This tutorial needs a review. You can edit it in GitHub following these contribution guidelines.
  • Создание исполняемого файла JAR
    • Создание проекта с помощью существующих файлов исходного кода
    • Настройка проекта
    • Построение проекта и создание файла JAR
  • Запуск и распространение файла JAR
    • Запуск приложения в среде IDE
    • Запуск приложения вне среды IDE
    • Распространение приложения другим пользователям
  • Запуск приложения Java
    • Запуск приложений из командной строки
    • Запуск приложений из сценария
  • Пакетирование приложения для Java Web Start
  • Советы по устранению проблем
    • Указание сопоставлений файлов JAR
    • Установка переменной среды PATH
  • Что дальше?

Многие начинающие программисты сталкиваются с таким вопросом: «Я создал свое приложение в среде IDE, как теперь заставить его работать из командной строки вне среды IDE?». Может возникнуть и такой вопрос: «Как мне распространить это приложение среди других пользователей, не передавая им заодно всю среду IDE?».

Ответы на эти вопросы относительно просты, но не всегда очевидны. В данном документе даны ответы на них путем демонстрации основ использования среды IDE для подготовки приложений к распространению и развертыванию. Кроме того, в данном документе предоставлены сведения, которые могут быть необходимы для настройки системы программиста (или которые нужно будет передать пользователям приложения). Мы покажем несколько различных подходов к развертыванию приложения, позволяющих пользователям получать доступ к нему посредством:

  • Двойного щелчка файла архива Java (JAR) приложения.

  • Вызова приложения из командной строки.

  • Вызова приложения из файла сценария.

  • Использования Java Web Start.

Figure 1. Содержимое этой страницы применимо к IDE NetBeans 8.0

 

Для работы с этим учебным курсом требуются программное обеспечение и материалы, перечисленные в таблице ниже.

Программное обеспечение или материалТребуемая версия

IDE NetBeans

версия 8.0

Комплект для разработчика на языке Java (JDK)

версия 7 или 8

Файлы исходного кода для руководства по развертыванию

 

Создание исполняемого файла JAR

В данной части учебного курса показано, как можно создать предназначенное для распространения приложение в среде IDE и затем запустить это приложение вне среды IDE. Мы пакетируем это приложение в исполняемый файл JAR.

Файл JAR – это файл архива, в который могут быть вложены файлы и папки. Файлы JAR подобны файлам ZIP, но файлы JAR могут иметь дополнительные атрибуты, полезные при распространении приложений Java. В число этих атрибутов входят цифровая подпись файлов JAR, дополнительное сжатие, совместимость с различными платформами и т. д.

В этом упражнении мы создаем проект среды IDE и затем помещаем в этот проект два заранее написанных файла исходного кода Java. Затем мы скомпилируем классы и создадим исполняемый файл JAR. После этого мы посмотрим, как запускать файлы JAR вне среды IDE.

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

Создание проекта с помощью существующих файлов исходного кода

  1. Загрузите файл DeploymentTutorial.zip и извлеките его содержимое. Данный архив ZIP содержит файлы исходного кода для приложения, а также несколько других файлов, которые будут использованы в этом руководстве.

    1. В IDE NetBeans выберите ‘Файл’ > ‘Создать проект’.

    2. На странице Choose Category («Выбор категории») выберите Java Project With Existing Sources («Проект Java с существующим исходным кодом») в категории Java и нажмите кнопку Next («Далее»).

    3. На странице Name and Location («Имя и местоположение») мастера введите AnotherGrep в поле имени проекта и укажите местоположение проекта. Нажмите кнопку «Далее».

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

  1. На странице мастера Existing Sources («Существующие исходные файлы») укажите файлы исходного кода, которые будут включены в проект. Нажмите кнопку Add Folder («Добавить папку») справа от поля Source Package Folders («Папки пакетов исходного кода»). Перейдите к папке DeploymentTutorial , которую только что разархивировали, разверните папку, выберите папку src и нажмите кнопку Open («Открыть»). Папка src будет добавлена к полю Source Package Folders («Папки пакетов исходного кода»).

  2. Нажмите кнопку «Завершить».

Примечание. Если, например, необходимо исключить определенные исходные файлы из операции импорта в проект, щелкните ‘Далее’, чтобы открыть последнее окно ‘Включает & Исключает’. В этом случае необходимо использовать все исходные файлы в папке src поэтому нажимается кнопка ‘Готово’, чтобы завершить работу в мастере создания проектов.

Проект откроется в среде IDE и отобразится в окне «Проекты». Изучить содержание проекта можно, развернув его узел пакетов исходного кода, в котором можно будет увидеть классы Grep и xGrep . Grep.java – файл консольной версии приложения. xGrep.java – файл версии приложения с графическим интерфейсом, использующей методы, определенные в Grep.java .

Настройка проекта

Для настройки проекта необходимо выполнить несколько действий:

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

  • Назначьте главный класс проекта. Это позволит гарантировать, что файл JAR, создаваемый при построении проекта, будет исполняемым.

Проверка платформы Java

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

  1. Правой кнопкой мыши щелкните узел проекта и выберите элемент «Свойства».

  2. На вкладке «Библиотеки» в качестве платформы Java должна быть указана версия JDK 1.7 (или JDK 1.8).

  3. На вкладке Sources («Источники») выберите JDK 7 (или JDK 8) как формат двоичных файлов/файлов исходного кода.

  4. Нажмите кнопку «OК», чтобы закрыть окно Project («Свойства»).

Назначение главного класса

Чтобы пользователь мог без труда запустить создаваемый файл JAR (дважды щелкнув его или введя java -jar AnotherGrep. jar в командной строке), необходимо указать главный класс внутри файла манифеста manifest в архиве JAR. (Файл манифеста является стандартной частью архива JAR, содержащей информацию о файле JAR, которая будет полезна для средства запуска java при запуске приложения.) Главный класс служит точкой входа, из которой средство запуска java запускает приложение.

При сборке проекта среда IDE создает файл JAR и включает в него манифест. При назначении главного класса проекта мы убеждаемся, что главный класс указан в манифесте.

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

  1. Правой кнопкой мыши щелкните узел проекта и выберите элемент «Свойства».

  2. Выберите категорию Выполнение и введите anothergrep.xGrep в поле ‘Основной класс’.

  3. Нажмите кнопку «ОК» для закрытия диалогового окна «Свойства проекта».

При сборке проекта ниже в этом руководстве будет создан манифест, включающий в себя следующую запись:

Main-Class: anothergrep. xGrep

Построение проекта и создание файла JAR

Теперь, когда файлы исходного кода готовы и параметры проекта настроены, пора выполнить сборку проекта.

Сборка проекта

При сборке проекта происходит следующее.

  • К папке проекта (далее именуемой «папка PROJECT_HOME«) добавляются папки build и dist .

  • Все исходные файлы компилируются в файлы .class , которые помещаются в папку PROJECT_HOME/build .

  • В папке PROJECT_HOME/dist создается архив JAR, содержащий проект.

  • Если для проекта указаны какие-либо библиотеки (кроме JDK), в папке dist создается папка lib . Библиотеки копируются в папку dist/lib .

  • Файл манифеста manifest в архиве JAR обновляется за счет включения записей, указывающих главный класс и все библиотеки, которые находятся в пути классов проекта.

Примечание. Содержимое Manifest можно просмотреть в окне ‘Файлы’ IDE. После построения проекта переключитесь на окно Files («Файлы») и перейдите к dist/AnotherGrep.jar . Разверните узел файла JAR, разверните папку META-INF и дважды щелкните MANIFEST.MF , чтобы отобразить Manifest в редакторе исходного кода.

Main-Class: anothergrep.xGrep

(Дополнительные сведения о файлах манифеста приведены в этом разделе в учебном курсе по Java).

Запуск и распространение файла JAR

Запуск приложения в среде IDE

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

Для запуска проекта AnotherGrep в IDE, щелкните правой кнопкой мыши узел проекта в окне ‘Проекты’ и выберите ‘Выполнить’.

Должно открыться окно xGrep. Можно нажать кнопку Browse («Обзор») для выбора файла, в котором следует выполнить поиск текстового шаблона. В поле Search Pattern («Шаблон поиска») введите шаблон в виде текста или регулярного выражения, совпадения с которым следует искать, и нажмите кнопку Search («Поиск»). Все совпадения появятся в области Output («Выходные данные») окна xGrep.

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

Запуск приложения вне среды IDE

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

Запустить приложение вне среды IDE можно выполнив следующие действия:

Если окно xGrep открылось, приложение запущено успешно.

Если окно xGrep не открывается, то, скорее всего, отсутствует сопоставление между файлами JAR и средой выполнения Java. См. Устранение проблем с сопоставлениями файлов JAR ниже.

Распространение приложения другим пользователям

Теперь, когда подтверждено, что приложение работает вне среды IDE, можно перейти к его распространению.

  • Отправьте файл JAR приложения всем, кто будет использовать приложение. Пользователи приложения смогут запускать его, дважды щелкнув файл JAR. Если у них это не получится, покажите им информацию в разделе Устранение проблем с сопоставлениями файлов JAR, приведенную.

Примечание. Если работа приложения зависит от дополнительных библиотек, помимо включенных в JDK, необходимо включить их в распространение (не относится к этому примеру). Относительные пути к этим библиотекам добавляются в запись classpath файла манифеста JAR при разработке приложения в среде IDE. Если эти дополнительные библиотеки не будут найдены по указанному пути класса (т.е. относительному пути) при запуске, приложение не запустится. Создайте архив ZIP, содержащий файл JAR и библиотеку, после чего отправьте этот файл ZIP пользователям. Проинструктируйте пользователей распаковать этот архив ZIP так, чтобы файл JAR и файлы JAR библиотек были в одной папке. Запустите файл JAR приложения.

Запуск приложения Java

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

В данном упражнении показано, как можно запустить приложение Java следующими двумя способами:

Запуск приложений из командной строки

Приложение можно запустить из командной строки, используя команду java . Если нужно выполнить исполняемый файл JAR, используйте параметр -jar команды.

Например, для запуска приложения AnotherGrep надо выполнить следующие действия:

  1. Открыть окно терминала. На системах Microsoft Windows это делается путем нажатия кнопки «Пуск» и выбора «Выполнить…​», ввода cmd в поле «Открыть:» и нажатия кнопки «OК».

  2. Перейдите в каталог PROJECT_HOME/dist (используя команду cd ).

  3. Введите следующую строку для запуска главного класса приложения:

java -jar AnotherGrep. jar

Если эти инструкции выполнены, а приложение не запускается, вероятно, следует выполнить одно из следующих действий:

C:\Program Files\Java\jdk1.7.0_51\bin\java -jar AnotherGrep.jar

Запуск приложений из сценария

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

В первую очередь необходимо изменить главный класс в приложении на консольную версию класса и построить файл JAR заново:

  1. В окне ‘Проекты’ в IDE щелкните правой кнопкой мыши узел проекта ( AnotherGrep ) и выберите ‘Свойства’.

  2. Выберите узел Run («Запуск») и измените свойство Main Class («Главный класс») на anothergrep.Grepanothergrep.xGrep ). Нажмите кнопку «ОК», чтобы закрыть окно Project Properties («Свойства проекта»).

  3. Снова щелкните правой кнопкой мыши узел проекта и выберите Clean and Build Project («Очистка и сборка проекта»).

По завершении этих действий файл JAR будет собран заново, а атрибут Main-Class в манифесте файла JAR будет указывать на anothergrep.Grep .

Сценарий bash – для компьютеров под управлением UNIX и Linux

В папке, куда было извлечено содержимое файла DeploymentTutorial.zip, можно найти сценарий bash с именем grep.sh . Давайте посмотрим на него:

#!/bin/bash
                    java -jar dist/AnotherGrep.jar $@

Первая строка указывает, какой интерпретатор команд следует использовать для интерпретации этого сценария. Вторая исполняет файл JAR, созданный средой IDE в папке PROJECT_HOME/dist . $@ просто копирует все переданные сценарию аргументы, заключая каждый в кавычки.

Данный сценарий подразумевает, что двоичные файлы Java являются частью переменной среды PATH. Если данный сценарий не работает на вашем компьютере, см. Установка переменной среды PATH.

Дополнительные сведения о написании сценариев для интерпретатора bash можно найти здесь.

Сценарий .bat для компьютеров Windows

На компьютерах Microsoft Windows пакетным файлам можно одновременно передавать не более девяти аргументов. При наличии более девяти аргументов файл JAR будет необходимо исполнить несколько раз.

Сценарий для этого может выглядеть следующим образом:

                @echo off
                set jarpath="dist/AnotherGrep.jar"
                set pattern="%1"
                shift
                :loop
                  if "%1" == "" goto :allprocessed
                  set files=%1 %2 %3 %4 %5 %6 %7 %8 %9
                  java -jar %jarpath% %pattern% %files%
                  for %%i in (0 1 2 3 4 5 6 7 8) do shift
                goto :loop
                :allprocessed

Данный сценарий можно найти под названием grep. bat в папке, куда было извлечено содержимое архива DeploymentTutorial.zip, если необходимо увидеть его в действии.

Девять аргументов представлены внутри пакетного файла как %<ARG_NUMBER> , где <ARG_NUMBER> должен иметь значение в пределах <0-9> . %0 зарезервировано для имени сценария.

Можно увидеть, что за раз (в одном цикле) программе передаются только девять аргументов. Оператор for просто смещает аргументы на девять, чтобы подготовить их к следующему циклу. Цикл завершается по обнаружении пустого аргумента файла оператором if (это указывает на отсутствие новых файлов для обработки).

Дополнительные сведения о пакетных сценариях можно найти на этой странице.

Пакетирование приложения для Java Web Start

Java Web Start представляет собой технологию, используемую для запуска приложений Java из веб-браузера одним щелчком мыши. Подробные сведения об упаковке приложений для развертывания с помощью Java Web Start см. в разделе Включение Java Web Start в IDE NetBeans. Здесь мы приводим только краткое описание действий, необходимых, чтобы сделать приложение развертываемым с помощью Java Web Start.

  1. Щелкните правой кнопкой мыши узел проекта в окне Projects («Проекты») и выберите Properties («Свойства»).

  2. На вкладке Web Start окна Project Properties («Свойства проекта») установите флажок Enable Web Start («Активировать Web Start»).

  3. Выберите Local Execution («Локальное исполнение») из раскрывающегося списка Codebase («Кодовая база»), поскольку мы будем тестировать только локальное исполнение.

  4. Нажмите кнопку ‘Настроить’, чтобы подписать приложение, указав сведения в диалоговом окне ‘Подписание’.

Примечание. Начиная с обновления Java SE 7 Update 21, выпущенного в апреле 2013 г., рекомендуется использование подписанных доверенных сертификатов для всех Java-аплетов и приложений Web Start Для получения дополнительной информации см. Апплеты Java и Web Start — подпись кода.

  1. Сохраните все прочие параметры по умолчанию и нажмите кнопку «OК».

  2. Щелкните правой кнопкой мыши узел проекта и выберите Clean and Build Project («Очистка и сборка проекта»). Данная команда среды IDE удалит все ранее скомпилированные файлы и результаты сборок, скомпилирует приложение заново и соберет проект с новыми параметрами.

  3. Вне среды IDE откройте папку PROJECT_HOME/dist , после чего откройте файл launch.html в своем браузере. Появится тестовая страница HTML с кнопкой Launch («Запуск»).

  4. Нажмите кнопку Launch, чтобы открыть приложение. Можно будет увидеть, что Java загружена и приложение запускается.

Примечание. Некоторые браузеры сначала перенаправляют пользователей на страницу загрузок Java.

Советы по устранению проблем

Указание сопоставлений файлов JAR

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

Если тип файлов JAR сопоставлен с JRE, значок, представляющий этот файл, должен включать логотип Java.

Примечание. Иногда связи файлов JAR переключаются установленным ПО, например, программным обеспечением для обработки файлов в формате zip.

Способ, которым тип файлов JAR можно привязать к средству запуска java , зависит от операционной системы.

Примечание. Убедитесь, что на вашем компьютере установлена одна из версий JRE. Следует использовать версию 1.4.2 или более поздние. Приложения Java нельзя запускать на компьютерах, где не установлена платформа Java. (В случае установки комплекта для разработчика на языке Java (JDK) с ним устанавливается и JRE. Однако если программа распространяется не только среди программистов, у пользователей может не быть JRE или JDK.)

  • На Windows XP установленную версию Java можно проверить выбрав «Пуск» > «Панель управления» > («Установка и удаление программ») (там должна быть указана, например, Java™ 7 Update 51).

  • В Windows Vista или 7 установленную версию Java можно проверить, выбрав «Пуск > Панель управления > Программы и компоненты» (там должно отображаться название версии, например Java™ 7 Update 51).

Если на компьютере отсутствует платформа Java, JRE можно загрузить с сайта загрузок Java SE.

Если же она уже имеется на компьютере, но сопоставление файлов не работает, выполните действия для добавления связи файла JAR в Microsoft Windows:

  1. Выберите «Пуск» > «Панель управления».

  2. (Применимо только к Windows Vista). Щелкните ‘Панель управления’ > ‘Программы’.

  3. В Windows XP дважды щелкните ‘Свойства папки’ и выберите вкладку ‘Типы файлов’. Для Windows Vista или 7 щелкните ‘Программы по умолчанию’ и выберите ‘Связать тип файла или протокол с программой’.

    1. В списке «Зарегистрированные типы файлов» выберите файл JAR.

    2. (В Windows XP в разделе «Сведения» диалогового окна щелкните «Изменить программу»).

    3. В диалоговом окне «Выбор программы» выберите Java Platform SE Binary.

    4. Нажмите кнопку «OК» для выхода из диалогового окна «Выбор программы».

    5. Нажмите кнопку ‘Закрыть’, чтобы закрыть диалоговое окно ‘Свойства папки’ (в Windows XP) или диалогового окна связывания типа файла или протокола с конкретными программами (в Windows 7).

Примечание. Если файлы JAR files связаны с Java Platform SE Binary в системе, но двойной щелчок все равно не запускает файл JAR, может потребовать указание параметра -jar в связи файлов.

Чтобы указать параметр -jar в сопоставлении файла на Microsoft Windows XP, выполните следующие действия.

  1. Выберите «Пуск» > «Панель управления».

  2. В Windows XP дважды щелкните ‘Свойства папки’ и выберите вкладку ‘Типы файлов’.

  3. В списке «Зарегистрированные типы файлов» выберите файл JAR.

  4. В разделе «Сведения» диалогового окна щелкните «Дополнительно».

  5. В диалоговом окне «Изменение свойств типа файлов» щелкните «Изменить…​».

  6. В текстовом поле «Приложение, исполняющее действие» добавьте следующую строку в конце пути к JRE:

 -jar "%1" %*

После этого поле должно содержать текст, подобный следующему:

"C:\Program Files\Java\jre1.7.0_51\bin\javaw.exe" -jar "%1" %*
  1. Нажмите кнопку «OК» для выхода из диалогового окна «Изменение действия для типа».

  2. Нажмите кнопку «OК» для выхода из диалогового окна «Изменение свойств типа файлов».

  3. Нажмите кнопку «Закрыть» для выхода из диалогового окна «Свойства папки».

Примечание. Начиная с Windows Vista расширенные связи файлов можно настроить с помощью RegEdit. Дополнительные сведения см. в статье Что произошло с диалоговым окном типов файлов?.

Для систем UNIX и Linux процедура изменения сопоставлений файлов зависит от того, какая рабочая среда (вроде GNOME или KDE) используется. Загляните в параметры своей рабочей среды или ознакомьтесь с документацией по рабочей среде.

Установка переменной среды PATH

Если на компьютере невозможно запустить класс Java или файл JAR без указания на местоположение JDK или JRE, возможно, следует изменить значение переменной среды PATH системы.

При работе в системе Microsoft Windows процедура установки переменной PATH зависит от используемой версии Windows.

Ниже приведены этапы установки переменной PATH на системе Windows XP:

  1. Выберите «Пуск» > «Панель управления» и дважды щелкните «Система».

  2. В диалоговом окне «Свойства системы» выберите вкладку «Дополнительно».

  3. Щелкните вкладку «Переменные среды».

  4. В списке переменных среды пользователя выберите PATH и нажмите кнопку «Изменить».

  5. Добавьте местоположение JRE к концу списка путей. Местоположения в списке разделены точками с запятой (;). Например, если JRE установлен в каталог C:\Program Files\Java\jdk1.6.0_51, в конец переменной PATH необходимо добавить следующее:

C:\Program Files\Java\jdk1.7.0_51\bin
  1. Нажмите кнопку «OК» для выхода из диалогового окна «Переменные среды», затем нажмите кнопку «ОК» для выхода из диалогового окна «Свойства системы».

При работе в операционной системе UNIX или Linux способ изменения переменной среды PATH зависит от используемой программы интерпретатора команд. Ознакомьтесь с документацией по интерпретатору команд для получения дополнительных сведений.

Отправить отзыв по этому учебному курсу

Что дальше?

Дополнительные сведения о работе с IDE NetBeans см. на странице Поддержка и документация веб-сайта NetBeans.

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

Сведения о функциях сборки в IDE NetBeans см. в разделе Сборка проектов Java документа Разработка приложений в IDE NetBeans.

Документация JDK 20 — Главная

  1. Главная
  2. Ява
  3. Java SE
  4. 20

Обзор

  • Прочтите меня
  • Примечания к выпуску
  • Что нового
  • Руководство по миграции
  • Загрузить JDK
  • Руководство по установке
  • Формат строки версии

Инструменты

  • Технические характеристики инструментов JDK
  • Руководство пользователя JShell
  • Руководство по JavaDoc
  • Руководство пользователя средства упаковки

Язык и библиотеки

  • Обновления языка
  • Основные библиотеки
  • HTTP-клиент JDK
  • Учебники по Java
  • Модульный JDK
  • Руководство программиста API бортового регистратора
  • Руководство по интернационализации

Технические характеристики

  • Документация API
  • Язык и ВМ
  • Имена стандартных алгоритмов безопасности Java
  • банок
  • Собственный интерфейс Java (JNI)
  • Инструментальный интерфейс JVM (JVM TI)
  • Сериализация
  • Проводной протокол отладки Java (JDWP)
  • Спецификация комментариев к документации для стандартного доклета
  • Прочие характеристики

Безопасность

  • Руководство по безопасному кодированию
  • Руководство по безопасности

Виртуальная машина HotSpot

  • Руководство по виртуальной машине Java
  • Настройка сборки мусора

Управление и устранение неполадок

  • Руководство по устранению неполадок
  • Руководство по мониторингу и управлению
  • Руководство по JMX

Client Technologies

  • Руководство по специальным возможностям Java

Программирование на Java

Добро пожаловать в Программирование на Java. Я ваш инструктор, Энджи Джонс.

Я сертифицированный Java-программист, работаю на этом языке более 18 лет. Помимо программирования на Java в таких компаниях, как IBM и Twitter, я также работал адъюнкт-профессором, преподавая Java в колледже.

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

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

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

Чтобы пройти этот курс, вам потребуется Java версии 10 или более поздней версии, а также редактор IntelliJ.

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

Первое, что мы собираемся сделать, это установить Java.

Java — это продукт Oracle. Итак, мы находимся на сайте Oracle, и если я посмотрю сюда, я увижу, что у нас есть версия для Java 12, у нас есть версия для Java 11 и версия для Java 8.

Если у вас уже установлена ​​Java 10 или более поздней версии, все в порядке. Нет необходимости в обновлении.



Я хочу перейти на Java 11 просто потому, что Java 12 действительно новая (на момент написания этой записи). Просто чтобы быть в безопасности, я собираюсь использовать Java 11.

С лицензированием Oracle происходит много нового, поэтому я не собираюсь устанавливать коммерческую версию, а перейду на Open JDK, у которого есть версия с открытым исходным кодом.

Загрузите версию для своих операционных систем: Linux, Mac и Windows. Я собираюсь пойти с Mac один.

Примечание об установке Java

Следующие инструкции в этом разделе предназначены только для Mac OS. Для Windows в разделе «Ресурсы» ниже есть видео.

Я разархивировал этот файл, и теперь у меня есть эта папка JDK (jdk-11.0.2.jdk). Я возьму эту папку и положу ее в каталог библиотеки.

Итак, я скопирую это в: Library > Java > JavaVirtualMachines.

Если у вас нет ни одной из этих папок, просто создайте их.



Обратите внимание, что у меня уже есть JDK 10, теперь я просто собираюсь добавить JDK 11. Он должен запросить у вас системный пароль. Идите вперед и введите его.

Теперь, если вы зайдете в свой терминал на Mac (вы можете открыть его, перейдя в поиск Spotlight и введя «терминал»). Это должно открыть терминал для вас.

Теперь в терминале мы можем ввести java -version . Теперь вы должны увидеть, какую версию вы скачали.



Я скачал версию 11 и вот что я там вижу.

Следующее, что мы хотим сделать, это загрузить IntelliJ.

IntelliJ — это редактор, который мы собираемся использовать для написания кода Java (я предоставил ссылку на него в разделе ресурсов).



Вы хотите выбрать свою операционную систему. Вы можете выбрать Windows, Mac или Linux. Я на Маке.

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

Примечание об установке IntelliJ

Следующие инструкции предназначены только для Mac OS. Для Windows в разделе «Ресурсы» ниже есть видео.

Хорошо, я дважды щелкнул по загрузке.