
作为一名java开发者,你是否遇到过这样的场景:辛辛苦苦写了一个桌面工具,发给客户或者同事,对方双击jar包毫无反应,或者弹出一个黑窗口报错"找不到主类",又或者被告知"你的电脑没有安装java"。这种交付体验不仅让用户抓狂,也让开发者颜面尽失。
本文将系统性地讲解java项目打包成exe的全部知识,从底层原理到实战操作,从工具选型到企业级ci/cd集成,让你看完就能直接上手,再也不用为java程序的交付问题发愁。
一、痛点场景:为什么java程序非要打成exe不可

1.1 真实场景还原
场景一:客户电脑没有jdk
你花了两周时间开发了一个数据报表生成工具,打包成jar发给客户。客户双击后没有任何反应,打电话过来质问:"你这软件是不是坏了?我双击了什么都没有。"你远程一看,客户电脑上连java都没装。你只能一步步教客户下载jdk、配置环境变量,折腾了一个小时,客户已经不耐烦了。
场景二:jdk版本不兼容
你的程序用java 17开发,客户电脑上装的是java 8。运行时直接抛出unsupportedclassversionerror,客户看不懂报错信息,只觉得你的软件"不好用"。
场景三:jar包双击没反应
即使客户装了正确版本的jdk,windows系统下jar文件的默认关联程序也可能不是java。双击后系统问你"用什么程序打开",普通用户根本不知道该选什么。
场景四:命令行启动体验差
你告诉客户"打开cmd,输入java -jar xxx.jar",客户一脸茫然。即使照着做了,一个黑窗口一直挂在那里,关掉黑窗口程序就退出了,体验极其不专业。
场景五:企业客户要求安装包
企业级客户的it部门有严格的软件分发规范,他们要求必须提供msi或exe安装包,能够静默安装、能在控制面板卸载、能创建桌面快捷方式。一个光秃秃的jar包根本过不了企业it的审核。
1.2 大白话总结
说白了,jar包就像一个"半成品菜",你得自己有锅(jdk)、会开火(命令行)才能做熟。而exe安装包就是"外卖成品",客户拿到手直接吃,什么都不用管。对于非技术用户来说,exe才是他们认知中"正常的软件"。
二、解决方案总览:四大主流方案一览

目前业界将java项目打包成exe主要有四大技术路线,各有适用场景:
| 方案 | 技术本质 | 代表工具 | 适用场景 |
|---|---|---|---|
| 官方原生打包 | jre裁剪+原生启动器+安装包 | jpackage(jdk 14+) | 企业级桌面应用,需要正规安装包 |
| 轻量包装器 | 生成exe启动壳,调用外部jre | launch4j、jsmooth | 小型工具,用户机器已有jre |
| 商业包装器 | 功能丰富的exe生成工具 | exe4j、install4j | 商业软件,需要高级安装功能 |
| aot原生编译 | 提前编译为机器码,无需jvm | graalvm native image | cli工具、云原生、追求极致启动速度 |
接下来我们逐一深入讲解每个方案的原理和实操。
三、是什么:java打包exe的底层原理

