Java 归档(JAR)文件设计的目的:
在应用程序打包时,希望只向用户提供一个单独的文件,而不是一个包含大量类文件的目录结构。

JAR 文件既可以包含类文件,也可以包含诸如图像和声音等其他类型的文件。JAR 文件是压缩的,JAR 文件使用了我们熟悉的 ZIP 压缩格式。可以使用任何 ZIP 工具查看 JAR 文件。

提示: pack200 是一种较通常的 ZIP 压缩算法更加有效的压缩类文件的方式。Oracle 声称,对类文件的压缩率接近 90%。

1. 创建 JAR 文件

可以使用 jar 工具制作 JAR 文件(在默认的 JDK 安装目录中,位于 jdk/bin 目录下)。创建一个新的 JAR 文件应该使用的常见命令格式为:
jar cvf JARFileName File1 File2 ...
例如,
jar cvf CalculatorClasses.jar *.class icon.gif
通常,jar 命令的格式如下:
jar options File1 File2 ...

可以将应用程序和代码库打包在 JAR 文件中。

java 程序选项
选项说明
c创建一个新的或者空的存档文件并加人文件。如果指定的文件名是目录,jar 程序将会对它们进行递归处理
C暂时改变目录,例如: jar cvf jarFileName.jar -C classes *.class 改变到 classes 子目录以便增加类文件
e在清单文件中创建一个入口点
f指定 JAR 文件名作为第二个命令行参数。如果没有这个参数,jar 命令会将结果写至标准输出(在创建 JAR 文件时)或者从标准输入读取(在解压或者列出 JAR 文件内容时)
i建立索引文件(用于加快大型归档的查找)
m将一个清单文件(manifest)添加到 JAR 文件中。清单是对归档内容和来源的一个说明。每个归档有一个默认的清单文件。但是,如果想验证归档文件的内容,可以提供自己的清单文件
M不为条目创建清单文件
t显示内容表
u更新一个已有的 JAR 文件
v生成详细的输出结果
x解压文件。如果提供一个或多个文件名,只解压这些文件;否则,解压所有文件
0存储,不进行 ZIP 压缩

2. 清单文件

除了类文件、图像和其他资源外,每个 JAR 文件还包含一个清单文件(manifest),用于描述归档文件的特殊特性。

清单文件被命名为 MANIFEST.MF,它位于 JAR 文件的一个特殊 META-INF 子目录中。符合标准的最小清单文件极其简单:
Manifest-Version: 1.0

复杂的清单文件可能包含更多条目。这些清单条目被分成多个节。第一节被称为主节(main section)。它作用于整个 JAR 文件。随后的条目用来指定命名实体的属性,如单个文件、包或者 URL。它们都必须以一个 Name 条目开始。节与节之间用空行分开。例如:

Mainfest-Version: 1.0lines describing this archiveName: Woozle.classlines describing this fileName: com/mycompany/mypkg/lines describing this package

