使用javac编译Java文件;创建包含Main-Class的manifest.mf;通过jar cvfm命令打包,指定清单文件及编译后的class文件路径,示例: ,
jar cvfm app.jar manifest.mf -C classes/ .
在Java开发中,将编译好的.class
文件、资源文件以及依赖库打包成一个可分发、可执行的JAR(Java Archive)文件是常见操作,使用命令行(jar
命令)完成此任务既基础又强大,以下是详细步骤和说明:
核心前提:安装并配置JDK
- 必须 确保已正确安装 Java Development Kit (JDK)。
jar
命令是 JDK 的一部分,位于 JDK 安装目录的bin
文件夹下(C:\Program Files\Java\jdk-xx\bin
)。 - 必须 将 JDK 的
bin
目录添加到系统的PATH
环境变量中,这样可以在任何命令行窗口(如 Windows 的 CMD/PowerShell 或 Linux/macOS 的 Terminal)中直接运行jar
命令。 - 验证安装: 打开命令行,输入
jar --version
或jar -v
,如果看到类似jar 17.0.10 ...
的版本信息(版本号取决于你的 JDK),说明环境配置正确。
打包步骤详解
假设你的项目结构如下(这是典型结构):
MyProject/
├── src/ (源代码目录,可选,打包通常不需要)
├── bin/ (编译后的 .class 文件目录)
│ ├── com/
│ │ └── example/
│ │ ├── Main.class
│ │ └── Util.class
│ └── resources/ (资源文件目录,如图片、配置文件)
│ └── config.properties
└── lib/ (第三方依赖库目录,可选)
├── library1.jar
└── library2.jar
打包普通 JAR (仅包含 .class 和资源文件,无主清单信息)
- 目标: 将
bin
目录下的所有内容(保持包结构)打包成一个 JAR 文件。 - 命令:
jar cvf myapp.jar -C bin .
- 参数解释:
c
:创建新的 JAR 文件。v
:在标准输出中生成详细输出(显示正在添加的文件)。f
:指定生成的 JAR 文件名(myapp.jar
)。-C bin .
:这是关键部分。-C bin
:在执行后续操作(这里是添加文件)之前,切换到bin
目录。- :表示添加
bin
目录下的(包括子目录com/
和resources/
),但不包含bin
目录本身,这确保了包结构com/example/...
在 JAR 中正确创建。
- 结果: 在当前目录下生成
myapp.jar
,解压后,你会看到com/example/Main.class
,com/example/Util.class
,resources/config.properties
等文件,结构完全对应bin
目录。
打包可执行 JAR (指定主类 Main-Class)
可执行 JAR 包含一个特殊的清单文件 (META-INF/MANIFEST.MF
),其中指定了程序的入口点(主类)。
-
方法 A:创建清单文件并打包
- 创建清单文件 (manifest.txt): 在项目根目录(或任何方便的位置)创建一个文本文件(如
manifest.txt
如下:Main-Class: com.example.Main
- 重要:
- 在
Main-Class:
后必须跟一个空格,然后是完全限定类名(包名+类名)。 - 文件最后一行必须是空行(按回车换行),这是 JAR 规范的要求,否则可能导致
"no main manifest attribute"
错误。
- 在
- 重要:
- 打包命令:
jar cvfm myapp-executable.jar manifest.txt -C bin .
m
:指定要包含的清单文件(manifest.txt
),这个m
选项必须紧跟在f
选项之后(顺序cvfm
或cfmv
都可以,但f
和m
必须相邻且m
后跟清单文件名)。- 其余参数
cvf
和-C bin .
的含义与普通 JAR 打包相同。
- 结果: 生成
myapp-executable.jar
,其META-INF/MANIFEST.MF
文件包含你定义的Main-Class
属性,你可以通过java -jar myapp-executable.jar
直接运行它。
- 创建清单文件 (manifest.txt): 在项目根目录(或任何方便的位置)创建一个文本文件(如
-
方法 B:在命令行中直接指定主类 (JDK 8u7+)
对于较新的 JDK 版本(8u7 及以上),可以使用-e
或--main-class
选项直接在命令行中指定主类,无需单独创建清单文件:jar --create --file myapp-executable.jar --main-class com.example.Main -C bin .
- 参数解释 (使用长选项更清晰):
--create
(c
):创建 JAR。--file myapp-executable.jar
(f myapp-executable.jar
):指定 JAR 文件名。--main-class com.example.Main
(-e com.example.Main
):关键! 指定主类,这会在生成的清单文件中自动添加Main-Class
属性。-C bin .
:切换到bin
目录并添加其所有内容。
- 结果: 与方法 A 相同,生成可执行的
myapp-executable.jar
。
- 参数解释 (使用长选项更清晰):
打包包含依赖库的 JAR (使用 Class-Path)
如果你的项目依赖其他 JAR 文件(在 lib/
目录下),需要在清单文件中设置 Class-Path
属性,告诉 JVM 在哪里找到这些依赖。
- 创建清单文件 (manifest-with-deps.txt):
Main-Class: com.example.Main Class-Path: lib/library1.jar lib/library2.jar
- 重要:
Class-Path:
后跟一个空格。- 列出所有依赖的 JAR 文件,用空格分隔,路径是相对于最终运行 JAR 文件的位置的路径(不是打包时的路径!)。
- 通常将依赖 JAR 放在最终生成的 JAR 文件所在目录的
lib/
子目录下,所以这里写lib/library1.jar
。 - 最后一行必须是空行。
- 重要:
- 打包命令:
jar cvfm myapp-with-deps.jar manifest-with-deps.txt -C bin . lib/
- 关键变化在最后:
-C bin . lib/
-C bin .
:添加编译后的类文件和资源(来自bin
)。lib/
:额外添加项目根目录下的lib/
目录及其内容(library1.jar
,library2.jar
)到 JAR 文件中,这样,依赖库就被打包进 JAR 的根目录下的lib/
文件夹里了。
- 关键变化在最后:
- 结果: 生成
myapp-with-deps.jar
,其内部结构包含:com/example/...
(你的类)resources/...
(你的资源)lib/library1.jar
lib/library2.jar
META-INF/MANIFEST.MF
(包含Main-Class
和Class-Path: lib/library1.jar lib/library2.jar
)
- 运行:
- 将
myapp-with-deps.jar
和它内部lib/
目录下的所有依赖 JAR 文件一起分发(通常需要解压或确保工具能处理嵌套 JAR)。 - 在命令行运行:
java -jar myapp-with-deps.jar
,JVM 会根据清单中的Class-Path
在 JAR 文件内部的lib/
目录下查找依赖库。
- 将
运行 JAR 文件
- 可执行 JAR (有
Main-Class
):java -jar your-application.jar
- 普通 JAR (指定主类运行):
java -cp your-application.jar com.example.Main
查看 JAR 文件内容
- 查看文件列表:
jar tf your-application.jar
- 查看详细文件列表(带大小、时间):
jar tvf your-application.jar
- 查看清单文件内容:
jar xf your-application.jar META-INF/MANIFEST.MF && type META-INF\MANIFEST.MF (Windows) jar xf your-application.jar META-INF/MANIFEST.MF && cat META-INF/MANIFEST.MF (Linux/macOS)
(这会解压出清单文件并显示其内容)
重要注意事项与最佳实践
- 文件编码: 确保你的清单文件 (
manifest.txt
) 使用 UTF-8 编码(无 BOM) 保存,Windows 记事本默认保存为带 BOM 的 UTF-8 或 ANSI,可能导致问题,推荐使用 VS Code、Notepad++、Sublime Text 等编辑器,明确选择UTF-8
或UTF-8 without BOM
编码保存。 - 清单文件空行: 再次强调,清单文件的最后一行必须是空行(一个空回车),这是 JAR 规范的要求,违反会导致
Main-Class
或Class-Path
不被识别。 Class-Path
的路径:Class-Path
中的路径是相对于最终运行java -jar
命令时的工作目录的路径,不是相对于你的 JAR 文件内部的路径(除非使用特殊加载机制),通常将依赖 JAR 放在主 JAR 文件所在目录的子目录(如lib/
)中,并在Class-Path
中写lib/dep1.jar lib/dep2.jar
。- 包含依赖的其他方式: 将依赖库打包进 JAR 的
lib/
目录并设置Class-Path
是标准方式,对于更复杂的依赖管理或需要“胖JAR/UberJAR”(将所有依赖的类解压后和你的类一起打包成一个单一JAR),jar
命令本身不够方便,需要使用构建工具(Maven Shade Plugin, Gradle Shadow Plugin)或专门的打包工具。 - 构建工具: 对于实际项目,强烈推荐使用 Maven 或 Gradle 等构建工具,它们能自动处理依赖下载、编译、资源处理、生成包含正确清单和依赖的可执行 JAR/UberJAR,极大简化流程并减少错误(
mvn clean package
或gradle jar
/gradle shadowJar
)。 - 安全警告: 不要随意执行来源不明的 JAR 文件 (
java -jar unknown.jar
),JAR 文件可以包含恶意代码。
为什么命令行打包是基础技能?
理解 jar
命令的工作原理是 Java 开发者的基本功,它让你:
- 清晰了解 JAR 文件的结构(清单文件、包目录、资源文件)。
- 理解
Main-Class
和Class-Path
机制。 - 在无构建工具或需要快速验证、简单打包时能独立操作。
- 更好地理解构建工具(Maven/Gradle)背后做的事情。
掌握这些命令行操作,能让你在 Java 应用打包和分发方面拥有更扎实的控制力和问题排查能力。
引用说明:
- 本文核心知识基于 Oracle 官方 Java 文档 中关于
jar
工具和 JAR 文件规范的说明。 jar
命令的具体选项和功能可通过在命令行运行jar --help
或查阅对应 JDK 版本的官方工具文档获取最准确信息。
原创文章,发布者:酷番叔,转转请注明出处:https://cloud.kd.cn/ask/6037.html