3.1 专业解释
java程序本质上是运行在jvm(java虚拟机)上的字节码,不能直接被 操作系统执行。将java项目打包成exe,本质上是解决三个问题:
- 启动器问题:需要一个原生的windows可执行文件(.exe)作为入口,它负责找到jvm并启动java程序。
- 运行时问题:需要一个jre(java运行时环境)来执行字节码。可以依赖用户机器上已有的jre,也可以自带一个裁剪后的jre。
- 分发问题:需要将启动器、jre、应用jar包以及相关资源打包成一个用户友好的安装包。
3.2 大白话类比
把java程序想象成一部电影。jar包就是电影的数字胶片,jvm就是放映机,exe就是电影院。用户拿到jar包,就像拿到一卷胶片,自己家里没有放映机(没装jdk)就看不了。而exe安装包就像把胶片、迷你放映机、播放说明一起打包成一个"家庭影院套装",用户买回来插上电就能看。
3.3 生活案例
你买了一个宜家的书架,包装盒里有木板(jar包)、螺丝(依赖库)和说明书(manifest.mf)。但是你需要自己有螺丝刀(jdk)才能组装。如果宜家把书架预装好,再配一个电动螺丝刀一起送货 上门,那就是exe安装包的体验——开箱即用。
四、为什么用:打成exe的核心价值
4.1 用户体验层面
- 零门槛运行:用户不需要安装jdk,不需要配置环境变量,不需要懂命令行。
- 专业感强:有安装向导、桌面快捷方式、开始菜单条目、卸载程序,和普通windows软件一模一样。
- 错误提示友好:启动器可以在找不到jre时给出友好的提示页面,而不是冷冰冰的命令行报错。
4.2 技术管控层面
- 版本锁定:自带裁剪后的jre,确保用户运行的jdk版本和开发时一致,避免版本兼容问题。
- jvm参数可控:可以在启动器中预设堆内存大小、gc策略等jvm参数,用户无法随意修改。
- 进程管理规范:exe有独立的进程名,在任务管理器中显示为你的应用名称,而不是千篇一律的"java.exe"。
4.3 企业合规层面
- 满足企业it分发要求:msi安装包支持组策略静默部署,符合企业软件管理规范。
- 数字签名支持:可以对exe进行代码签名,消除windows smartscreen警告,提升信任度。
- 自动更新集成:可以结合自动更新框架,实现软件的版本迭代管理。
五、演进史:java打包技术是如何发展到今天的

5.1 java 8及以前:javafxpackager时代
在java 8时代,oracle随jdk附带了一个叫做javafxpackager的工具(后来改名为javapackager),它可以将javafx应用打包成原生安装包。但这个工具有诸多限制:仅支持javafx应用、配置繁琐、生成的安装包体积大、跨平台支持差。大多数开发者还是依赖第三方工具如launch4j、exe4j来完成打包。
5.2 java 9:jlink与模块系统的革命
java 9引入了jpms(java平台模块系统),同时提供了jlink工具。jlink可以根据应用实际使用的模块,裁剪出一个最小化的jre运行时镜像。以前一个完整的jre动辄200mb以上,使用jlink裁剪后可以压缩到40mb甚至更小。这为java应用的轻量化分发奠定了基础。
5.3 java 14:jpackage首次亮相(孵化阶段)
jep 343提案引入了jpackage工具,作为java 14的孵化特性。jpackage整合了jlink的裁剪能力和原生打包能力,可以直接生成windows的exe/msi、macos的dmg/pkg、linux的deb/rpm安装包。这是oracle官方首次提供一个功能完整的打包工具,但由于是孵化阶段,api和行为还可能变化,企业用户持观望态度。
5.4 java 17:jpackage正式稳定
在java 17这个lts(长期支持)版本中,jpackage正式转正,成为jdk的标准工具。从此java开发者有了一个官方、稳定、跨平台的打包解决方案。配合jlink的模块裁剪能力,jpackage成为企业级java桌面应用打包的首选方案。
5.5 现代:graalvm native image的崛起
近年来,graalvm的native image技术逐渐成熟。它采用aot(ahead-of-time,提前编译)技术,在构建时就将java字节码编译成原生机器码,生成的exe完全不需要jvm即可运行,启动时间从秒级降到毫秒级,内存占用也大幅降低。虽然在反射、动态代理等场景需要额外配置,但对于cli工具、微服务等场景,native image正在成为新的趋势。
六、怎么用:三大方案实战教程
6.1 方案一:jpackage(官方推荐,企业首选)

