JDK源码分析--Properties源码分析

x33g5p2x  于2021-12-18 转载在 其他  
字(5.3k)|赞(0)|评价(0)|浏览(447)

注:

以下分析基于JDK1.8.0_74。

一、概述

      1、Properties类表示一组持久属性。属性列表中的每个键及其对应值都是一个字符串。

     2、可以将属性内容写出到stream中或者从stream中读取属性内容。

     3、Properties类继承自Hashtable,是线程安全的类,即多个线程可以共享一个Properties对象,而不需要外部同步。

     4、Hashtable的所有方法Properties对象均可以访问。

     5、Properties支持文本方式和xml方式的数据存储。

     (1)在文本方式中,格式为key:value,其中分隔符可以是:冒号(:)、等号(=)、空格。其中空格可以作为key的结束,同时获取的值回将分割符号两端的空格去掉。

     (2)XML属性文档具有以下DOCTYPE声明:

<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">

注意,导出或导入属性时不访问系统URI (http://java.sun.com/dtd/properties.dtd);它只是作为一个字符串来唯一标识DTD

XML例如:

<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<properties>

<comment>comments</comment>

<entry key="key1">value1</entry>

<entry key="key2">value2</entry>

<entry key="key3">value3</entry>

</properties>

     6、Properties只支持1对1模式的属性设置,而且不支持多层多级属性设置。

二、他爹

1**、继承Hashtable<Object,Object>**

     Hashtable可以理解为线程安全的HashMap,它们都实现了Map接口,内部实现几乎一样。

Hashtable与HashMap的不同点有以下几个:

     (1)Hashtable是线程安全的,方法都使用了synchronized修饰。

但这里值得一提的是,平时在使用线程安全的Map时,并不推荐使用Hashtable,因为其方法使用的同步锁(对象锁,对当前实例加锁),并发执行效率较低;在Java5以后,提供了ConcurrentHashMap,可替代Hashtable的功能,或使用Collections.synchronizedMap((Map<K,V> m)方法获得线程安全的SynchronizedMap类。(ConcurrentHashMap使用的是锁分段技术,可有效提高并发访问效率,具体请自行百度;推荐使用该类达到线程安全的目的)

     (2)HashMap使用的迭代器(Iterator)是fail-fast迭代器,Hashtable的迭代器(Enumerator)不是fail-fast的。

:fail-fast 机制是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。例如:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件。)

     (3)HashMap可以使用null键和值,而Hashtable的键和值都不能为null。

    以put(K key, V value)方法为例:

public synchronized V put(K key, V value) {
    // Make sure the value is not null
    if (value == null) {
        throw new NullPointerException();
    }
    // Makes sure the key is not already in the hashtable.
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
   ……
    return null;
}

     该方法判断value为null时会抛出NullPointerException,而执行后面的语句key.hashCode()时,如果key为null也会抛出NullPointerException。

2**、不建议的继承方法**

JDK1.8帮助文档中提到:

      因为属性继承自Hashtable,所以put和putAll方法可以应用于Properties对象。强烈反对使用它们,因为它们允许调用者插入键或值不是字符串的条目。应该使用setProperty方法。如果在包含非字符串键或值的“受损”Properties对象上调用store或save方法,则调用将失败。类似地,如果对包含非字符串键的“受影响的”Properties对象调用propertyNames或list方法,则调用将失败。

二、重要属性

protected Properties defaults;

      除了继承于Hashtable的属性,Properties仅有这一个属性。其作用在JDK1.8帮助文档中解释为:

“属性列表可以包含另一个属性列表作为其“默认值”;如果在原始属性列表中没有找到属性键,则搜索第二个属性列表。”

     1、默认值的初始化:通过带参数的构造方法对其进行赋值。

     2、默认值的使用:

主要使用在方法getProperty(String key),通过key获取value。即 “当原始属性列表中没有找到属性键时,搜索此默认属性列表”。

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;
}

三、构造方法

     1、无参构造方法

public Properties() {
    this(null);
}

     2、设置默认Properties值的构造方法

public Properties(Properties defaults) {
    this.defaults = defaults;
}

四、常用方法

1**、getProperty(String key)**

     通过键获取值,如果列表中无值,再从默认Properties属性中获取值。

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;
}

2**、getProperty(String key, String defaultValue)**

     通过键获取值,如果无值返回入参的值。

public String getProperty(String key, String defaultValue) {
    String val = getProperty(key);
    return (val == null) ? defaultValue : val;
}

3**、load(InputStream inStream)**

     从字节流中加载key/value键值对,要求所有的key/value键值对是按行存储,同时是ISO-8859-1编码。

public synchronized void load(InputStream inStream) throws IOException {
    load0(new LineReader(inStream));
}

private void load0 (LineReader lr) throws IOException {
   ……
    String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
    String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
    put(key, value);
}

4**、load(Reader reader)**

     从字符流中加载key/value键值对,要求所有的键值对都是按照行来存储的。

public synchronized void load(Reader reader) throws IOException {
    load0(new LineReader(reader));
}

load0方法同上。

5**、loadFromXML(InputStream in)**

     从xml文件中加载property,Java7底层使用XMLUtils.load(Properties,InputStream)方法来加载;Java8对这个方法做了修改,以下源码为Java8;XML默认情况下使用UTF-8字符编码。

public synchronized void loadFromXML(InputStream in)
    throws IOException, InvalidPropertiesFormatException{
    XmlSupport.load(this, Objects.requireNonNull(in));
    in.close();
}
private static final XmlPropertiesProvider PROVIDER = loadProvider();
static void load(Properties props, InputStream in)
    throws IOException, InvalidPropertiesFormatException{
    PROVIDER.load(props, in);
}

loadProvider()方法返回的是抽象类XmlPropertiesProvider的一个实现类BasicXmlPropertiesProvider,底层通过其ParserSAX来实现XML数据加载。

6**、store(Writer writer, String comments)**

     将所有的property(保存defaults的)都写出到字符流中,如果给定comments的话,则可在输入设置上添加注释。

public void store(Writer writer, String comments)
        throws IOException{
    store0((writer instanceof BufferedWriter)?(BufferedWriter)writer
                                             : new BufferedWriter(writer),
           comments,
           false);
}

7**、store(OutputStream out, String comments)**

     将所有的property(保存defaults的)都写出到字节流中,默认ISO-8859-1编码,如果给定comments的话,则可在输入设置上添加注释。

public void store(OutputStream out, String comments)
        throws IOException{
    store0(new BufferedWriter(new OutputStreamWriter(out, "8859_1")),
           comments,
           true);
}

8**、storeToXML(OutputStream os, String comment, String encoding)**

     数据写入到xml文件中。

public void storeToXML(OutputStream os, String comment, String encoding)
        throws IOException{
    XmlSupport.save(this, Objects.requireNonNull(os), comment,
                    Objects.requireNonNull(encoding));
}

public void storeToXML(OutputStream os, String comment)
        throws IOException{
    storeToXML(os, comment, "UTF-8");
}

相关文章