JDK源碼分析--Properties源碼分析

注:

        以下分析基於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");
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章