jpackage是jdk 14+自带的官方打包工具,java 17及以上版本稳定可用。它能够自动调用jlink裁剪jre,并生成原生安装包。
6.1.1 环境准备
首先确认你的jdk版本:
java -version
输出应类似:
java version "17.0.9" lts
在windows上打包exe/msi还需要安装wix toolset 3.x(注意不是wix 4),下载地址:https://wixtoolset.org/releases/
安装后将wix的bin目录加入系统path,验证:
light.exe -help
6.1.2 第一步:构建可执行jar包
使用maven的maven-shade-plugin将所有依赖打入一个可执行jar:
<build>
<plugins>
<plugin>
<groupid>org.apache.maven.plugins</groupid>
<artifactid>maven-shade-plugin</artifactid>
<version>3.5.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.manifestresourcetransformer">
<mainclass>com.example.myapp.main</mainclass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>执行构建:
mvn clean package
构建完成后,在target/目录下会生成一个包含所有依赖的可执行jar包,例如myapp-1.0.jar。
6.1.3 第二步:使用jpackage生成exe安装包
创建一个input目录,将jar包放进去:
mkdir input copy target\myapp-1.0.jar input\
执行jpackage命令:
jpackage ^ --type exe ^ --input input ^ --dest output ^ --name myapp ^ --main-jar myapp-1.0.jar ^ --main-class com.example.myapp.main ^ --app-version 1.0.0 ^ --vendor "羽师科技" ^ --description "我的java桌面应用" ^ --icon app.ico ^ --java-options "-xmx512m" ^ --win-dir-chooser ^ --win-menu ^ --win-shortcut
参数说明:
| 参数 | 说明 |
|---|---|
--type exe | 生成exe安装包,也可以用msi |
--input | 包含jar包的输入目录 |
--dest | 输出目录 |
--name | 应用名称 |
--main-jar | 主jar包文件名 |
--main-class | 主类全限定名 |
--app-version | 应用版本号 |
--icon | 应用图标(.ico格式) |
--java-options | 传递给jvm的参数 |
--win-dir-chooser | 允许用户选择安装目录 |
--win-menu | 添加到开始菜单 |
--win-shortcut | 创建桌面快捷方式 |
执行完成后,在output/目录下会生成myapp-1.0.0.exe安装包。用户双击即可安装,安装后桌面和开始菜单都会出现快捷方式。
6.1.4 第三步:生成免安装的应用镜像
如果你不想做安装包,只想生成一个解压即用的文件夹,可以使用--type app-image:
jpackage ^ --type app-image ^ --input input ^ --dest output ^ --name myapp ^ --main-jar myapp-1.0.jar ^ --main-class com.example.myapp.main ^ --icon app.ico
这会在output/myapp/目录下生成一个完整的应用文件夹,包含裁剪后的jre和启动器,用户直接双击myapp.exe即可运行,无需安装。
6.1.5 maven插件集成(企业级用法)
在企业项目中,可以使用org.panteleyev:jpackage-maven-plugin将jpackage集成到maven构建流程中:
<plugin>
<groupid>org.panteleyev</groupid>
<artifactid>jpackage-maven-plugin</artifactid>
<version>1.6.0</version>
<configuration>
<name>myapp</name>
<appversion>${project.version}</appversion>
<vendor>羽师科技</vendor>
<destination>target/dist</destination>
<input>target</input>
<mainjar>${project.build.finalname}.jar</mainjar>
<mainclass>com.example.myapp.main</mainclass>
<javaoptions>
<option>-xmx512m</option>
</javaoptions>
<type>exe</type>
<winmenu>true</winmenu>
<winshortcut>true</winshortcut>
<windirchooser>true</windirchooser>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jpackage</goal>
</goals>
</execution>
</executions>
</plugin>这样执行mvn clean package就会自动完成编译、打jar、生成exe的全过程。
6.2 方案二:launch4j(轻量快速,适合小工具)