要想编辑清单文件,需要将希望添加到清单文件中的行放到文本文件中,然后运行:
jar cfm JARFileName ManifestFileName ...
例如,要创建一个包含清单的 JAR 文件,应该运行:
jar cfm MyArchive.jar manifest.mf com/mycompany/mypkg/*.class

要想更新一个已有的 JAR 文件的清单,则需要将增加的部分放置到一个文本文件中,然后执行下列命令:
jar ufm MyArchive.jar manifest-additions.mf

注释: 请参考 https://docs.oracle.com/javase/10/docs/specs/jar/jar.html 获得有关 JAR 文件和清单文件格式的更多信息。

3. 可执行 JAR 文件

可以使用 jar 命令中的 e 选项指定程序的入口点,即通常需要在调用 java 程序启动器时指定的类:
jar cvfe MyProgram.jar com.mycompany.mypkg.MainAppClass files to add
或者,可以在清单文件中指定程序的主类,包括以下形式的语句:
Main-Class: com.mycompany.mypkg.MainAppClass
不要为主类名增加扩展名 .class 。

警告: 清单文件的最后一行必须以换行符结束。否则,清单文件将无法被正确地读取。常见的错误是创建了一个只包含 Main-Class 行而没有行结束符的文本文件。

不论哪一种方法,可以简单地通过下面命令来启动应用程序:
java -jar MyProgram.jar

要通过双击 JAR 文件图标来启动应用程序,下面是各种操作系统的操作方式:

  • 在 Windows 平台中,Java 运行时安装程序将为 “.jar” 扩展名创建一个文件关联。会用 javaw -jar 命令启动文件(与 java 命令不同,javaw 命令不打开 shell 窗口)。
  • 在 Mac OS X 平台中,操作系统能够识别 “.jar” 扩展名文件。当双击 JAR 文件时就会执行 Java 程序。

人们对 JAR 文件中的 Java 程序与原生应用程序还是感觉不同。在 Windows 平台中,可以使用第三方的包装器工具将 JAR 文件转换成 Windows 可执行文件。包装器是一个 Windows 程序(扩展名为 .exe),它可以查找和加载 Java 虚拟机(JVM),或者在没有找到 JVM 时告诉用户应该做些什么。有许多商业和开源的产品,例如,Launch4J 和 IzPack。

4. 打包 Jar 包示例

ResourceTest.mf 内容:

Main-Class: com.xiang117.corejava.ResourceTest

目录结构:

ResourceTest.mfcom  └─xiang117      └─corejava          ├──about.png          ├──ResourceTest.class          └──data                ├──content.txt                └──title.txt

打包命令:
jar cvfm ResourceTest.jar ResourceTest.mf com/xiang117/corejava/*.class com/xiang117/corejava/*.png com/xiang117/corejava/data/*.txt
java -jar ResourceTest.jar

5. 多版本 JAR 文件

随着模块和包强封装的引入,之前可以访问的一些内部 API 不再可用。例如,JavaFX 8 有一个内部类 com.sun.javafx.css.CssParser。如果用它解析一个样式表,你会发现你的程序不再能正常编译了。补救很简单,只需要改用 Java 9 提供的 javafx.css.CssParser。不过这样会有一个问题。你需要向 Java 8 和 Java 9 用户发布不同的应用程序,或者需要利用类加载器和反射等一些技巧。

为了解决类似这样的问题,Java 9 引入了多版本 JAR(multi-release JAR),其中可以包含面向不同 Java 版本的类文件。
为了保证向后兼容,额外的类文件放在 META-INF/version 目录中:

Application.classBuildingBlocks.classUtil.classMETA-INF  ├──MANIFEST.MF(with line Multi-Release: true)  ├──versions  ├──9  │  ├──Application.class  │  └──BuildingBlocks.class  └──10     └──BuildingBlocks.class

假设 Application 类使用了 CssParse 类。那么遗留版本的 Application.class 文件可以使用 com.sun.javafx.css.CssParser,而 Java 9 版本可以使用 javafx.css.CssParser

Java 8 完全不知道 META-INF/version 目录,它只会加载遗留的类。Java 9 读取这个 JAR 文件时,则会使用新版本。

要增加不同版本的类文件,可以使用 --release 标志:
jar uf MyProgram.jar --release 9 Application.class

要从头构建一个多版本的 JAR 文件,可以使用 -C 选项,对应每个版本要切换到一个不同的类文件目录:
javac cf MyProgram.jar -C bin/8 . --release 9 -C bin/9 Application.class
面向不同版本编译时,要使用 --release 标志和 -d 标志来指定输出目录:
javac -d bin/8 --release 8 ...

在 Java 9 中,-d 选项会创建这个目录(如果原先的目录不存在)。
--release 标志也是 Java 9 新增的。在较早的版本中,需要使用 -source、-target 和 -boot-classpath 标志。JDK 现在为之前的两个 API 版本提供了符号文件。在 Java 9 中,编译时可以将 --release 设置为 9、8 或 7.

多版本 JAR 并不适用于不同版本的程序或库。对于不用的版本,所有类的公共 API 都应当是一样的。多版本 JAR 的唯一目的是支持你的某个特定版本的程序或库能够在多个不同的 JDK 版本上运行。如果你增加了功能或改变了一个 API,那就应当提供一个新版本的 JAR。

注释: javap 之类的工具并没有改造为可以处理多版本 JAR 文件。如果调用:
javap -classpath MyProgram.jar Application.class
你会得类的基本版本(毕竟,它与更新的版本应该有相同的公共 API)。如果必须查看更新的版本,可以调用:
javap -classpath MyProgram.jar\!/META-INFO/version/9/appliaction.class

6. 关于命令行选项说明

Java 开发包(JDK)的命令行选项一直以来都使用单个短横线加多字母选项名的形式,如:
java -jar ...
javac -XLint:unchecked -classpath ...
但 jar 命令是个例外,这个命令遵循经典的 tar 命令选项格式,而没有短横线:
jar cvf

从 Java 9 开始,Java 工具开始转向一种更常用的选项格式,多字母选项名前面加两个短横线,另外对于常用的选项可以使用单字母快捷方式。例如,调用 Linux ls 命令时可以提供一个 “human-readable” 选项:
ls --human-readable
或者
ls -h

在 Java 9 中,可以使用 --version 而不是 -version,另外可以使用 --class-path 而不是 -classpath。

详细内容可以参见 JEP 293 增强请求(http://openjdk.java.net/jeps/293)。在所有清理工作中,作者还提出要标准化选项参数。带 -- 和多字母的选项的参数用空格或者一个等号(=)分割:
javac --class-path /home/user/classdir ...

javac --class=path=/home/user/classdir ...
单字母选项的参数可以用空格分割,或者直接跟在选项后面:
javac -p moduledir ...

javac -pmoduledir ...

警告: 后一种方式现在不能使用,而且一般来讲这也不是一个好主意。如果模块目录恰好是 arametes 或 rocessor,这就很容易与遗留的选项发生冲突。

无参数的单字母选项可以混合在一起:
jar -cvf MyProgram.jar -e mypackage.MyProgram */*.class

