简介:这份资源面向需要将 Java 工程打包为脱离 JDK 环境运行可执行文件的开发者,尤其适合刚接触 exe4j 工具、希望快速完成 jar 转 exe 并排查运行异常的中级学习者。内容围绕 exe4j 的完整使用流程展开,涵盖从 Eclipse 导出 jar、配置 JAR EXE mode、添加依赖 jar 包,到设置 Java 版本与精简版 JRE 搜索路径等关键环节,并针对生成的可执行文件运行时一闪而过的问题给出在 main 方法末尾加入 Thread.sleep 的实用排错思路。资源包共 1 个 docx 文档,约 925KB,以图文步骤形式记录操作要点,便于对照实践。目前已有 1253 人学习下载,适合需要将 Java 桌面程序交付到无 JDK 机器上运行的开发者参考,可帮助读者少走弯路,快速掌握 exe4j 打包与常见异常处理。
1. 从一台没装 JDK 的 Windows 机器说起:exe4j 打包 Java 工程到底解决了什么
同事把一台刚重装系统的 Win11 笔记本推过来,桌面干干净净,连 JDK 都没装,只丢下一句「把这个 Java 工程跑起来」。你手里只有一个 jar 包和一堆 lib 依赖,双击 jar 没反应,命令行敲java -jar直接提示找不到命令。这种场景下,exe4j 就是那个把「Java 工程」翻译成「Windows 原生 exe」的中间人:它不重新编译你的代码,而是生成一个 exe 启动器,把 JRE 和你的 jar 一起打包或指向,让目标机器不需要预装 JDK 也能双击运行。适合谁?做内部工具、桌面客户端、给非技术同事交付 Java 程序的人。核心词 exe4j、java、jdk、jar、可执行文件,这一章先把边界划清楚,后面再动手。
2. exe4j 的打包模型:它凭什么让 jar 脱离 JDK 运行
2.1 exe4j 不是编译器,是启动器生成器
很多人第一次用 exe4j 会误以为它把 Java 字节码编译成了机器码,其实不是。exe4j 生成的是一个 Windows 原生 exe,这个 exe 内部做三件事:定位一个可用的 JRE、拼出java -jar或java -cp的启动参数、把控制台或 GUI 交给你的主类。真正跑代码的还是 JVM,只是 JVM 由 exe 帮你找、帮你带。
这就解释了为什么 exe4j 打包出来的程序,目标机器上要么有 JRE,要么你把 JRE 一起塞进发布目录。它解决的是「启动入口」和「运行环境定位」问题,不是「消除 JVM」问题。理解这一点,后面所有参数配置都不会跑偏。
常见做法是两种分发形态:
| 分发形态 | 目标机器要求 | 体积 | 适用场景 |
|---|---|---|---|
| 仅 exe + jar,依赖目标机 JRE | 需预装 JRE/JDK | 小 | 内网机器统一装过 JDK |
| exe + jar + 捆绑 JRE | 无需任何 Java 环境 | 大(几十到上百 MB) | 交付给非技术用户、干净系统 |
选哪种,取决于你的交付对象。给测试同事的内网工具,第一种就够;给客户或完全不懂技术的用户,必须第二种。
2.2 打包前必须理清的工程结构
exe4j 对输入的要求很朴素:一个主类、一组 classpath 条目。所以打包前先把工程整理成「可独立运行」的状态。以 Maven 工程为例,先确认能这样跑起来:
# 在工程根目录执行,确认依赖都齐、主类能启动 mvn clean package java -jar target/myapp-1.0.0.jar如果这一步报NoClassDefFoundError,说明依赖没打进去,先解决依赖问题再谈 exe4j。常见做法是用 maven-shade-plugin 或 maven-assembly-plugin 打一个包含全部依赖的 fat jar:
<!-- pom.xml 片段:用 shade 插件打可执行 fat jar --> <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> <!-- 指定入口主类,否则 java -jar 找不到 Main-Class --> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>com.example.Main</mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin>逻辑说明:shade 插件把所有依赖解压合并进一个 jar,并写入META-INF/MANIFEST.MF的Main-Class。参数上,mainClass必须写全限定类名;如果工程里有多个 main 方法,这里只能选一个作为入口。打完包后java -jar能跑通,exe4j 的输入才算干净。
提示:如果工程依赖里有 Spring Boot,直接用 spring-boot-maven-plugin 打出的 jar 自带启动器,exe4j 里 classpath 配置方式和普通 fat jar 略有不同,建议先确认
java -jar能跑再进 exe4j。
2.3 用 exe4j 向导生成第一个 exe 的关键几步
打开 exe4j,新建配置,向导分几个阶段,真正决定成败的是这几处:
第一,Distribution 选「JAR in EXE mode」还是「Regular mode」。桌面工具一般选 JAR in EXE mode,exe 只做启动器,jar 单独放。
第二,Executable 设置里填输出 exe 的路径和名称,比如dist/MyApp.exe。
第三,Java invocation 里配置 classpath 和主类。classpath 把 fat jar 加进去,主类填com.example.Main。
第四,JRE 配置。如果目标机不装 JDK,这里选「Bundled JRE」并指定一个 JRE 目录,exe4j 会把它一起纳入发布结构。JRE 版本要和编译版本匹配,用 JDK 17 编译的 class 文件,JRE 不能低于 17。
第五,Splash screen 和版本信息按需填,不影响运行。
生成后目录结构大致是:
dist/ MyApp.exe app/myapp-1.0.0.jar jre/ <- 捆绑模式才有双击 exe,exe4j 生成的启动器会按配置找到 jre 和 jar,拼出启动命令。到这一步,一台没装 JDK 的机器就能跑起来了。
3. 把 JRE 一起带走:捆绑运行时的配置与体积权衡
3.1 用 jlink 裁剪一个最小 JRE
直接拷一个完整 JDK 目录进去,动辄两三百 MB,交付体验很差。JDK 9 以后自带 jlink,可以按模块裁一个只含所需模块的运行时。先看工程用到哪些模块,最省事的做法是先跑一遍再补:
# 生成一个只含基础模块的运行时,输出到 custom-jre 目录 jlink --add-modules java.base,java.desktop,java.logging,java.sql \ --output custom-jre \ --strip-debug --no-header-files --no-man-pages --compress=2逻辑说明:--add-modules列出运行时保留的模块,java.base必留,GUI 程序加java.desktop,用日志加java.logging,连数据库加java.sql。--strip-debug去掉调试信息,--no-header-files和--no-man-pages去掉头文件和手册,--compress=2压缩。参数上,模块列少了运行时报ClassNotFoundException,列多了体积回升,需要按实际依赖调。
裁完的 custom-jre 通常能压到 40 到 60 MB,比完整 JDK 小一大截。把这个目录作为 exe4j 的 Bundled JRE 指向目标。
3.2 exe4j 里 JRE 搜索顺序怎么设
exe4j 的 JRE 配置页有一个搜索顺序列表,可以同时配「捆绑 JRE」和「系统已装 JRE」。合理顺序是:
- 先找发布目录下的
jre子目录(捆绑的) - 找不到再找系统注册表里的 JRE
- 都没有就弹提示
这样做的价值是:目标机装了 JRE 就用系统的,省体积;没装就用捆绑的,保证能跑。配置时把捆绑路径写成相对路径jre,不要写死绝对路径,否则换台机器就翻车。
3.3 版本不匹配是最高频的翻车点
血泪经验:用 JDK 17 编译,捆绑了一个 JRE 8,exe 双击闪退,日志里一行UnsupportedClassVersionError。Java 的 class 文件版本和 JRE 版本强绑定,编译版本高于运行版本一定失败。排查方法是在目标机命令行跑jre/bin/java -version,和编译时的javac -version对一下。
另一个坑是 32 位和 64 位。exe4j 生成的 exe 位数取决于你用的 exe4j 版本和捆绑 JRE 位数,32 位 exe 配 64 位 JRE 直接起不来。统一用 64 位,除非目标机确实是老 32 位系统。
4. 依赖、路径与日志:exe4j 打包最容易踩的五个坑
4.1 现象:双击 exe 一闪而过,什么都没发生
原因:主类抛异常但控制台被关掉了,看不到错误。exe4j 默认可能不保留控制台。
解决:在 exe4j 的 Executable 配置里勾选「Console application」或把 stderr 重定向到文件。更稳的做法是在主类最外层包一层 try-catch,把异常写进日志文件:
public static void main(String[] args) { try { launch(args); } catch (Throwable t) { // 把启动期异常落盘,方便在无控制台的 exe 里排查 try (PrintWriter pw = new PrintWriter(new FileWriter("startup-error.log", true))) { t.printStackTrace(pw); } catch (IOException ignored) {} throw t; } }逻辑说明:exe 双击运行时没有终端,异常栈默认丢失。把 Throwable 捕获后写入固定文件,是排查启动失败最直接的手段。参数上,日志路径用相对路径,落在 exe 同目录,方便用户回传。
4.2 现象:提示找不到某个 jar 或 class
原因:classpath 配置漏了依赖,或者用了相对路径但工作目录不对。
解决:exe4j 的 classpath 条目尽量用 fat jar 一条搞定,避免列一长串 lib。如果必须用 lib 目录,路径用 exe4j 提供的变量而不是硬编码。工作目录在 exe4j 里可以显式设置,建议设成 exe 所在目录。
4.3 现象:程序能跑但读不到配置文件
原因:代码里用new File("config.properties")这种相对路径,工作目录变了就找不到。
解决:配置文件要么打进 jar 用getResourceAsStream读,要么在 exe4j 里显式设置工作目录,要么用System.getProperty("user.dir")拼绝对路径。三种方式选一种,别混用。
4.4 现象:捆绑 JRE 后体积爆炸
原因:直接拷了完整 JDK,或者 jlink 模块列太多。
解决:用 jlink 裁剪,只留必要模块;确认拷的是 JRE 不是 JDK;--compress=2打开。一般桌面工具能压到 50 MB 以内。
4.5 现象:杀毒软件误报 exe
原因:exe4j 生成的启动器结构容易被启发式引擎盯上。
解决:给 exe 做代码签名是最正规的路子;内部工具可以加白名单;实在不行换 launch4j 试试,它的启动器结构不同,误报率有时更低。这不是 exe4j 的 bug,是所有自解压式启动器的通病。
5. 从能跑到好用:签名、静默启动与交付前自检
5.1 给 exe 加版本信息和图标
exe4j 的 Executable 配置页可以填版本号、公司名、产品名,并指定一个.ico图标。别小看这一步,交付给用户的 exe 如果是个默认白板图标,信任度直接打折。图标用 256x256 的 ico,版本号和你工程版本对齐,属性面板里看起来才像个正经软件。
5.2 静默启动与单实例控制
桌面工具经常需要「双击就开,不弹黑框」。在 exe4j 里选 GUI application 模式,控制台不显示。如果程序不允许开多个实例,可以在主类里加单实例锁:
// 用文件锁实现单实例,避免用户重复双击开多个窗口 FileLock lock; try { FileChannel ch = FileChannel.open(Paths.get("app.lock"), StandardOpenOption.CREATE, StandardOpenOption.WRITE); lock = ch.tryLock(); if (lock == null) { System.exit(0); // 已有实例在跑,直接退出 } } catch (IOException e) { // 锁文件异常时放行,不阻塞正常启动 }逻辑说明:tryLock返回 null 表示锁被占用,说明已有实例。参数上,锁文件放 exe 同目录,程序退出时 JVM 会释放文件锁,不需要手动清理。注意这个方案在多个用户同时登录同一台机器时行为不同,按需调整。
5.3 交付前的自检清单
打包完别急着发,找一台干净机器(或新建一个没装 JDK 的虚拟机)走一遍:
| 检查项 | 通过标准 |
|---|---|
| 双击 exe | 正常启动,无闪退 |
| 无 JDK 环境 | 能跑,不提示找不到 java |
| 配置文件读取 | 修改配置后行为变化 |
| 日志输出 | 异常时 startup-error.log 有内容 |
| 卸载/删除 | 删目录后无残留注册表项 |
这套自检跑通,基本可以交付。我自己的习惯是每次改完 exe4j 配置都重新在一台干净虚拟机上验一遍,因为 exe4j 的配置是二进制工程文件,改了什么有时候自己都记不清,只有干净环境能给出诚实答案。希望帮到你。
本文还有配套的精品资源,点击获取