launch4j是一个开源的轻量级java exe包装器,它生成的exe本质上是一个启动器壳,负责查找jre并启动jar包。launch4j本身不打包jre,但可以指定旁置的jre目录。
6.2.1 下载安装
从官网下载:https://launch4j.sourceforge.net/
解压后即可使用,提供gui界面和命令行两种模式。
6.2.2 编写配置文件
创建launch4j-config.xml:
<?xml version="1.0" encoding="utf-8"?>
<launch4jconfig>
<dontwrapjar>false</dontwrapjar>
<headertype>gui</headertype>
<jar>target\myapp-1.0.jar</jar>
<outfile>output\myapp.exe</outfile>
<errtitle>myapp 启动错误</errtitle>
<cmdline></cmdline>
<chdir>.</chdir>
<priority>normal</priority>
<downloadurl>https://www.java.com/download/</downloadurl>
<supporturl></supporturl>
<stayalive>false</stayalive>
<restartoncrash>false</restartoncrash>
<manifest></manifest>
<icon>app.ico</icon>
<classpath>
<mainclass>com.example.myapp.main</mainclass>
</classpath>
<jre>
<path>jre</path>
<bundledjre64bit>true</bundledjre64bit>
<bundledjreasfallback>false</bundledjreasfallback>
<minversion>17</minversion>
<maxversion></maxversion>
<jdkpreference>preferjre</jdkpreference>
<runtimebits>64</runtimebits>
</jre>
<heapsize>512</heapsize>
<heapmaxsize>1024</heapmaxsize>
<versioninfo>
<fileversion>1.0.0.0</fileversion>
<txtfileversion>1.0.0</txtfileversion>
<filedescription>我的java桌面应用</filedescription>
<copyright>copyright 2026 羽师科技</copyright>
<productversion>1.0.0.0</productversion>
<txtproductversion>1.0.0</txtproductversion>
<productname>myapp</productname>
<companyname>羽师科技</companyname>
</versioninfo>
</launch4jconfig>关键配置说明:
<headertype>gui</headertype>:gui应用,不显示黑窗口;如果是命令行工具用console。<path>jre</path>:优先使用exe同级目录下的jre文件夹,实现自带jre。<minversion>17</minversion>:最低jre版本要求。<downloadurl>:找不到合适jre时跳转到的下载页面。
6.2.3 执行打包
命令行方式:
launch4jc.exe launch4j-config.xml
或者打开launch4j的gui界面,导入配置文件,点击"build wrapper"按钮。
6.2.4 自带jre实现免安装运行
使用jlink裁剪一个最小jre:
jlink ^ --add-modules java.base,java.desktop,java.logging ^ --output jre ^ --strip-debug ^ --compress=2 ^ --no-header-files ^ --no-man-pages
将生成的jre文件夹和myapp.exe放在同一目录下,整个文件夹拷贝到任何windows机器上都能直接运行,无需安装jdk。
6.3 方案三:graalvm native image(极致性能,无需jvm)

