Часто используемый способ - это скачать
launch4j, натравить его на свой jar файл и получить исполняемый файл. По сути это самораспаковывающийся архив с запускалкой и оригинальным файлом внутри, но пользователю это не видно.
Более правильный способ - это использовать
jlink:
Структура каталогов простейшего проекта
src
└───com.example
│ module-info.java
│
└───com
└───example
App.java
module-info.javamodule com.example { }
App.javapackage com.example;
public class App {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
Команды сборки
javac -d build\mods\com.example src\com.example\module-info.java src\com.example\com\example\App.java
jar --create --file=build\libs\com.example.jar --main-class=com.example.App -C build\mods\com.example .
jlink --module-path build\mods --add-modules com.example --output build\app\example --launcher start=com.example/com.example.App --compress=2 --no-header-files --no-man-pages --strip-debug
В результате в каталоге build\app появится каталог приложения example, который содержит в себе JRE и и саму программу в готовом для распространения виде. Правда, запуск всё ещё осуществляется с помощью батника - example\bin\start.bat
Естественно, лучше не набирать команды руками, а поручить это сборщику - Maven или Gradle.