JAR文件
Java归档(JAR)文件用于将应用程序打包成一个单独的文件,一个JAR文件既可以包含类文件,也可以包含诸如图像和声音这些其他类型的文件。JAR文件是压缩的,使用了ZIP压缩格式。
创建JAR文件
在jdk/bin目录下的jar工具可制作JAR文件,创建JAR文件常见命令格式为:jar cvf JARFileName File1 File2 … 。
此外,还有一些其他命令,格式为: jar options File1 File2 。下面为一些可选项:
options | function |
---|---|
c | 创建一个新的JAR存入文件 |
v | 生成详细的输出结果 |
m | 将一个清单文件添加到JAR中 |
f | 将JAR文件指定为第二个命令行参数 |
e | 在JAR文件中添加一个条目 |
i | 建立索引文件 |
x | 解压文件 |
0 | 只存储,不压缩 |
清单文件(manifest)
除了类文件、图像和其他资源外,每个JAR文件还包含一个用于描述归档特征的清单文件。
清单文件被命名为MANIFEST.MF,它位于JAR文件的一个特殊META-INF子目录中,最小的符合标准的清单文件是:Manifest-Version:1.0,复杂的清单文件可能包含更多条目,这些清单条目被分成多个节,第一节称为主节(main section),它作用于整个JAR文件,随后的条目用来指定已命名条目的属性,这些已命名的条目可以是某个文件、包或者URL,它们都必须起始于名为Name的条目,例如:Name:[Class].class。节与节之间用空行分开。
要编辑清单文件,需要将希望添加到清单文件中的行放到文本文件中,然后运行:jar cfm [JARFileName] [ManifestFileName] …
要更新一个已有的JAR文件的清单,需要将增加的部分放置到一个文本文件中,然后执行命令:jar ufm [MyArchive].jar manifest-additions.mf
可执行JAR文件
可以使用jar命令的e选项指定程序的入口点,即通常需要在调用java程序加载器时指定的类:jar cvfe [Myprogram].jar [MainClass] [files]
或者可以在清单中指定应用程序的主类:Main-Class:[MainClass]
清单文件最后一行必须以换行符结束,可以用java -jar命令启动应用程序。
资源
资源是在applet和应用程序中使用的类使用的一些相关数据文件,包括图像、音频、带消息字符串和按钮标签的文本文件、二进制数据文件等。
获取资源的步骤如下:
- 获得具有资源的Class对象;
- 如果资源是一个图像或声音文件,那么就需要调用getresource(filename)获得作为URL的资源位置,然后利用getImage或getAudioClip方法进行读取;
- 与图像或声音文件不同,其他资源可用getResourceAsStream方法读取文件中的数据。
密封(seal)
可以将Java包密封以保证不会有其他的类加入到其中。如果在代码中使用了包可见的类、方法和域,就可能希望密封包。密封一个包,要将包中所有类放入一个JAR文件,在清单文件主节加入一行:Sealed:true改变全局默认设定,对每个包,可以在JAR文件清单增加一行:Sealed:true。
应用首选项的存储
用户通常希望保存它们的首选项和定制信息,再次启动时就可以恢复这些信息,Java传统做法是将配置信息保存在属性文件中,此外还有更健壮的方法:首选项API。
属性映射(property map)
属性映射是一种存储键值对的数据结构,通常用来存储配置信息:
- 键和值是字符串;
- 映射可以很容易的存入文件以及从文件加载;
- 有一个二级表保存默认值。
实现属性映射的类为Properties。以下是一些基本操作:
Properties settings = new Properties();
settings.setProperty("width", "200");
settings.setProperty("title", "Hello, World!"); //指定配置信息
OutputStream out = new FileOutputStream("program.properties");
settings.store(out, "Program Properties"); //将属性映射保存到第一个参数表示的文件,第二个参数是包含在文件中的注释。
InputStream in = new FileInputStream("program.properties");
settings.load(in); //从文件中加载属性
System.getProperties(); //主目录对应的属性映射
String userDir = System.getProperty("user.home") //读取单个键
String title = settings.getProperty("title", "Default title") //如果映射中有一个属性为第一个参数title,title设置为相应的字符串,否则,设为默认值第二个参数Default title。
/*将默认值放在一个二级属性映射中,在主属性映射的构造器中提供这个二级映射*/
Propertier defaultSettings = new Properties();
defaultSettings.setProperty("width", "300");
defaultSettings.setProperty("height", "200");
defaultSettings.setProperty("title", "Default title");
Properties settings = new Properties(defaultSettings);
属性映射实现了Map接口,不过put和get方法操作的是Object对象,getProperty和setProperty操作的是字符串。
属性映射是没有层次的简单类,如果存储复杂的配置信息,应当使用Preferences类。
首选项API
使用属性映射有以下缺点:
- 有些操作系统没有主目录的概念,很难找到一个统一的配置文件位置;
- 有些配置文件的命名没有标准约定,用户安装多个Java应用时,容易发生命名冲突。
有些操作系统有一个存储配置信息的中心存储库,如Windows的注册表。Preferences类以一种平台无关的方式提供了这样一个中心存储库。在Windows中,以注册表存储信息,在Linux中,存在本地文件系统。Preferences存储库有一个树状结构,结点路径名类似于包名,以避免命名冲突。存储库的各个节点分别有一个单独的键值对表,可以用来存储数值、字符串或字节数组,但不能存储可串行化的对象。可以有多个并行的数,操作系统使用当前用户对应的树。下面是一些基本操作:
Preference root = Preferences.userRoot(); //用户根节点
Preference root = Preferences.systemRoot(); //系统根节点
Preference node = root.node("com/mycompany/myapp") //目录访问结点
Preferences node = Preferences.userNodeForPackage(obj.getClass); //用对象访问结点
/*访问键值表*/
int getInt(String key, int defval);
long getLong(String key, long defval);
/*写数据*/
put(String key, String value);
putInt(String key, int value);
注册表这样的存储库存在两个问题:
- 他们会变成充斥着过期信息的垃圾场;
- 配置数据与存储库纠缠在一起,以至于很难把首选项搬到新平台。
对于第二点,可以用exportSubtree(OutputStream out)和exportNode(OutputStream out)方法导出一个子树或结点的首选项。
数据用XML格式保存,可调用importPreferences(InputStream in)方法将数据导入到另一个存储库。
服务加载器
对于采用插件体系结构的应用,JDK提供了ServiceLoader类加载符合公共接口的插件。
定义一个接口,其中包含服务的各个实例应当提供的方法,服务提供者提供一个或多个实现这个服务的类,实现类可以放在任何包中,每个实现类必须有一个无参构造器。把这些类的类名增加到META-INF/services目录下的一个UTF-8编码文本文件中,文件名必须与完全限定类名一致。假设接口名为Service,程序可这样初始化一个服务加载器:public static ServiceLoader< Service > serviceLoader = ServiceLoader.load(Service.class);
服务加载器的iterator方法会对服务提供的所有实现返回一个迭代器。可以遍历所有对象,选择适当的对象使用。