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