Всем привет. Это информационная статья интерактивного характера - здесь я делюсь своими навыками, помечаю свои трудности (что-то глобальное я помечаю их как "Трудность №n", а мелкие неуверенности раскрываю в контексте), а Вы же оставляете свои вопросы, предложения и решения, которые могут пойти в улучшение данного материала для дальнейших пользователей.
Объединяя опыт, мы улучшаем быт!
Содержание статьи:
(для удобства можно делать поиск параграфов через "Поиск по странице" - сочетание Ctr + F или через "Оглавление" в начале статьи (функция Дзена))
- Описание содержимого статьи
- Установка/проверка Java
- Установка IDE NetBeans
- Навигация в IDE NetBeans
- Создание проекта
- Создание графической части. GUI-элементы конструктора
- Создание функциональной части
- Архивирование проекта в исполняемые файлы (.jar и .exe)
Описание содержимого статьи
О чем: о создании игры "2048" с простым графическим интерфейсом в программе NetBeans на языке программирования Java, но с основанием "3", где детально будет рассказано о всех этапах ее реализации.
Для кого: текст написан подробно о простом, поэтому он должен быть актуальным для начинающих программистов. Если же у Вас есть хороший опыт, то ждем Ваши комментарии.
И зачем: большая часть всех Интернет-ресурсов, связанных с использованием интегрированной среды разработки (IDE) NetBeans и языка программирования (ЯП) Java, является англоязычной. Также стоит отметить некоторую их устарелость, как в плане содержания (многие написаны для 8 версии, а сейчас уже есть 22), так и в плане хостинга (неработоспособность Интернет-ресурсов).
Установка/проверка Java
Установка Java
Прежде всего стоит рассказать о том, как персональный компьютер (ПК) работает с разными ЯП.
(Трудность №1 - тема уставки необходимого программного обеспечения (ПО) для работы ЯП сама по себе довольно абстрактная, потому что вся работа этого ПО происходит "за кадром". Поэтому дальнейший текст - это моё понимание своего опыта, нескольких просмотренных форумов и статей.
Насколько мне удалось понять, интерпретация ЯП всё равно в конечном счёте основывается на трансляции, поскольку интерпретатору или виртуальной машине, эмулирующей инструкции процессора, всё равно нужно передать команду физическому процессору на машинном коде.
Необходимость и отличие такого подхода от компиляции заключается в том, что появляется независимость от платформы, ведь конечная реализация машинного кода будет основана на работе созданного под конкретную машину интерпретатора. Может у интерпретатора проявляется и важное отличие в семантическом анализе кода, так как код в интерпретаторе выполняется по блокам, а не весь сразу - имелось в виду то, что если бы код интерпретируемого языка попытаться полностью скомпилировать, то тогда бы ЯП с динамической типизаций приходилось бы каждый раз при встрече переменной вызывать блок проверки типа, вместо использования кэш памяти, созданной после одной из операций проверки)
Выполнение любой программы на ПК - это очередность нескольких этапов, где должен быть как минимум один этап трансляции/компиляции (перевода программы с одного ЯП на другой, в случае компиляции - на машинный код) и/или интерпретации (непосредственного выполнения кода программы без компиляции в машинный) . В конечном итоге всё приводится к машинному коду - системе команд взаимодействия инструкций процессора и устройств памяти (другими словами - это ЯП, который "читает" непосредственно процессор).
Количество этих этапов зависит от уровня языка и способа перевода.
Уровень языка может быть высоким и низким. Разница между ними в том, что программа, написанная на низкоуровневом языке максимально приближена к машинному коду (под этим подразумевается понятие "транслитерации" - дословно, это означает побуквенный перевод (типа, Привет - Privet), но в нашем случае имеется в виду покомандный перевод (одна команда ЯП - одна инструкция на машинном языке)).
Способы перевода - это определенная цепочка трансляций. Трансляция может быть разной, например:
- Компиляция всего кода в машинный.
- Трансляция кода в промежуточный байт-код, который потом загружают в интерпретатор или виртуальную машину, что в процессе работы компилирует машинный код по частям (преобразует свои инструкции в машинные инструкции, которые понимает процессор).
- Трансляция кода в промежуточный специальный код, который потом компилируют в машинный (как пример, промежуточный ЯП CIL в трансляторе C#; так поддерживается независимость ЯП от платформы (ПК)).
Благодаря этой информации можно понять то, какие инструменты нам могут быть необходимы для написания кода на разных ЯП.
Для написания программ на ЯП Java необходимо установить "комплект для разработки" Java Development Kit (JDK) - это набор необходимых инструментов, с помощью которого создают и запускают программы (виртуальная машина JVM + платформа для выполнения программ JRE + файлы необходимые для запуска, отладки, компиляции).
Скачивается с официального сайта компании Oracle: https://www.oracle.com/java/technologies/downloads/
Среди разных версий на сегодняшний день я могу посоветовать JDK 21, так как она имеет долгосрочную поддержку (LTS)
Для удобства скачайте .exe установщик:
В самом установщике просто необходимо пару раз нажать "Next" и оставить путь установочной папки по умолчанию.
Проверка Java
Чтобы проверить успешность установки, необходимо:
- Нажимаем на клавиатуре сочетание клавиш Win + R (открывает специальное меню "Выполнить" - многофункциональное меню, туда можно вставить что угодно, даже ссылку на сайт (после оно откроет вам этот сайт в браузере по умолчанию)).
- Написать в появившейся строке "cmd" и нажать "ОК".
- Написать в появившемся окне "java -version".
(Это окно командной строки Windows).
После этого должны снизу появиться строки, первая из которых "java version "21..."
Если такого не произошло, то нужно настроить "Переменные среды", это можно сделать так:
- В строке поиска Windows (слева внизу) написать "Изменение системных переменных среды" и открыть.
- Зайти в "Переменные среды...".
- Два раза нажать на строку "Path" в нижней табличке "Системные переменные".
- Нажать "Создать", вставить в ячейку текст "C:\Program Files\Java\jdk-21\bin" и нажать "Ок".
(Путь установки jdk до папки bin, поскольку именно в ней находятся исполняемые файлы (.exe), необходимые для работы с Java (JVM, компилятор, упаковщик .jar файлов)) - После этого желательно перезагрузить ПК.
(Может заработать и без этого - тут вся проблема в том, как операционная система проставила зависимости после наших изменений).
После этого можно снова провести проверку в командной строке.
Далее также необходимо создать новую системную переменную JAVA_HOME, указав в ней путь уже целиком до папки jdk - это необходимо для того, чтобы IDE смогли лишь по названию системной переменной получить путь до папки с jdk (в противном случае придётся его устанавливать вручную в настройка IDE).
Для этого:
- Под нижней табличкой "Системные переменные" нажмите "Создать".
- Впишите в графу "Имя переменной": JAVA_HOME, а в "Значение переменной": C:\Program Files\Java\jdk-21 (путь до папки с jdk).
Если у вас уже до этого была установлена Java, то необходимо удалить её из переменной среды Path и изменить значение в JAVA_HOME.
Можно оставить несколько разных версий Java в Path, но в JAVA_HOME должна быть только одна версия. В этом случае у вас будет работать только одна из версий Java (может быть, та, что выше в списке), но в таком случае может случится несостыковка путей с JAVA_HOME, из-за чего вы можете начать программировать в IDE на другой версии, а из-за этого, особенно если вы программировали на 21, а запуск .jar файла происходит на 8, вы получите ошибку версий "a java exception has occurred".
Также может быть что даже при удалении всех остальных версий Java из Path и самих директорий (папок) со старыми jdk или jre, у вас всё равно не будут запускаться файлы .jar (при том, что проект в IDE будет отлично работать) - это может быть потому, что в реестре операционной системы до сих пор указан путь на старую папку jdk или jre в графе открытия файлов формата .jar. Можно попробовать решить это так:
- Нажимаем сочетание клавиш Win+R.
- Пишем regedit (это редактор реестра (это настройки, предустановки, необходимые для взаимодействия программ на ПК)) и нажимаем "ОК".
- В верхней части окна есть белая строка, туда пишем "HKEY_CLASSES_ROOT\.jar" и смотрим, чтобы в "Значение" было указано "jarfile".
- Далее пишем "HKEY_CLASSES_ROOT\jarfile\shell\open\command" и смотрим чтобы был указан путь до вашей папки jdk или jre, bin и до приложения javaw.exe. Также необходимо указать параметры "-jar "%1" %*".
Полный путь у меня: "C:\Program Files\Java\jdk-21\bin\javaw.exe" -jar "%1" %*.
Установка IDE NetBeans
IDE (интегрированная среда разработки) - это ПО, которое позволяет комфортно программировать, посредством объединения необходимых инструментов и GUI (ведь также можно программировать и в обычном "Блокноте"), а именно:
- Текстовый редактор, выделяющий синтаксис, ошибки и обеспечивающий автодополнение кода.
- Возможность подключить систему контроля версий (Git).
- Поддержка разных расширений (пакеты, фреймворки).
- Поддержка автоматической сборки проекта (компиляция, создание конфигурационных файлов, упаковка в исполнительный файл).
NetBeans - это бесплатная IDE с открытым исходным кодом. У неё есть возможность "визуального" создания GUI для приложения методом перетаскивания. У нее простой и приятный дизайн (в сравнении с IDE Eclipse), а также она более "легкая" по использованию памяти (в сравнение с IntelliJ IDEA).
Для установки необходимо перейти на официальный сайт Apache NetBeans в раздел загрузки: https://netbeans.apache.org/front/main/download/archive/
Здесь предлагаю выбрать самую последнюю версию (на момент написания - это 23) и нажать на кнопку "Download".
Далее нужно скачать .exe файл, выбрать ссылку для скачивания и запустить его.
(Трудность №2 - раньше этого не замечал, но при установке IDE нам предлагают установить JDK. Получается, что можно было обойтись без первого шага, но он всё равно полезен, так как объясняет значение JDK)
По пути установки необходимо оставить все без имений, поскольку там лишь предлагают установку Java SE/EE (Это пакет для разработки на Java, и, можно сказать, сам ЯП (его спецификация), стандартная и корпоративная версии; имеется в виду дополнительные библиотеки и инструменты) и дополнительных средств, касающихся web-разработки, что в сумме весит лишь около 1 гигабайта.
Также появляется окно выбора пути установки IDE и JDK - рассматривайте относительно место на жестком диске (в сумме их вес не превышает 2 гигабайтов).
Навигация в IDE NetBeans
Открываем IDE NetBeans (если нет ярлыка на рабочем столе, то создайте его из .exe файла из папки по пути "netbeans\bin\netbeans64.exe" (тут указан относительный путь от места установки IDE)).
У меня установлена 22 версия, так что, могут быть небольшие различия.
У меня измененный цвет темы (с белого на черный), чтобы сделать также необходимо:
- Перейти во вкладку "Tools", из выпадающего списка выбрать "Options".
- Перейти во вкладку "Fonts & Colors", изменить профиль на "FlatLaf Dark" и нажать кнопку "Apply".
- Перейти во вкладку "Appearance", далее в "Look and Feel" изменить профиль на "FlatLaf Dark" и нажать кнопку "Ok".
- После этого перезапустите IDE.
Конечное рабочее окно должно выглядеть так:
Слева расположено окно с проектами и пакетами (пакет - это "папка" с классами, где каждый класс сохранен в отдельном файле расширения .java; проект - это "папка" с пакетами). Чуть ниже него окно элементов класса (название класса, методов и переменных). Справа окно редактора кода, где пишется код, а под ним терминал, где выводятся значения исполняемого кода.
Чтобы открыть редактор кода во весь экран нужно дважды нажать на название файла в окне редактора кода. Вот пример:
Чтобы вернуть всё назад сделай все действия в обратном порядке.
Если у вас окно Projects и открываемых .java файлов слилось в одно, то нужно перейти во вкладку "Windows" и нажать "Reset Windows".
Создание проекта
Чтобы создать проект необходимо нажать на вкладку "File" и далее на "New Project".
Далее нужно выбрать сборщик проекта. Я возьму Ant (лучше взять Gradle, но в рамках нашего проекта мы даже не полезем в папку сборщика, поэтому это неважно). Также нужно выбрать тип создаваемого проекта. Я возьму "Java Application".
(Трудность №3 - с логической точки зрения, нет разницы между созданием "Java Application" без Main класса и "Java Class Library", потому что потом мы создаем новый класс JFrame, содержащий метод Main, и сборщик кода должен это автоматически учесть и вписать в конфигурационный файл)
После этого можно поменять название проекта и путь его установки. В данном окне необходимо убрать галочку с пункта "Create Main Class", так как мы созданим свой новый класс с этим методом.
Далее необходимо создать JFrame Form (Название класса для создание GUI). Нужно нажать правой кнопкой мыши на название проекта и выбрать "New", "JFrame Form...".
Все, проект настроен, можно начинать выполнять основную задачу.
Создание графической части. GUI-элементы конструктора
Если мы сейчас нажмем на кнопку Run (или нажмем на клавиатуре f6), то появится пустое окно в углу экрана:
Теперь нам необходимо создать непосредственно GUI. Закрываем это пустое окно и переходим во вкладку "Design", которая находится под названием файла.
Теперь методом перетаскивания мы будем добавлять элементы из правого окна (номер 1) в левый прямоугольник (номер 2), и изменять их свойства после выбора необходимого элемента (выбрать элемент можно в окне 2 или 3, изменить свойство можно либо в окне 4 после выбора, либо нажав на элемент правой кнопкой мыши и выбрав "Properties").
Далее окна 1, 2, 3 и 4 будут названы как окна Palette, Design, Navigator, Properties соответственно (название каждого окна указывается в его верхней части).
Для начала нам необходимо установить в окно JFrame макет расположения элементов (layout). Их есть много разных, для более детального ознакомления можно провести поиск в интернете по запросу "layout Swing", но нам для простоты и удобства отлично подойдет Border layout. Вот пример того, как будут располагаться элементы:
Теперь нам необходимо создать 5 панелей (можно назвать их "папками", в которые мы потом поместим элементы), на которых мы потом будем располагать текст и игровое поле. Сделаем это просто путем перетягивания пока что одного элемента "Panel" из раздела "Swing Containers".
Теперь методом копирования одного jPanel и вставки в JFrame (нужно на него нажать, чтоб он выделился) необходимо создать еще 4 панели:
Теперь покажу простой чертеж того, что у нас должно получиться в самом конце, чтоб понимать дальнейшую работу:
Тут представлено расположение тех 5 панелей, которые мы только что установили.
Далее то, что будет в них установлено:
Здесь представлено:
- Картинка логотипа.
- Окно счета игры.
- Кнопка "Новая игра".
- Кнопка "Выход".
- Основное игровое поле - сетка 4 на 4 квадрата.
- Текст с правилами.
- Кнопка сдвига чисел вверх.
- Кнопка сдвига вправо.
- Кнопка сдвига вниз.
- Кнопка сдвига влево.
Теперь схема их размеров в пикселях:
Теперь начнем указывать размеры уже установленных элементов.
Помимо размеров добавим фоновый цвет, а также некоторые свойства для рамки (JFrame), а именно:
- resizable false (убрали галочку - значит false, стоит галочка - значит true)- невозможность расширить рамку - так как у нас элементы без привязки к краям экрана, то они не будут масштабироваться, а просто останутся в прежнем размере. При расширении окна изменится лишь размер прежнего фона, что нам не надо, поэтому отключаем такую возможность.
- title - название, которое будет в верхней рамке (где обычно расположены кнопки свернуть и закрыть).
- alwaysOnTop true - чтобы всегда это окно было поверх других - для удобства игры.
Мы изменили размер на первом фото в 4 разных строках - можно было бы и меньше, обойтись только "size", поскольку если он установлен, то игнорируется остальные 3 значения, но чтобы нас не спутали разные цифры в этих строчках - мы заменим их все на одно значение (значение указано в пикселях, высчитано из рисунка).
В вкладке Code мы устанавливаем размер элемента для окна Design, то есть, там можно поставить и 1000 на 1000 (изменится вид нашего элемента только в окне разработки), но при запуске появится окно 270 на 500.
Также отредактируем панели, в них мы изменим цвет (background - он изменяется тремя значениями RGB палитры), размер и Direction - положение в окне.
Теперь добавим элементы в верхнюю панель.
Далее можно поступить двумя способами, которые (именно в нашем случае из-за схожести логики Layout) приведут к одному и тому же результату (в зависимости от Layout и целей проекта можно использовать разные методы, но корректнее всего первый):
- Сначала изменить Layout панели, а потом добавить элементы.
- Сначала добавить элементы, а потом изменить Layout панели.
Можно Layout для этой панели пока не менять, поскольку изначально он "Free Design", что позволит удобно устанавливать элементы в любое место панели. А после изменить его на Absolute Layout, который будет располагать элементы относительно координат. В нашем случае положение элементов не изменится - изменится только способ их привязки. Данный способ может быть удобен так как у Absolute Layout шаг расположения элемента в окне Design 10 пикселей (то есть был на позиции (x,y) = (20,30), а при сдвиге вправо на один шаг будет (x,y) = (30,30)), что может быть не очень комфортно.
Необходимость изменения Free Design на почти такой же заключается в некоторой тонкости:
Мы сделали свой выбор в пользу Absolute Layout, так как у Free Design есть неочевидная привязка положения элемента к изначальному месту установки. После изменения размера элемента происходит сдвиг всех элементов из особенности этого Layout, а именно - связи с объектами со всех 4 сторон. Может случиться такое, что последующее измение значений этих "связей" не изменит позиции элемента из-за той самой "неочевидной привязки положения элемента к изначальному месту установки".
(Трудность №4 - изначально я использовал именно Free Design, но столкнулся с такой проблемой, что положение некоторых элементов не изменилось несмотря на установку нового значения отступа, что я объяснил выше. Пример ниже на фото.)
Мы пойдем по первому пути, поэтому изменяем Layout на Absolute Layout.
Создадим по два Label и Button в jPanel1 путем перетаскивания.
После следует изменить настроить эти элементы, а именно: размер, расположение относительно окна, содержание (цвет и текст или изображение).
Сначала изменим размер каждого элемента: можно изменить только значение preferredSize, поскольку наша рамка (JFrame) не масштабируется. Лучше выбирать объекты из окна Navigator, потому что после изменения размера объекты могут неудобно сместиться или вообще выйти из поля панели (это и произошло на фото ниже).
После изменим расположение относительно окна каждого элемента.
Тут необходимо задать правильное значение координат X и Y. Координаты считаются от верхнего левого края панели (это важно, считается от края родительской панели, а не всей рамки). Точка расположения элемента, относительно заданной координаты - верхний левый угол.
Высчитываем координаты по схеме (также не забудьте, что шаг одного сдвига 10 пикселей - это поможет расположить элементы справой стороны, так как они все расположены от края панели на 10 пикселей).
Далее добавим текст и изображение логотипа.
Самый простой вариант создать изображение - это использовать Paint. Предлагаю сделать такую картинку:
- Открываем Paint.
- Измените размер, убрав галочку с "Сохранить пропорции".
- Сделайте заливку, используя "Заливка цветом" - "Изменение цветов". Укажите в значениях "Красный", "Зеленый" и "Синий" значения фона панели, а именно - 231, 222, 214 соответственно.
- Добавляем элемент "Текст" (выглядит как большая буква "А") и пишем "2048" и "based on". Сделайте "2048" кегль 36, а "based on" кеглем 14. Сделайте жирный шрифт. Используйте серый цвет.
- Возьмите карандаш и нарисуйте красную цифру 3.
- Сохраните в удобное место. Можно в формате .
Далее добавляем картинку для jLabel1. Нужно в Properties выбрать icon, нажать на многоточие, нажать"Import to Project..." и импортировать картинку, выбрав ее и нажав "Finish". После нажимаем "OK"
Далее добавим текст (вообще более правильно добавлять текст, используя отдельно созданные переменные, которым мы присвоим в качестве значения наш текст, а после присвоим его элементу, вызвав специальную функцию - так более удобно управлять этим впоследствии, но мы пойдем более простым путём) и изменим его расположение с размером.
Нужно в Properties выбрать text и вписать нужный текст. И двум переменным horizontalAlignment и verticalAlignment дать значение CENTER (дали горизонтальному и вертикальному расположению установку на позицию в центре).
Также нужно изменить font, нажав на многоточие. Выбрать Font Style - Bold и Size 14.
Теперь изменим кнопки. Изменим цвет background (фон) и foreground (шрифт). Также дайте параметру focusable значение false, чтобы кнопка не обводилась синим цветом при нажатии.
Добавь текст в нижнюю панель.
Нужно добавить элемент Label из окна Palette методом перетаскивания. После, так как у нас Layout "Free Design" изменить "связи" со всех четырех сторон.
Делается это двумя способами:
- Нужно выбрать элемент, а после двойным нажатием на один из четырех серых прямоугольников ("связей") изменить его размер (сделать так 4 раза).
- Нужно выбрать элемент, а после нажать на него правой кнопкой мыши и выбрать "Edit Layout Space..." и изменить значения сразу четырех "связей".
Изменяем значение "связей" согласно схеме, а также размер самого элемента.
Нужно отцентровать текст (двум переменным horizontalAlignment и verticalAlignment дать значение CENTER).
Далее введите текст. Чтобы ввести текст с переносом, нужно записать его в html-тегах (<html> и <\html>), используя <br> в качестве специального символа для переноса. Также можно создать простой стиль в теге <div> (базовый блок текста), написав <div style='text-align:center'>.
Вот пример текста: <html><div style='text-align:center'>Press the buttons on the sides of the table to play.<br>You need to score 3072 points on one tile.</div></html>
(это запись на языке HTML)
Теперь создадим основное игровое поле.
Измените цвет фона на [231,222,214]. Далее нужно изменить Layout центральной панели на BorderLayout, добавить в нее одну пустую панель и 4 кнопки.
Далее нужно всё расставить и подогнать по размерам (preferredSize, Direction).
После необходимо добавить текст на кнопки и цвет.
С этим всё немного странно, поскольку мы будем использовать разные шрифты и кегли для символов, а также по разному писать эти символы.
Для начала необходимо убрать border в Properties для каждой кнопки - из-за него боковые кнопки не будут отображать текст (им не будет хватать места, поскольку граница (border) уменьшает количество места для текстового поля). Для этого в border выберете (No Border).
Нам нужно отобразить значки для кнопок: >, <, ∨, ∧. Так как у этих символов визуально разный размер, то это мы будем сглаживать это разными стилями шрифтов и кеглями, а именно Times New Roman 18 bold для ∨, ∧, и Arial 20 bold для >, < (всё это я выбрал экспериментально, поскольку не все шрифты вообще имели символы ∨, ∧ в своей кодировке).
После нужно написать символы в поле text. Проблемы возникают с символами ∨, ∧. Для того чтобы их написать и они корректно отобразились (простое перекопирование не всегда помогает (у меня оно отобразило лишь при изначально установленном шрифте, а после увеличения кегля символ сломался)) нужно использовать html-тег.
- <html>∧</html> для ∧
- <html>∨</html> для ∨
Теперь заполним центр (jPanel4).
Для этого добавим Grid Layout - это Layout таблица. И добавим ей 4 Columns (столбца) и 4 Rows (строки). И добавим в неё один Label.
Теперь настроим jLabel4, чтобы потом его взять за образец и перекопировать 15 раз (поле 4 на 4 = 16 ячеек).
Добавим цвет фона, текста, уберем базовый текст, добавим шрифт, отцентруем текст, добавим обводку (border).
Обратите внимание, чтобы значение opaque было true - иначе jLabel4, за исключением его обводки, будет прозрачным.
Обводку нужно создать с учётом некоторых математических вычислений.
Толщина обводки и размеры (длина и ширина) Label должны поместиться в панель 200 на 200 пикселей. Значит должно выполниться равенство:
- border = b; Label = L
- b + L + b + L+ b + L+ b + L + b = 200
- 5*b + 4*L = 200
Например, если взять обводку толщиной в 3 пикселя, то получится:
- 5*3 + 4*L = 200
- L = (200-15)/4 = 185/4 - не целое число, а значит появится незаполненная щель между кнопками и ячейками с двух краёв (так логика заполнения идет слева направо, и сверху вниз, то щель будет снизу и справа)
Поэтому возьмём border толщиной равно 4.
Создадим 15 копий jLabel4 (таблица 4 на 4 = 16 ячеек).
И сейчас остался последний шаг с этой часть GUI - это уравнять значение border и элементов таблицы (так как border считается частью элемента, то расстояние между двумя элементами внутри таблицы равно двум border (граница одного + граница другого), а толщина внешнего края таблицы остается равной одному border), поэтому необходимо пододвинуть элементы в таблице на расстояние одного border.
Сделать это можно в Properties для GridLayout, передав значение border со знаком минус.
Для проверки можете изменить значение opaque (непрозрачность) в Properties для панели, в которой лежат 16 Label. Если не будет щели снизу и справа, то всё рассчитано правильно.
Вот пример того, что будет если сделать толщину border равной 3.
Теперь создадим новое окно, которое будет появляться при проигрыше или выигрыше, предлагающее нам либо начать новую игру, либо выйти.
Для этого создадим новый класс JFrame Form, как уже создавали ранее.
Зададим размер форме 300 на 200 пикселей, resizable false, title и alwaysOnTop true.
Измените Layout рамки на BorderLayout (для удобства, чтобы потом поставить панель в центр и не нужно было бы его растягивать по краям, как это было бы в Layout Free Design) и добавьте в центр панель. Измените ее цвет фона на [231,222,214].
Потом измените Layout панели jPanel2 на GridLayout и создайте в нем 2 строки. После добавьте в верхнюю строку Label, а в нижнюю еще одну панель jPanel3 и измените ее прозрачность (в Properties панели измените значение opaque false).
Далее измените Layout панели JPanel3 на GridLayout и создайте в нем 2 столбца.
Добавьте в каждый из них по одной панели, измените их прозрачность и их Layout на CardLayout (располагает элементы один над другим). В настройках этих Layout измените Horizontal и Vertical Gap на 10. Поместите в каждую панель по кнопке (jButton1 и jButton2).
Потом нужно изменить цвет фона и текста кнопок, изменить сам текст кнопок, шрифт кнопок jButton1 и jButton2. Для jLabel1 нужно изменить шрифт, цвет текста и отцентровать.
На этом всё создание GUI завершено.
Создание функциональной части
Теперь переходим в окно Source для написания кодовой части (находится сверху под названием файла).
Благодаря тому, что мы пишем код в IDE, созданный нами GUI автоматически собрался в необходимую кодовою структуру - унаследовались классы графического пакета, появились специальные методы и поля.
(Единственная проблема этого в отношении IDE NetBeans (например, по сравнению с Android Studio) то, что эти участки кода неизменяемы (например, нельзя создать базовую структуру через графическую часть, а кодом уже её изменить)).
- Метод - это обособленный блок кода (код представляет собой набор инструкций) внутри класса. (Также существует термин "функция", который имеет схожее толкование. Разница лишь в том, что функция - это независимая часть кода, а метод - зависимая от класса).
(Так как в Java вся кодовая часть реализована через классы, то в Java существуют только методы). - Поле - это переменная, принадлежащий классу. А переменная - это сущность (то есть, это нечто, чем можно управлять, а не инструкция, которая говорит о том как управлять), занимающая определенную область памяти компьютера.
(Такое различие появилось из-за подхода к программированию в Java - ООП (объектно-ориентированное программирование)).
Сейчас код довольно прост:
- public class NewJFrame extends javax.swing.JFrame - создали класс с названием NewJFrame, который унаследует методы и переменные класса javax.swing.JFrame.
- public NewJFrame() - конструктор класса; он автоматически вызывается и исполняется при создании экземпляра класса (представление класса как переменной).
- private void initComponents() - это метод, создающий наш GUI.
- Далее в методе main происходит настройка компонентов GUI в операторе try, создавая их в соответствии какой-то определенной концепции (Nimbus, в нашем случае) (Имеется в виду то, как буду выглядеть и работать те или иные элементы. Например, кнопки, потому что мы не создавали полностью их внешний вид, он был создан автоматически (анимация нажатия, форма)).
После в методе main вызывается из другого класса (EventQueue), который регулирует действие пользователя (движение мышью, нажатие клавиш) и отрисовывает GUI ,обрабатывая их в отдельном потоке задач, метод (invokeLater), который загружает в этот поток задач объект (new Runnable()), который является анонимным классом (это экземпляр класса, где класс тут же создаётся, но не имеет имени - такое обычно нужно, чтобы дописать методы других классов (это как раз и используется в нашем случае) (но так как он тут же создаётся и не имеет имени, то после нельзя создать новый экземпляр этого класса)), который расширяет интерфейс Runnable (это что-то вроде класса), в котором дописывает существующий метод run(), создавая в нём экземпляр нашего основного класса NewJFrame, для которого мы вызываем метод setVisible (включаем видимость отображение GUI). - И снизу объявляются необходимые поля (созданные ранее в вкладке Design Panel, Button и Label).
Начнем писать код после сгенерированного метода "private void initComponents()" (его можно свернуть, чтоб не машал, нажав на значок "-").
Для начала нам нужно создать поле - матрицу (или же таблицу) 4 на 4 ().
Создание полей (и переменных) проходит через два этапа:
- Объявление (резервирование места под хранение данных).
- Инициализация (загрузка данных в зарезервированное место).
Запись объявления поля (а также переменных и методов) может сопровождаться модификаторами, которые записываются поочередно:
- Модификаторы доступа (public (доступно для вызова за пределами класса), private (доступно только внутри класса), protected (доступно только в семействе класса), без модификатора (доступно в текущем пакете)).
(Такой модификатор может быть только один). - Остальные модификаторы (например, static (при измении значения изменяет значение во всех копиях), final (делает изменение значения невозможным)).
(Таких модификаторов может быть несколько, при условии, что они не будут конфликтовать).
(Важно, что для переменных можно использовать не все модификаторы, а только final).
Далее в записи пойдет тип данных (например, int - целочисленный, double - для десятичный дробей (но тут нужно учитывать погрешность вычисления процессором (в зависимости от задачи, например в 0.000001) (то есть, логическое выражение 0.1 + 0.2 == 0.3 будет равно false, так как на самом деле у этих значений может отличаться десятый знак после запятой) - она появляется из-за того, что процессор может работать только с числами в двоичной системе счисления, а не все десятичные дроби могут иметь конечный вид записи в двоичном коде), String - строковый, boolean - логический (булевый) (может принимать только значение true или false))
И после - название (обычно методы и переменные называют с маленькой буквы, а классы с большой).
В случае с полями (и переменными) - дальше может пойти как знак разделитель инструкций ";" (показывает транслятору/интерпретатору, что после идёт новая инструкция) или оператор присваивания "=".
- Если мы поставим знак разделителя инструкций ";", то после необходимо будет снова обратиться к данному полю (или переменной), чтобы присвоить ему какое-либо значение.
- Если мы поставим оператор присваивания, то после него необходимо написать что именно мы присваиваем (тут может быть как очевидное (какое-то значение: число, строка, массив), так и неочевидное присваивание (метод, возвращающий какое-то значение)) - это и есть инициализация.
В случае с методами - после названия в скобках указываются параметры (входные значения; может быть одно, несколько или не быть вовсе) и далее в фигурных скобках пишут код метода.
В зависимости от необходимости бывают разные способы объявления методов (у методов нет инициализации, потому что они не могут хранить конкретное значение, как поле или переменная):
- Если указано специальное слово void (пишется вместо типа данных), то метод не возвращает значение.
- Если указан тип данных, то в логическом конце (их может быть несколько) кода должно стоять слово return и имя переменной, того же типа данных, что указали при объявлении метода, которую будет возвращать метод.
Обращение к созданным методам и полям (или переменным) происходит посредством написания их имени:
- Например, есть переменная int i; то обращение к ней - i = 10;
- Например, есть метод void i() {} то обращение к нему - i();
Но тут нужно учитывать:
- Для полей и переменных - область видимости: есть глобальная (это поля - переменные класса - к ним можно обращаться из любого места класса) и локальная (это переменные - если переменная объявлена в методе или условном операторе (в параметре или в коде), то к ней нельзя обратиться вне метода или условного оператора).
- Для полей, переменных и методов - модификаторы: например, если поле или метод static, то их можно вызывать из любого места внутри класса без создания экземпляра класса (то есть, так, как указано выше) и вне класса, указав в начале название родительского класса через точку (например, NewJFrame.i - то есть, так же без создания экземпляра), а если поле или метод non static, то их можно вызвать из любого non static места внутри класса без создания экземпляра класса (в противном случае нужно создать экземпляр класса, например NewJFrame instanseNewJFrame = new NewJFrame(); instanseNewJFrame.i) и вне класса только через экземпляр.
Теперь для написания кода нужно продумать способ взаимодействия полей и методов внутри и за пределами класса:
- Использовать static поля и методы для изменения окна - для управления ими внутри и за пределами без создания экземпляра.
- Использовать non static поля и методы для изменения окна - для управления ими внутри static методов и за пределами класса создавать экземпляр класса и передавать на него ссылку.
Оба этих способа позволят нам добиться одного и того же результата, но тут стоит понять полезность каждого из них:
- В обоих случаях нам необходимо будет создавать экземпляр класса главного окна внутри метода run (для загрузки данных в поток).
- С одной стороны, мы можем создать поля и методы non static и далее обращаться к ним посредством имени уже созданного экземпляра.
- С другой стороны, нам необходимо будет внутри одного из методов создать и перейти в новое окно (в конце игры), из которого нужно будет управлять основным окном (обращаться к его методам и полям). В этом случае удобно использовать static поля и методы, чтоб не объявлять экземпляр класса и инициализировать его через переданную на него ссылку.
- Использование static полей и методов можно также утвердить тем, что под каждое определённое окно используется свой определённый класс. Поэтому будет логично и удобно, если методы и поля будут принадлежать именно классу, а не экземпляру (то есть, можно создать много экземпляров, но при изменении поля в одном из них - оно изменится во всех, поэтому принято говорить, что в таком случае поле принадлежит классу, а не экземпляру).
- Также, если после создания игры мы запустим сразу несколько наших приложений, то, несмотря на на модификатор static, приложения будут независимы друг от друга, поскольку будут выполнятся на разных JVM, у которых будет своя копия static значений в отдельном месте памяти компьютера.
Всё уходит в пользу использования static модификатора, но я воспользуюсь non static, потому что этот подход позволит затронуть некоторые общепринятые особенности написания кода, благодаря чему можно будет получить более обширный опыт, применимый и к другим проектам.
Чтобы создать матрицу 4 на 4 напишем такой код:
private (модификатор доступа (будем закрывать доступ для переменных и методов, если они не используются другими классами)) int (тип данных - целочисленный) [][] (объявление двумерного массива) array (название переменной) = (оператор присваивания) new int [4][4] (создание нового двумерного массива размерностью 4 на 4);
В новосозданной матрице все позиции будут заняты нулями.
Для массивов позволительно указывать квадратные скобки как после типа данных, так и поле названия переменной.
Далее нужен метод, который создаст случайное число с определенной вероятностью (для оригинальной игры 2048 используется правило: вероятность 1/10, что будет 4, 9/10, что будет 2).
Для написания метода будем использовать цикл и условный оператор (не перепутайте с условием внутри цикла). Основной код в них пишется как и у методов - в фигурных скобках
Циклы бывают (обратите внимание на скобки и знак разделителя инструкций):
- С предусловием - while (условие) {код}
- С постусловием - do {код} while (условие);
- С параметром - for (условие) {код}
Условные операторы могут писаться так:
- Без противоположного исхода - if (условие) {код}
- С противоположным исходом - if (условие) {код} else {код}
- С множеством условий - (как вариация предыдущих пунктов) после if, но перед else можно добавить несколько else if (условие) {код}
- С множеством значений - (иногда более удобный аналог else if) switch (выражение) {case значение -> {код}}
Тут используется не условие, а выражение. Выражение - это общее название. Оно может быть либо условным (логическим), либо арифметическим. Условное равно может быть равно только true или false, а арифметическое - например, 5 или "Привет" (сложение чисел или объединение строк (для строк этот процесс называется конкатенация))
Условия записываются в виде специальных выражений, используя логические операторы: == (равно), != (не равно), > (больше), < (меньше) , >= (больше или равно), <= (меньше или равно).
А для составления сложных условий используют также: && (условное И), || (условное ИЛИ).
Среди арифметических операторов выделяют: ++ (увеличение числа на 1), -- (уменьшение числа на единицу), += (сложение переменной и присваемого значения), -= (вычитание из переменной присваемого значения).
(Также есть несколько других операторов, но они уже не полезны в рамках нашего проекта).
Код метода будет выглядеть так:
В нашем случае создаётся либо 3, либо 6.
Логика кода:
- Создаем бесконечный цикл while (true).
(Выйти из условия true можно только принудительным обрывом цикла специальным словом break) - Создаём две целочисленные переменные randX, randY со значениями 0 <= x < 4 (это будет случайная позиция в матрице).
(Создаются с помощью метода nextInt() класса Random - базовый класс, его не нужно создавать - после написание инструкции с его указанием потребуют импортировать это класс в текущий файл - в первой строчке кода текущего файла появится строка "import java.util.Random;") - Проверяем условным оператором if является ли число на случайной позиции в матрице array[randX][randY] нулем (чтобы заполнить именно пустую клетку, а не ту, где уже есть число).
() - Если ноль, то создаём целочисленную переменную randInt с помощью метода nextInt() со значением 0 <= x < 10, если создался 0 (вероятность 1/10), то заполняем это случайное место матрицы числом 6, иначе 3, и завершаем бесконечный цикл.
- Если не ноль, то заново с пункта 2.
Теперь нужно чтобы числа с матрицы переписались в таблицу GUI.
Для игры необходимо также чтобы ячейки таблицы GUI раскрашивались в определенный цвет, который зависит от помещенного числа.
А ещё чтобы при достижении ячейкой числа 3072 (аналог 2048) окончилась игра и появилось уведомляющее об этом окно.
Для этого создадим три метода:
- Метод, который пройдет по всей матрице, проверяя, что за число в ячейках, и, в зависимости от этого, выберет цвет ячейки (она находит две переменные: число и цвет).
- Метод, который добавит число и цвет в ячейку.
Также учтём то, что при достижении одной ячейкой числа 3072 (аналог 2048) должна окончится игра и появится уведомляющее окно. - Метод, который создаст уведомляющее окно. (Метод "остановки игры" не нужен, так как такой уже существует в наследуемом классе).
Так как эти действия происходят последовательно (сначала нашли две переменные в 1 методе, потом отправили их для обработки в 2 метод, где также проверили их и при необходимости вызвали 3 метод) начнём программировать с последнего метода.
Чтобы сделать окно, которое бы открылось в конце игры, необходимо создать экземпляр его класса. Чтобы изменить в этом окне текст jLabel1, мы создадим метод в этом классе и вызовем его через экземпляр. Но чтобы добавить методы, выполняющиеся при нажатии кнопки ("начать новую игру" и "выход"), нужно обратиться к классу, из которого мы и создали экземпляр текущего окна (то есть, в NewJFrame мы создали экземпляр NewJFrame2, а после нужно из NewJFrame2 обратиться к NewJFrame, причем тому, из которого создали экземпляр NewJFrame2, а не новосозданному экземпляру NewJFrame, который бы добавили в NewJFrame2).
Так как мы не используем static модификатор, то в классе нового окна нам нужно объявить экземпляр старого окна, а после создания нового окна предать этому экземпляру ссылку на старое окно для инициализации.
Для этого есть два пути:
- Использовать конструктор класса для NewJFrame2 - при создании экземпляра класса NewJFrame2 мы передадим ему в качестве параметра ссылку на текущий экземпляр класса NewJFrame, это вызовет метод (его и называют конструктором), который выполнит указанные инструкции - в нашем случае, он инициализирует экземпляр класса NewJFrame.
- Использовать отдельный метод класса NewJFrame2 для инициализации экземпляра NewJFrame.
Воспользуемся первым способом, так как этот подход более удобный (позволяет при создании экземпляра сразу его настроить в одну строку кода).
Для этого откроем файл NewJFrame2.java, перейдем в вкладку Code.
Тут нам надо исправить несколько ошибок, связанных с генерацией кода при создании файла:
В этом файле имеется свой метод main, который запускает слежку за действиями пользователя (invokeLater). В пакете может быть только один метод main, да и слежка уже была запущена ранее, поэтому нам надо полностью удалить метод main.
После мы уже можем создавать поле NewJFrame и дописывать конструктор класса NewJFrame2 (добавим условие и код).
Специальное слово this говорит о том, что я ссылаюсь на переменную, объявленную вне метода (конструктора класса) - поле. Таким образом можно использовать переменные с одинаковыми именами внутри метода.
Можно это сравнить с именем экземпляра класса.
Потом нужно написать метод под названием setter - это метод, который позволяет изменять значение private полей класса из другого класса. Это нужно для того, чтобы изменять значение jLabel1 в случае проигрыша или выигрыша.
Упростим написание с помощью генератора кода. Для этого нажмите правой кнопкой мыши по строке, где хотите вставить код (я вставлю под конструктор класса), выберете "Insert Code..." - "Setter...". Далее отметьте те поля, для которых вы хотите создать данный метод (для каждого создастся отдельно) и нажмите "Generate".
После будет необходимо изменить параметр и код сгенерированного метода setter на получение текста и его установку для jLabel1.
Далее перейдем в файл NewJFrame.java в вкладку Code.
Напишем метод, который бы создавал экземпляр класса NewJFrame2, вызывал метод для изменения jLabel1, показывал на экране окно NewJFrame2 и останавливал процесс текущей игры окна NewJFrame.
Логика кода:
- Получаем на вход целое число choose для выбора значения в условном операторе (switch).
- Объявили и инициализировали экземпляр класса NewJFrame2, передав в качестве параметра специальное слово this - таким образом передаётся экземпляр класса NewJFrame.
- Проверяем значение в условном операторе switch. В обоих случаях передаём текст jLabel1 через setter, располагаем окно newJFrame2 по центру (setLocationRelativeTo(null)), делаем видимым (setVisible()) и отключаем окно newJFrame
После создадим метод добавления числа в ячейку:
Логика кода:
- Получаем на вход позицию ячейки таблицы (i - это строка, j - это столбец), цвет в системе RGB (r - число для красного, g - число для зеленого, b - число для синего цвета) - все целочисленные, и само число для ячейки (num) - строковый тип.
- Далее создаются строковые переменные ii и jj, которые хранят значение позиции ячейки на единицу больше (так как счет в матрице идет от 0 до 3) в формате строки (так как изначально они подаются в формате целого числа). (Это нужно для визуального удобства, поскольку таким образом (в формате {i,j}) обозначаются элементы матрицы в высшей математике).
- После в условном операторе switch они конкатенируют (объединяются) и переводятся из строки в целое число.
- В конце, в зависимости от числа, определённая ячейка таблицы GUI (их названия создались автоматически, когда мы их добавляли во вкладке Design - можете перейти туда и сверить расположение с названием) сначала получает текст методом setText, передавая в качестве параметра строку num, а потом меняет цвет методом setBackground, отправляя в параметр новосозданную переменную класса Color (базовый класс, необходимо импортировать в файл).
Потом создадим метод, который сделает проход по всем элементам матрицы, и, в зависимости от значения, выберет определенные числовые сочетания цветовой палитры RGB в формате трёх элементов.
Логика кода:
- Делаем перебор по всем элементам таблицы циклом for (создаём переменную i, проверяем i < 4<, если да, то выполняем код ниже и увеличиваем значение i на 1 (то же для j)) (идет перебор значений {i,j}: {0,0}, {0,1}, {0,2}, {0,3}, {1,0} ..., {3,3}).
- В зависимости от значения ячейки таблицы (от 3 до 3072 - геометрическая прогрессия с шагом *2, где первое число равно 3 (0 - это значение пустой ячейки, оно не входит в прогрессию)) делаем выбор условным оператором switch и передаем значения (среди которых самостоятельно подобрали числовые сочетания цветовой палитры RGB в формате трёх элементов) предыдущему методу colorTitle.
- Если число равно 3072, то также вызываем предпредыдущий метод window.
Далее нужно написать методы перемещения и сложения значений в таблице в зависимости от нажатой кнопки (вверх, вниз, вправо, влево).
Для этого потребуется:
- Метод перемещения и сложения.
- Метод проверки возможности перемещения (потому что не всегда нажатие на определённую кнопку может вызвать перемещение).
- Метод копирования матриц (для метода создания отработочной матрицы, которую можно будет как сравнить с изначальной в методе перемещения и сложения (для проверки изменения), так и отправить в метод проверки возможности перемещения).
- Метод сравнения матриц (для проверки изменённости матриц).
Так как эти действия происходят (почти) последовательно (сначала переместили и сложили значения и проверили изменилась ли после этого матрица (методом 3 и 4) в 1 методе; если матрица не изменилась, то потом проверили возможность перемещения в 2 методе (используя для сравнения методы 3 и 4)) начнём программировать с последнего метода.
Мы создали метод с возвращением значения, потому что нам необходимо было получить результат от выполнения метода (если делать метод без возвращения, то пришлось бы создавать static поле, над которым бы производились изменения в коде метода, что не имеет особого смысла, так как этот метод будет использоваться на локальных переменных).
Логика кода:
- Получаем на вход две целочисленные матрицы array1 и array2.
- Создаём целочисленную переменную count для счёта количества одинаковых элементов в двух матрицах.
- Делаем перебор по всем элементам матрицы циклом for.
- Делаем проверку элементов матрицы array1[i][j] и array2[i][j] условным оператор if. Если совпали то увеличиваем значение count на один.
- В конце перебора возвращаем значение count.
Далее Напишем метод копирования матриц (это необходимо, так как такие сложные объекты, как другие классы и матрицы передаются ссылкой, а простые, такие как int, boolean - передаются значением):
Логика кода:
- Получаем на вход одну целочисленную матрицу array2.
- Создаём целочисленную матрицу array1 для заполнения.
- Делаем перебор по всем элементам таблицы циклом for.
- Присваиваем переменной array1[i][j] значение array2[i][j]
- В конце перебора возвращаем значение array1.
Далее напишем метод проверки возможности перемещения.
Логику кода я прописал в комментариях над кодом. Главное это понять работу первого шага, поскольку остальные являются его полной аналогией:
- Происходит проверка шага по линии от самого последнего элемента (если влево/вправо, то идет просчёт по j, если вверх/вниз, то по i).
- Смотрят на текущий элемент, если он не первый (потому что перед ним ничего нет), то проверяют предыдущий: если он ноль, то двигают текущий на его место, а текущее место обнуляют, если равный, то суммируют, а текущий обнуляют, в ином случае смотрят на следующий элемент.
- Если уже произвели сложение элемента, то запомнили позицию этого элемента, чтоб больше с ним ничего не складывать.
Потом создадим метод для перемещения и сложения - он аналогичен прошлому методу.
Логика кода схожа с предыдущим методом. Новые шаги подписаны комментариями над кодом.
Главное, что тут произошло:
- Создали целочисленное поле очков countPoint, которое увеличивается при складывании двух клеток в одну.
- В методе moving() на вход получаем число, которое указывает направление перемещения (на них потом привяжем кнопки).
- В конце проверяем перемещение матрицы - если произошло, то создаём новое число на пустой клетке (random()), изменяем поле количества очков (setText), заполняем матрицу новыми числами и раскрашиваем ячейки (color()). Иначе проверяем возможность хода методом haveTry().
Теперь необходимо сделать метод, который бы начинал новую игру - отчищал матрицу (создал новую, обратившись к полю array), обнулял очки (присвоил ноль полю countPoint), создавал новое случайное число на пустой ячейке в матрице (вызов метода random()), раскрашивал ячейки (вызов метода color()) и выводил новое число очков (выполнение setText для jLabel2 с параметром countPoint).
Логика метода описана выше.
Осталось всего два простых шага:
- Создать методы для реакции на нажатие кнопок.
- Настроить метод main().
Для того чтобы создать методы для кнопок нужно:
- Перейти во вкладку Design.
- Выбрать кнопку, нажать на неё правой кнопкой мыши и выбрать Events - Action - actionPerformed.
- После этого откроется вкладка Code, где появится сгенерированный метод названиеКнопкиActionPerformed(). Внутри этого этого метода можно удалить комментарий "// TODO add your handling code here:" и на его строчке писать свой код.
Таким образом необходимо создать методы для всех кнопках для обоих окон (6 кнопок на главном окне и 2 кнопки на втором окне).
Для класса NewJFrame необходимо настроить методы кнопок следующим образом:
Логика кода написана в комментариях над кодом.
Для класса NewJFrame2 необходимо настроить методы кнопок следующим образом:
Логика кода также написана в комментариях над кодом.
Теперь можно изменить метод main().
(Переходим в класс NewJFrame во вкладку Code).
Необходимо вызвать метод setVisible() после вызова всех настраивающих окно методов, а именно: создание случайного числа (random()), заполнение матрицы значениями и изменение цвета её ячеек (color()), установка окна по центру экрана (setLocationRelativeTo()).
На этом всё программирование закончено, можно запустить проект и проверить работку приложения:
При отсутствии ходов (необходимо нажать на кнопку перемещения, даже после того, как по свершению заключительного перемещения внешне ходы стали невозможны, поскольку метод проверки ходов вызывается лишь после нажатия на одну из кнопок перемещения):
Архивирование проекта в исполняемые файлы (.jar и .exe)
Так как мы уже запускали проект, а то он уже является собранным в .jar файле - файл, содержащий код и вложенные файлы (например, картинки), готовый к запуску в JVM.
Но предлагаю его пересобрать ради избежание каких-либо низкоуровневых ошибок.
Для это следует зайти во вкладку Projects, нажать правой кнопкой мыши на название нашего проекта и выбрать из появившегося меню пункт Clean and Build.
После этого можем найти наш файл с расширением .jar в директории (папке) проекта.
Для того чтобы быстро туда перейти можно нажать правой кнопкой мыши на название нашего проекта и выбрать из появившегося меню пункт "Open in System". Далее перейти в папку dist. В ней и будет находится наш файл с расширением .jar.
Для того чтобы обернуть его в .exe файл можно воспользоваться разными инструментами.
Самый простой вариант - использовать веб сервис, но в нём обычно мало каких параметров можно указать при создании, которые могут быть необходимы для работы за большими проектами.
Среди десктоп приложений можно выделить Launch4j.
Воспользуемся Launch4j, потому что в ней можно указать библиотеки, которые будут классы, которые будут импортироваться (В нашем случае это класс AbsoluteLayout - это класс от NetBeans, а не от Swing, поэтому его приходится импортировать).
Я скачивал программу отсюда: https://launch4j.sourceforge.net/
Далее, необходимо перейти в папку, куда вы установили программу (потому что она автоматически не создаёт ярлык на рабочий стол), запустить одно из приложений (представлены в разных расширениях: launch4j.exe и launch4j.jar), а после произвести настройку:
- Во вкладке Basic нужно указать путь, куда вы сохраните файл в формате .exe, причём место сохранения должно быть тоже, где сейчас находится файл проекта с расширением .jar, поскольку в этой же директории мы имеем добавочные классы, а зависимости в конфигурационном файле (манифесте) записываются относительным, а не абсолютным путём (по крайней мере, таким он ставится изначально, а другим я не пытался его записать (поскольку это бессмысленно с точки зрения портативности)).
- Также нужно указать путь самого .jar файла (папка проекта dist).
- По желанию - путь до иконки (любой, тут уже место не важно, потому что картинка копируется внутрь .exe файла) (я быстро создал в Paint картинку 256 на 256 пикселей в формате .png, а после через веб сервис конвертировал её в .ico).
- Далее во вкладке Classpath можно оставить всё как есть, потому что мы указали путь до .jar файла проекта - в нём существует метод main (он запускается на JVM), а остальные классы, если они были импортированы через IDE, должны тоже подтянуться сами.
- Во вкладке JRE нужно лишь указать в графе "JRE paths": %JAVA_HOME%, если ранее строка была пустой.
- После нужно нажать сверху на "шестерёнку", сохранить конфигурационный файл программы (в любое место).
- А после нажать на "синюю" кнопку "Test wrapper" - должно запуститься приложение, а в папке dist создаться .exe файл (далее можно отправить ярлык себе на рабочий стол, главное не перемещать сам .exe файл из0за привязки к относительным путям других классов).
На этом всё.
Буду рад, если этот материал смог вам помочь.
Желаю успехов!