构建可执行JAR需严格遵循规范:MANIFEST.MF必须首个条目、含Main-Class;路径用正斜杠、不以/开头;每个JarEntry须设时间戳并调用closeEntry();推荐try-with-resources管理流。

用 JarOutputStream 构建可执行 JAR 包,关键不在“压缩”,而在严格遵循 JAR 规范——特别是条目顺序、路径格式、时间戳和清单(META-INF/MANIFEST.MF)的写入时机与内容。稍有偏差,就可能造成无法启动、解压失败或类加载异常。
MANIFEST.MF 必须是第一个条目且含 Main-Class
Java 运行时要求 META-INF/MANIFEST.MF 必须作为归档中的首个条目;否则某些环境(如旧版 ClassLoader 或 Java Web Start)会忽略它。同时,若要支持 java -jar 启动,必须在 manifest 主属性中明确设置 Main-Class:
- 先创建
Manifest实例,调用manifest.getMainAttributes().put(Attributes.Name.MAIN_CLASS, "com.example.Main") - 再构造
JarEntry("META-INF/MANIFEST.MF") - 紧接着调用
jarOut.putNextEntry(entry) - 然后用
manifest.write(jarOut)写入内容 - 最后必须
jarOut.closeEntry()
所有 JarEntry 路径必须规范
JAR 内部路径不是文件系统路径,而是 ZIP 标准路径,有固定规则:
- 一律使用正斜杠
/作分隔符,不能用反斜杠\ - 路径不能以
/开头(例如/com/example/MyClass.class是错的) - 类文件路径应为相对形式,如
"com/example/MyClass.class" - 目录条目(如空包)可显式添加,结尾加
/(如"com/example/"),但非强制;JAR 工具能自动推导
每个条目必须显式设置时间戳并配对 closeEntry
JarEntry 默认时间戳在部分 JDK 版本中为 1980-01-01 00:00:00,会导致解压工具报错或校验失败:
- 务必调用
entry.setTime(System.currentTimeMillis())或设为确定值(如构建时统一用1717027200000L对应 2024-05-30) - 每次
putNextEntry()后,必须立即调用closeEntry(),否则后续写入可能被截断 - 推荐全程使用
try-with-resources管理JarOutputStream,避免手动flush()或close()遗漏
资源写入需注意流管理与字节处理
向 JAR 中写入 class、配置或其他二进制资源时,要确保数据完整、不混杂:
- 每个资源对应一个独立
JarEntry,不可复用 entry 对象 - 用
BufferedInputStream读取源文件,配合固定缓冲区(如byte[8192])循环read()+write() - 不要依赖
available()判断长度;以read()返回值是否为-1为准 - 外部 JAR 包若需嵌入(非依赖路径),应作为普通资源写入,路径按需指定(如
"lib/xxx.jar"),但注意:标准java -jar不会自动加载子 JAR,需自定义类加载器或改用 Spring Boot / Maven Shade 等方案











