exe4j 打包 Java 工程实战:让 jar 脱离 JDK 运行与 JRE 捆绑

发布时间:2026/10/1 14:55:44
exe4j 打包 Java 工程实战:让 jar 脱离 JDK 运行与 JRE 捆绑
简介这份资源面向需要将 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小内网机器统一装过 JDKexe 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 groupIdorg.apache.maven.plugins/groupId artifactIdmaven-shade-plugin/artifactId version3.5.1/version executions execution phasepackage/phase goalsgoalshade/goal/goals configuration transformers !-- 指定入口主类否则 java -jar 找不到 Main-Class -- transformer implementationorg.apache.maven.plugins.shade.resource.ManifestResourceTransformer mainClasscom.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 modeexe 只做启动器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/ - 捆绑模式才有双击 exeexe4j 生成的启动器会按配置找到 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 --compress2逻辑说明--add-modules列出运行时保留的模块java.base必留GUI 程序加java.desktop用日志加java.logging连数据库加java.sql。--strip-debug去掉调试信息--no-header-files和--no-man-pages去掉头文件和手册--compress2压缩。参数上模块列少了运行时报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 8exe 双击闪退日志里一行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--compress2打开。一般桌面工具能压到 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 的配置是二进制工程文件改了什么有时候自己都记不清只有干净环境能给出诚实答案。希望帮到你。本文还有配套的精品资源点击获取