graalvm native image采用aot编译技术,在构建时将java字节码直接编译成原生机器码,生成的exe完全不依赖jvm,启动速度极快,内存占用极低。
6.3.1 环境准备
下载并安装graalvm(建议graalvm for jdk 17或21):https://www.graalvm.org/downloads/
设置java_home指向graalvm安装目录,并将bin加入path。验证:
java -version native-image --version
在windows上还需要安装visual studio build tools,包含windows sdk和msvc编译器。安装时勾选"使用c++的桌面开发"工作负载。
6.3.2 简单示例:hello world
编写helloworld.java:
public class helloworld {
public static void main(string[] args) {
system.out.println("hello, native image!");
}
}编译并生成原生exe:
javac helloworld.java native-image helloworld
这会生成helloworld.exe,直接运行:
helloworld.exe
输出:
hello, native image!
启动时间通常在10毫秒以内,内存占用只有几mb。
6.3.3 从jar包生成原生exe
对于已有jar包的项目:
native-image -jar target/myapp-1.0.jar myapp
6.3.4 maven插件集成
在pom.xml中添加graalvm原生镜像插件:
<profiles>
<profile>
<id>native</id>
<build>
<plugins>
<plugin>
<groupid>org.graalvm.buildtools</groupid>
<artifactid>native-maven-plugin</artifactid>
<version>0.10.1</version>
<extensions>true</extensions>
<configuration>
<mainclass>com.example.myapp.main</mainclass>
<buildargs>
<buildarg>--no-fallback</buildarg>
<buildarg>--initialize-at-build-time</buildarg>
</buildargs>
</configuration>
<executions>
<execution>
<id>build-native</id>
<phase>package</phase>
<goals>
<goal>compile-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>执行原生编译:
mvn clean package -pnative
6.3.5 反射配置(重要)
native image在编译时会做静态可达性分析,无法自动识别反射调用。如果你的代码使用了反射、动态代理、jni等特性,需要提供配置文件。
在src/main/resources/meta-inf/native-image/目录下创建reflect-config.json:
[
{
"name": "com.example.myapp.model.user",
"alldeclaredconstructors": true,
"allpublicconstructors": true,
"alldeclaredmethods": true,
"allpublicmethods": true,
"alldeclaredfields": true,
"allpublicfields": true
}
]也可以使用java agent自动收集配置:
java -agentlib:native-image-agent=config-output-dir=src/main/resources/meta-inf/native-image -jar target/myapp-1.0.jar
运行程序覆盖所有功能路径后,agent会自动生成所需的配置文件。
七、竞品对比:四大方案优劣势深度对比
| 对比维度 | jpackage | launch4j | exe4j | graalvm native image |
|---|---|---|---|---|
| 是否官方 | 是(jdk 14+自带) | 否(开源第三方) | 否(商业软件) | 否(oracle graalvm) |
| 技术本质 | jre裁剪+原生启动器+安装包 | 轻量exe启动壳 | 功能丰富的exe启动壳 | aot编译为原生机器码 |
| 是否需要jvm | 是(自带裁剪jre) | 是(依赖外部或旁置jre) | 是(依赖外部或旁置jre) | 否(完全原生) |
| 启动速度 | 正常(jvm启动,约1-3秒) | 正常(jvm启动) | 正常(jvm启动) | 极快(毫秒级) |
| 内存占用 | 标准jvm(约100-300mb) | 标准jvm | 标准jvm | 极低(约10-50mb) |
| 安装包体积 | 中等(约40-80mb,含裁剪jre) | 极小(exe仅几十kb,不含jre) | 小(exe约1-2mb,不含jre) | 中等(约20-60mb) |
| 反射/动态代理支持 | 完全支持 | 完全支持 | 完全支持 | 需要额外配置 |
| 跨平台打包 | 否(必须在目标os上构建) | 是(可在linux/macos生成windows exe) | 否(仅windows) | 否(必须在目标os上构建) |
| 生成安装包 | 支持(exe/msi/dmg/deb/rpm) | 不支持(仅生成exe) | 不支持(仅生成exe,需配合install4j) | 不支持(仅生成exe) |
| 学习曲线 | 中等 | 简单 | 简单(gui向导) | 较陡 |
| 商业授权 | 免费(gplv2 + ce) | 免费(mit/bsd) | 收费(约$199起) | 免费(社区版)/企业版收费 |
| 最佳适用场景 | 企业级桌面应用、javafx/swing | 小型工具、用户已有jre | 商业软件、需要高级功能 | cli工具、微服务、云原生 |
7.1 选型建议
- 如果你做企业级桌面应用:选jpackage,官方稳定,安装包规范,自动裁剪jre。
- 如果你做小工具且用户机器有jre:选launch4j,轻量快速,配置简单。
- 如果你做商业软件且预算充足:exe4j/install4j组合,功能最丰富,支持自动更新。
- 如果你做cli工具或追求极致性能:选graalvm native image,毫秒启动,低内存。
八、企业项目实战:ci/cd自动化打包流水线

