☰
exe4j 打包 Java 工程实战:让 jar 脱离 JDK 运行与 JRE 捆绑
2026/10/1 14:55:42 网站建设 项目流程

简介:这份资源面向需要将 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」。合理顺序是:

  1. 先找发布目录下的jre子目录(捆绑的)
  2. 找不到再找系统注册表里的 JRE
  3. 都没有就弹提示

这样做的价值是:目标机装了 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 的配置是二进制工程文件,改了什么有时候自己都记不清,只有干净环境能给出诚实答案。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询