Properties class 表示为一组持久的属性,Properties可以保存为stream或者从stream中加载。每个key和它对应的value在属性列表里是一个string。
一个属性列表可以包含其他的属性列表作为它的”defaults”;如果原始属性列表中找不到属性键,则搜索第二个属性列表。
因为Properties继承自HashTable,Properties object可以使用
put(K key, V value),
putAll(Map<? extends K, ? extends V> t) putAll(Map<? extends K, ? extends V> t)
方法。他们允许调用者添加键或值不是String,但是强烈不建议这样使用。可以使用 setProperty(String key, String value)方法。如果Store()和save()方法在一个包含非string的key or value的“折衷的,妥协的”Properties object上被调用,调用将会失败。同样的,如果在一个包含非string key的“折衷的”Properties object上调用propertyNames()或list()方法,将会失败。
load(java.io.Reader), store(java.io.Writer, java.lang.String) 方法在指定的一个简单的面向行的格式中基于stream加载和保存属性
load(java.io.InputStream),store(java.io.OutputStream,java.lang.String) 方法和load(Reader)/store(Writer, String)以相同的方式运行,除了输入/输出stream采用ISO 8859-1字符编码。不能在此编码中直接表示的字符可以使用定义的Unicode转义来编写。在转义序列中只允许一个”u”字符。 Native2ASCII工具可用于属性文件和其他字符编码相互转换。
loadFromXML(InputStream) and storeToXML(OutputStream, String, String)方法在一个简单的XML格式中load和store属性。默认使用UTF-8编码,但是,如果需要,可以指定特定的编码。实现需要支持UTF-8和UTF-16编码,还有或许也要支持其他编码。一个XML属性文件有以下的DOCTYPE声明:XXXXXXXXX
这个class是线程安全的,多线程不需要外部同步就可以分享单个Properties object。
一个属性列表,其中包含此属性列表中未找到的任何键的默认值。
protected Properties defaults;
创建一个没有默认值的空的属性列表
public Properties() {
this(null);
}
创建一个有指定默认值的空的属性列表
public Properties(Properties defaults) {
this.defaults = defaults;
}
调用Hashtable的put()方法,提供类似和getProperty()方法,
强制使用string作为属性的key和value。
返回的值是HashTable调用put()的结果
public synchronized Object setProperty(String key, String value) {
return put(key, value);
}
在一个简单的面向行格式中从input character stream读取属性列表(keyand element pairs)。
properties按行处理,自然行和逻辑行。
一个自然行被定为一组行终止符\n \r \r\或者the end of the stream。
一个自然行可以是一个空白行,一个注释行,包含全部或者部分key-element pair。
一个逻辑行包含所有key-element的数据,可以通过使用反斜杠字符\转义行终止符序列,
将这些数据分布在几个相邻的自然行中;注意,注释行不能这么扩展;每个注释的自然行必须有她自己的注释指示符,如下所述,直至抵达stream的end,lines从input中读取。
一个自然行只包含空白字符会被认为是空白并且忽略掉。一个注释行有一个ASCII作为它的首个非空白字符。注释行同样会被忽略并且不会编码key-element信息。除了行终止符,还有不很多会认为是空白的
如果一个逻辑行分布在多个自然行中,转义行终止符序列、行终止符序列和下一行开头的任何空格的反斜杠对键或元素值没有影响。key和element解析(加载时)的其余讨论将假设构成key和element的所有字符在行连续字符被删除后出现在单个自然行上,请注意,仅检查行终止符序列之前的字符来确定行终止符是否被转义是不够的; 要转义行终止符,必须有奇数个连续的反斜杠。 由于输入是从左到右处理的,因此在行终止符(或其他地方)之前的非零偶数 2 n 个连续反斜杠在转义处理后编码 n 个反斜杠。
键包含行中从第一个非空白字符开始到但不包括第一个未转义的 {@code '='}、{@code ':'} 或空白字符的所有字符 除了行终止符。 所有这些键终止字符都可以通过使用前面的反斜杠字符将它们转义来包含在键中; 例如,{@code \:\=}
将是两个字符的键 {@code ":="}。 可以使用 {@code \r} 和 {@code \n} 转义序列包含行终止符。 跳过键后的任何空格; 如果键后的第一个非空白字符是 {@code '='} 或 {@code ':'},则忽略它,并且也跳过它后面的任何空白字符。 该行所有剩余的字符成为相关元素字符串的一部分; 如果没有剩余字符,则元素为空字符串 {@code ""}。 一旦识别出构成键和元素的原始字符序列,就如上所述执行转义处理。
八进制转义不被识别。 字符序列 {@code \b} 不代表退格字符。
该方法不会将无效转义字符之前的反斜杠字符 {@code \} 视为错误; 反斜杠被悄悄删除。 例如,在 Java 字符串中,序列 {@code "\z"} 会导致编译时错误。 相比之下,此方法会默默地删除反斜杠。 因此,此方法将两个字符序列 {@code "\b"} 视为等效于单个字符 {@code 'b'}。 单引号和双引号不需要转义; 然而,根据上面的规则,反斜杠前面的单引号和双引号字符仍然分别产生单引号和双引号字符。 Unicode 转义序列中只允许有一个“u”字符。 此方法返回后,指定的流保持打开状态。
public synchronized void load(Reader reader) throws IOException {
load0(new LineReader(reader));
}
从输入字节流中读取属性列表(键和元素对)。 输入流采用简单的面向行的格式,如load(java.io.Reader) 中所指定,并假定使用 ISO 8859-1 字符编码; 即每个字节是一个 Latin1 字符。 不在 Latin1 中的字符和某些特殊字符使用 3.3 节中定义的 Unicode 转义在键和元素中表示。
此方法返回后,指定的流保持打开状态。
public synchronized void load(InputStream inStream) throws IOException {
load0(new LineReader(inStream));
}
都是一些内部的方法,处理数据
class LineReader{
}
private String loadConvert(){}
private String saveConvert(){}
private static void writeComments(){}
调用store(OutputStream out, String comments)方法,catch抛出的IOExceptions
废弃的原因,如果在保存属性列表是发生I/O error,这个方法不抛出异常
保存属性列表的首选方法是通过store(OutputStream out, String comments)
或者storeToXML(OutputStream os, String comment)方法
@Deprecated
public void save(OutputStream out, String comments) {
try {
store(out, comments);
} catch (IOException e) {
}
}
调用别的方法
properties 表中的属性列表(键和元素对)用load(java.io.Reader) load(Reader)
方法格式写入到输出字符流
Properties表中默认的那些属性不会被写入
如果注释参数不是空的,然后是一个ASCII的字符(#),注释字符串和行分隔符首先写入到输出流
and so on.....都是关于注释
这个方法返回了之后会保持开启
public void store(Writer writer, String comments)
throws IOException
{
store0((writer instanceof BufferedWriter)?(BufferedWriter)writer
: new BufferedWriter(writer),
comments,
false);
}
和前面那个差不多
不同点:
这个流写入用的是ISO 8859-1字符编码
注释中的非Latin-1要注意
这个方法返回了之后会保持开启
public void store(OutputStream out, String comments)
throws IOException
{
store0(new BufferedWriter(new OutputStreamWriter(out, "8859_1")),
comments,
true);
}
将指定输入流上的 XML 文档表示的所有属性加载到此属性表中
The XML document must have the following DOCTYPE declaration:
* <pre>
* <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
* </pre>
* Furthermore, the document must satisfy the properties DTD described
* above.
具体要求如上
这个方法返回了之后会关闭
public synchronized void loadFromXML(InputStream in)
throws IOException, InvalidPropertiesFormatException
{
XmlSupport.load(this, Objects.requireNonNull(in));
in.close();
}
调用此方法的行为与调用storeToXML(os, comment, "UTF-8") 完全相同。
public void storeToXML(OutputStream os, String comment)
throws IOException
{
storeToXML(os, comment, "UTF-8");
}
public void storeToXML(OutputStream os, String comment, String encoding)
throws IOException
{
XmlSupport.save(this, Objects.requireNonNull(os), comment,
Objects.requireNonNull(encoding));
}
根据key找属性,找不到就返回null
public String getProperty(String key) {
Object oval = super.get(key);
String sval = (oval instanceof String) ? (String)oval : null;
return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval;
}
根据key找属性,找不到就返回默认值
public String getProperty(String key, String defaultValue) {
String val = getProperty(key);
return (val == null) ? defaultValue : val;
}
返回此属性列表中所有键的枚举,如果尚未从主属性列表中找到同名的键,则包括默认属性列表中的不同键。
public Enumeration<?> propertyNames() {
Hashtable<String,Object> h = new Hashtable<>();
enumerate(h);
return h.keys();
}
返回此属性列表中的一组键,其中键及其对应的值是字符串,如果尚未从主属性列表中找到同名的键,则在默认属性列表中包括不同的键。 键或值不是 <tt>String</tt> 类型的属性将被省略。 返回的集合不受 <tt>Properties</tt> 对象的支持。 对此<tt>Properties</tt> 的更改不会反映在集合中,反之亦然。
public Set<String> stringPropertyNames() {
Hashtable<String, String> h = new Hashtable<>();
enumerateStringProperties(h);
return h.keySet();
}
打印列表
public void list(PrintStream out) {
out.println("-- listing properties --");
Hashtable<String,Object> h = new Hashtable<>();
enumerate(h);
for (Enumeration<String> e = h.keys() ; e.hasMoreElements() ;) {
String key = e.nextElement();
String val = (String)h.get(key);
if (val.length() > 40) {
val = val.substring(0, 37) + "...";
}
out.println(key + "=" + val);
}
}
支持以 XML 格式加载/存储属性的类
此处定义的load,store方法委托给系统范围的XmlPropertiesProvider(sun.util.spi.XmlPropertiesProvider,此类中有load, store方法)
在第一次调用任一方法时,系统范围的提供程序的位置如下
如果定义了系统属性 {@code sun.util.spi.XmlPropertiesProvider},则它被视为具体提供者类的全限定名称使用系统类加载器作为启动加载器加载该类
如果无法使用零参数构造函数加载或实例化它,则会引发未指定的错误
如果未定义系统属性,则使用 {@link ServiceLoader} 类定义的服务提供者加载工具来定位提供者,系统类加载器作为启动加载器和 {@code sun.util.spi.XmlPropertiesProvider} 作为服务类型。 如果此过程失败,则会引发未指定的错误。 如果安装了多个服务提供者,则不会指定将使用哪个提供者。
如果通过上述方式未找到提供者,则系统默认提供者将被实例化并使用。
private static class XmlSupport {
private static XmlPropertiesProvider loadProviderFromProperty(ClassLoader cl) {
String cn = System.getProperty("sun.util.spi.XmlPropertiesProvider");
if (cn == null)
return null;
try {
Class<?> c = Class.forName(cn, true, cl);
return (XmlPropertiesProvider)c.newInstance();
} catch (ClassNotFoundException |
IllegalAccessException |
InstantiationException x) {
throw new ServiceConfigurationError(null, x);
}
}
private static XmlPropertiesProvider loadProviderAsService(ClassLoader cl) {
Iterator<XmlPropertiesProvider> iterator =
ServiceLoader.load(XmlPropertiesProvider.class, cl).iterator();
return iterator.hasNext() ? iterator.next() : null;
}
private static XmlPropertiesProvider loadProvider() {
return AccessController.doPrivileged(
new PrivilegedAction<XmlPropertiesProvider>() {
public XmlPropertiesProvider run() {
ClassLoader cl = ClassLoader.getSystemClassLoader();
XmlPropertiesProvider provider = loadProviderFromProperty(cl);
if (provider != null)
return provider;
provider = loadProviderAsService(cl);
if (provider != null)
return provider;
return new jdk.internal.util.xml.BasicXmlPropertiesProvider();
}});
}
private static final XmlPropertiesProvider PROVIDER = loadProvider();
static void load(Properties props, InputStream in)
throws IOException, InvalidPropertiesFormatException
{
PROVIDER.load(props, in);
}
static void save(Properties props, OutputStream os, String comment,
String encoding)
throws IOException
{
PROVIDER.store(props, os, comment, encoding);
}
}