在企业项目中,手动打包exe是不可接受的。需要将打包流程集成到ci/cd流水线中,实现代码提交后自动构建、自动测试、自动打包、自动发布。
8.1 gitlab ci配置示例
stages:
- build
- test
- package
- release
variables:
maven_opts: "-dmaven.repo.local=.m2/repository"
# 构建jar包
build_jar:
stage: build
image: maven:3.9-eclipse-temurin-17
script:
- mvn clean package -dskiptests
artifacts:
paths:
- target/*.jar
expire_in: 1 week
# 运行单元测试
run_tests:
stage: test
image: maven:3.9-eclipse-temurin-17
script:
- mvn test
artifacts:
reports:
junit: target/surefire-reports/*.xml
# windows平台打包exe
package_windows_exe:
stage: package
tags:
- windows
script:
- choco install wixtoolset -y
- mkdir input
- copy target\myapp-1.0.jar input\
- jpackage --type exe --input input --dest dist --name myapp --main-jar myapp-1.0.jar --main-class com.example.myapp.main --app-version %ci_commit_tag% --vendor "羽师科技" --icon app.ico --win-dir-chooser --win-menu --win-shortcut
artifacts:
paths:
- dist/*.exe
expire_in: 1 month
only:
- tags
# macos平台打包dmg
package_macos_dmg:
stage: package
tags:
- macos
script:
- mkdir input
- cp target/myapp-1.0.jar input/
- jpackage --type dmg --input input --dest dist --name myapp --main-jar myapp-1.0.jar --main-class com.example.myapp.main --app-version $ci_commit_tag --vendor "羽师科技" --icon app.icns
artifacts:
paths:
- dist/*.dmg
expire_in: 1 month
only:
- tags
# 发布到制品库
release:
stage: release
image: curlimages/curl:latest
script:
- curl -u $nexus_user:$nexus_pass --upload-file dist/myapp-$ci_commit_tag.exe https://nexus.example.com/repository/releases/com/example/myapp/$ci_commit_tag/myapp-$ci_commit_tag.exe
only:
- tags8.2 jenkins pipeline配置示例
pipeline {
agent any
tools {
jdk 'jdk-17'
maven 'maven-3.9'
}
environment {
app_name = 'myapp'
app_version = "${env.build_number}"
}
stages {
stage('checkout') {
steps {
git branch: 'main', url: 'https://git.example.com/myapp.git'
}
}
stage('build & test') {
steps {
bat 'mvn clean package'
}
post {
always {
junit 'target/surefire-reports/*.xml'
}
}
}
stage('package exe') {
agent { label 'windows' }
steps {
bat '''
mkdir input
copy target\\myapp-1.0.jar input\\
jpackage --type exe ^
--input input ^
--dest dist ^
--name %app_name% ^
--main-jar myapp-1.0.jar ^
--main-class com.example.myapp.main ^
--app-version %app_version% ^
--vendor "羽师科技" ^
--icon app.ico ^
--win-dir-chooser ^
--win-menu ^
--win-shortcut
'''
}
post {
success {
archiveartifacts artifacts: 'dist/*.exe', fingerprint: true
}
}
}
stage('deploy') {
steps {
echo '部署到下载服务器...'
}
}
}
}8.3 企业级最佳实践
- 多平台矩阵构建:使用不同操作系统的runner,同时构建windows exe、macos dmg、linux deb。
- 版本号统一管理:从git tag或ci构建号获取版本号,确保安装包版本和代码版本一致。
- 代码签名:在打包后对exe进行数字签名,消除windows smartscreen警告。
- 自动更新集成:配合
update4j或sparkle等框架,实现软件自动更新。 - 制品归档:将每个版本的安装包归档到nexus或artifactory,支持历史版本回溯。
- 灰度发布:先发布给内部测试用户,验证无误后再全量发布。
九、常用场景与实战技巧
9.1 场景一:swing桌面应用打包
swing应用打包时需要注意中文乱码问题,在jpackage的--java-options中添加文件编码参数:
jpackage ^ --type exe ^ --input input ^ --dest output ^ --name myswingapp ^ --main-jar myapp.jar ^ --main-class com.example.main ^ --java-options "-dfile.encoding=utf-8 -dsun.jnu.encoding=utf-8 -xmx512m"
9.2 场景二:javafx应用打包
javafx从jdk 11开始被移除,需要单独引入依赖。使用jpackage时需要在--java-options中指定javafx模块路径:
jpackage ^ --type exe ^ --input input ^ --dest output ^ --name myjavafxapp ^ --main-jar myapp.jar ^ --main-class com.example.main ^ --java-options "--module-path lib --add-modules javafx.controls,javafx.fxml -xmx512m"
也可以使用javafx-maven-plugin的jlink目标先生成包含javafx模块的运行时镜像,再用jpackage打包。
9.3 场景三:spring boot命令行工具打包
spring boot应用可以使用graalvm native image打包成原生exe,实现毫秒启动:
<plugin>
<groupid>org.graalvm.buildtools</groupid>
<artifactid>native-maven-plugin</artifactid>
</plugin>执行:
mvn -pnative native:compile
生成的exe在target/目录下,直接运行即可,无需jvm。
9.4 场景四:带外部配置文件的应用
如果应用需要读取外部配置文件(如config.properties),在jpackage中使用--input目录将配置文件一起打包:
mkdir input copy target\myapp.jar input\ copy config.properties input\ jpackage --type app-image --input input --dest output --name myapp --main-jar myapp.jar
安装后配置文件会位于应用目录下,用户可以直接编辑。
9.5 常用技巧
技巧一:减小安装包体积
使用jlink的--compress=2和--strip-debug选项,可以将jre体积进一步压缩:
jlink --add-modules java.base,java.desktop --output jre --strip-debug --compress=2 --no-header-files --no-man-pages
技巧二:自定义安装界面
jpackage支持通过--resource-dir自定义安装界面资源,包括欢迎页、许可协议等:
jpackage --type exe --input input --dest output --name myapp --main-jar myapp.jar --resource-dir custom-resources
技巧三:设置jvm堆内存
在jpackage中通过--java-options预设jvm参数:
jpackage --type exe --input input --dest output --name myapp --main-jar myapp.jar --java-options "-xms256m -xmx1024m -xx:+useg1gc"
十、面试官高频面试题
面试题一:java项目打包成exe有哪些方案?各自的优缺点是什么?
参考答案:
主要有四大方案:
- jpackage:jdk 14+官方工具,java 17稳定。优点是官方支持、自动裁剪jre、生成原生安装包(exe/msi/dmg/deb/rpm)、跨平台。缺点是必须在目标操作系统上构建,不能交叉编译。适合企业级桌面应用。
- launch4j:开源轻量包装器。优点是体积小、配置简单、可以在linux/macos上交叉生成windows exe。缺点是本身不打包jre,需要用户机器有jre或旁置jre文件夹,不生成安装包。适合小型工具。
- exe4j/install4j:商业工具。优点是功能丰富、gui向导、支持自动更新。缺点是收费,仅支持windows。适合商业软件。
- graalvm native image:aot编译。优点是生成真正的原生二进制,无需jvm,毫秒级启动,内存占用极低。缺点是反射、动态代理等需要额外配置,构建时间长,对某些框架支持不完善。适合cli工具和云原生场景。
面试题二:jpackage和jlink有什么区别和联系?
参考答案:
- jlink是java 9引入的工具,用于根据应用实际使用的模块,裁剪出一个最小化的jre运行时镜像。它只负责生成运行时,不负责生成启动器和安装包。
- jpackage是java 14引入的工具,用于将java应用打包成原生安装包。jpackage内部会自动调用jlink来裁剪jre,然后将裁剪后的jre、应用jar、原生启动器一起打包成安装包。
简单说,jlink是jpackage的底层依赖之一,jpackage = jlink + 原生启动器 + 安装包生成。
面试题三:为什么jpackage不能在linux上打包windows的exe?
参考答案:
jpackage生成的是平台原生的安装包,它依赖目标平台的原生工具链。在windows上生成exe/msi需要调用wix toolset,在macos上生成dmg需要调用系统的磁盘映像工具,在linux上生成deb/rpm需要调用dpkg/rpm工具。这些工具都是平台相关的,无法跨平台调用。因此jpackage必须在目标操作系统上运行,不能交叉编译。如果需要多平台打包,需要使用多平台的ci/cd构建矩阵。
面试题四:graalvm native image的工作原理是什么?有什么限制?
参考答案:
工作原理: native image采用aot(ahead-of-time)提前编译技术。在构建时,它通过静态分析扫描应用的字节码,找出所有可达的代码路径,然后将这些字节码直接编译成目标平台的原生机器码。同时它会在构建时初始化一部分类,将堆对象序列化到镜像中。最终生成的是一个完全独立的原生可执行文件,不需要jvm即可运行。
主要限制:
- 反射支持不完善:编译时无法预知动态加载的类,需要通过
reflect-config.json手动配置。 - 动态代理需要配置:需要通过
proxy-config.json声明动态代理接口。 - jni需要配置:需要通过
jni-config.json配置。 - 类加载器限制:不支持运行时创建自定义类加载器加载新类。
- 构建时间长:aot编译比普通javac编译慢得多,大项目可能需要几十分钟。
- 峰值性能可能低于jit:对于长时间运行的服务,jvm的jit编译器经过运行时优化后峰值性能可能更高。
面试题五:如何减小java应用打包后的体积?
参考答案:
- 使用jlink裁剪jre:只包含应用实际需要的模块,而不是完整的jdk。一个最小的java.base模块jre只有约20mb。
- 使用
--strip-debug:移除调试信息,减小体积。 - 使用
--compress=2:对jre进行zip压缩。 - 使用
--no-header-files和--no-man-pages:移除头文件和man手册。 - 排除未使用的依赖:在maven/gradle中排查并排除未使用的依赖库。
- 使用proguard混淆优化:对字节码进行优化和混淆,移除未使用的代码。
- 使用graalvm native image:aot编译后的原生二进制体积通常比jre+jar小。
面试题六:launch4j生成的exe和jpackage生成的exe有什么本质区别?
参考答案:
launch4j生成的exe只是一个轻量级的启动器壳(通常只有几十kb),它本身不包含jre,运行时需要在系统中查找或在指定路径查找jre,然后调用jvm来执行jar包。jar包可以被打包进exe内部,也可以放在外部。
jpackage生成的exe是一个完整的安装包,安装后会在目标机器上部署一个完整的应用目录,包含:原生启动器exe、裁剪后的jre运行时、应用jar包和资源文件。用户机器上完全不需要安装jdk。
简单说,launch4j的exe是"借鸡生蛋"(用系统的jre),jpackage的exe是"自带厨房"(自带裁剪jre)。
总结
java项目打包成exe,从早期的第三方工具群雄割据,到如今jpackage官方方案一统江湖,再到graalvm native image开辟原生编译新赛道,技术生态已经非常成熟。选择哪种方案,取决于你的应用场景、用户群体和性能要求。对于大多数企业级桌面应用,jpackage是目前最稳妥、最规范的选择;对于追求极致性能的cli工具,graalvm native image值得投入研究。
以上就是java项目打包成exe的4大主流方法完整指南的详细内容,更多关于java打包成exe的资料请关注代码网其它相关文章!
发表评论