警告: 目前不能使用这种方式。这肯定会带来混淆。假设 javac 有一个 -c 选项。那么 javac -cp 是指 java -c -p 还是 -cp?

这就会带来一些混乱,希望过段时间能够解决这个问题。尽管我们想要远离这些古老的 jar 选项,但最好还是等到尘埃落定为妙。不过,如果你想做到最现代化,那么可以安全地使用 jar 命令的长选项:
jar --create --verbose --file jarFileName file1 file2 ...
对于单字母选项,如果不组合,也是可以使用的:
jar -c -v -f jarFileName file1 file2 ...

7. 密封

Java 包密封(seal)可以保证不会有其他的类加入到其中。如果在代码中使用了包可见的类、方法和域,就可能希望密封包,如果不密封,其他类就有可能放在这个包中,进而访问包可见的特性。

如果密封了 com.mycompany.util 包,就不能用下面的语句顶替密封包之外的类:
package com.mycompany.util
要想密封一个包,需要将包中的所有类放到一个 JAR 文件中。在默认情况下,JAR 文件中的包是没有密封的。可以在清单文件的主节中加入下面一行:
Sealed: true
来改变全局的默认设定。对于每个单独的包,可以通过在 JAR 文件的清单中增加一节,来指定是否想要密封这个包。例如

Name: com/mycompany/util/Sealed: trueName: com/mycompany/misc/Sealed: true

要想密封一个包,需要创建一个包含清单指令的文本文件。然后用常规的方式运行 jar 命令:
jar cvfm MyArchive.jar manifest.mf files to add

©著作权归作者所有:来自51CTO博客作者bpx_的原创作品,如需转载,请注明出处,否则将追究法律责任

你的鼓励让我更有动力

赞赏

0人进行了赞赏支持

更多相关文章

  1. 详解 centos7设置nfs文件共享 实操记录
  2. 关于PHP框架中.env文件的加载过程
  3. PHP识别文件伪装(文件上传)
  4. PHP作用域和文件夹操作示例
  5. php实现将文件上传到临时目录
  6. php怎么把文件设置为插件
  7. 关于检测文件是否有病毒的PHP实现逻辑
  8. php获取文件夹中文件的两种方法
  9. 了解PHP文件上传相关知识

随机推荐

  1. 【Android】让Python在Android系统上飞一
  2. android四大组件学习总结
  3. Pro Android 4 第五章 理解Intent
  4. Android 常用UI控件的一些属性设置(在.xm
  5. Android(安卓)ViewPager嵌套ViewPager滑
  6. Android基础控件使用指南
  7. Service的xml属性解析
  8. 说说 Android 的常见 UI 控件
  9. Android事件分发/传递机制总结
  10. (一)Android事件分发机制 - View篇