概述
大多数Java应用在某个阶段都需要使用属性文件,通常以键值对的形式存储一些简单的参数,这些数据不包含在编译后的代码中。为此,Java语言提供了对属性文件的内置支持,通过java.util.Properties
工具类来处理这类配置文件。
本文将重点介绍如何使用Properties
。
加载属性
2.1. 从属性文件加载
我们先来看一个从属性文件中加载键值对的例子。我们将加载两个放在类路径上的文件:
app.properties
:
version=1.0
name=TestApp
date=2016-11-12
catalog
:
c1=files
c2=images
c3=videos
注意虽然推荐使用带有.properties
后缀的文件,但这并不是必需的。
现在我们可以简单地将它们加载到Properties
实例中:
String rootPath = Thread.currentThread().getContextClassLoader().getResource("").getPath();
String appConfigPath = rootPath + "app.properties";
String catalogConfigPath = rootPath + "catalog";
Properties appProps = new Properties();
appProps.load(new FileInputStream(appConfigPath));
Properties catalogProps = new Properties();
catalogProps.load(new FileInputStream(catalogConfigPath));
String appVersion = appProps.getProperty("version");
assertEquals("1.0", appVersion);
assertEquals("files", catalogProps.getProperty("c1"));
只要文件内容符合属性文件的格式要求,Properties
类就能正确解析。更多关于属性文件格式的细节请参阅。
2.2. 从XML文件加载
除了属性文件,Properties
类还可以加载XML文件,遵循特定的DTD规范。
以下是来自icons.xml
文件的键值对加载示例:
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>xml example</comment>
<entry key="fileIcon">icon1.jpg</entry>
<entry key="imageIcon">icon2.jpg</entry>
<entry key="videoIcon">icon3.jpg</entry>
</properties>
现在让我们加载它:
String rootPath = Thread.currentThread().getContextClassLoader().getResource("").getPath();
String iconConfigPath = rootPath + "icons.xml";
Properties iconProps = new Properties();
iconProps.loadFromXML(new FileInputStream(iconConfigPath));
assertEquals("icon1.jpg", iconProps.getProperty("fileIcon"));
获取属性
我们可以使用getProperty(String key)
和getProperty(String key, String defaultValue)
方法根据键获取值。
如果存在对应的键值对,两种方法都会返回相应的值。但如果找不到这样的键值对,前者会返回null
,而后者会返回defaultValue
:
String appVersion = appProps.getProperty("version");
String appName = appProps.getProperty("name", "defaultName");
String appGroup = appProps.getProperty("group", "baeldung");
String appDownloadAddr = appProps.getProperty("downloadAddr");
assertEquals("1.0", appVersion);
assertEquals("TestApp", appName);
assertEquals("baeldung", appGroup);
assertNull(appDownloadAddr);
尽管Properties
类继承了Hashtable
类的get()
方法,但我们不建议使用它获取值。get()
方法会返回一个Object
值,只能强制转换为String
,而getProperty()
方法已经为我们妥善处理了原始的Object
值。
下面的代码会抛出异常:
float appVerFloat = (float) appProps.get("version");
设置属性
我们可以使用setProperty()
方法更新已有的键值对或添加新的键值对:
appProps.setProperty("name", "NewAppName"); // update an old value
appProps.setProperty("downloadAddr", "www.baeldung.com/downloads"); // add new key-value pair
String newAppName = appProps.getProperty("name");
assertEquals("NewAppName", newAppName);
String newAppDownloadAddr = appProps.getProperty("downloadAddr");
assertEquals("www.baeldung.com/downloads", newAppDownloadAddr);
同样,虽然Properties
类继承了Hashtable
类的put()
和putAll()
方法,出于与get()
方法相同的理由,我们也不建议使用它们;Properties
只支持String
类型的值。
下面的代码不能按预期工作,当我们使用getProperty()
获取其值时,它会返回null
:
appProps.put("version", 2);
删除属性
如果要删除键值对,可以使用remove()
方法:
String versionBeforeRemoval = appProps.getProperty("version");
assertEquals("1.0", versionBeforeRemoval);
appProps.remove("version");
String versionAfterRemoval = appProps.getProperty("version");
assertNull(versionAfterRemoval);
存储
6.1. 存储到属性文件
Properties
类提供了一个store()
方法来输出键值对:
String newAppConfigPropertiesFile = rootPath + "newApp.properties";
appProps.store(new FileWriter(newAppConfigPropertiesFile), "store to properties file");
第二个参数用于添加注释。如果我们不需要写任何注释,可以将其设置为null
。
6.2. 存储到XML文件
Properties
类还提供了storeToXML()
方法,以XML格式输出键值对:
String newAppConfigXmlFile = rootPath + "newApp.xml";
appProps.storeToXML(new FileOutputStream(newAppConfigXmlFile), "store to xml file");
第二个参数与store()
方法相同。
其他常见操作
Properties
类还提供了其他操作属性的方法:
appProps.list(System.out); // list all key-value pairs
Enumeration<Object> valueEnumeration = appProps.elements();
while (valueEnumeration.hasMoreElements()) {
System.out.println(valueEnumeration.nextElement());
}
Enumeration<Object> keyEnumeration = appProps.keys();
while (keyEnumeration.hasMoreElements()) {
System.out.println(keyEnumeration.nextElement());
}
int size = appProps.size();
assertEquals(3, size);
默认属性列表
Properties
对象可以包含另一个Properties
对象作为其默认属性列表。如果原始属性中找不到键,将搜索默认列表。
除了app.properties
,我们的类路径上还有一个default.properties
文件:
default.properties
:
site=www.google.com
name=DefaultAppName
topic=Properties
category=core-java
示例代码:
String rootPath = Thread.currentThread().getContextClassLoader().getResource("").getPath();
String defaultConfigPath = rootPath + "default.properties";
Properties defaultProps = new Properties();
defaultProps.load(new FileInputStream(defaultConfigPath));
String appConfigPath = rootPath + "app.properties";
Properties appProps = new Properties(defaultProps);
appProps.load(new FileInputStream(appConfigPath));
assertEquals("1.0", appVersion);
assertEquals("TestApp", appName);
assertEquals("www.google.com", defaultSite);
9. 属性和编码
默认情况下,属性文件期望是ISO-8859-1(拉丁-1)编码,因此不应使用包含ISO-8859-1编码范围以外字符的属性。
我们可以通过工具(如JDK的native2ascii工具)或在文件上显式指定编码来解决这一限制。
对于XML文件,loadFromXML()
方法和storeToXML()
方法默认使用UTF-8字符编码。然而,在读取编码不同的XML文件时,可以在DOCTYPE
声明中指定。写入也足够灵活,我们可以在storeToXML()
API的第三个参数中指定编码。
结论
在这篇文章中,我们讨论了基本的Properties
类用法。我们学习了如何使用Properties
,加载和存储键值对(属性和XML格式),操作Properties
对象中的键值对(如获取值、更新值和获取大小),以及如何为Properties
对象使用默认列表。
示例的完整源代码可以在这个GitHub项目中找到。