為 Java 1.4 增加的 java.util.prefs 包,通過提供對特定於實現的注冊表(例如,Windows 平台上的 Windows 注冊表)的訪問能力,使您能夠操作用戶首選項數據和配置數據。
您是不是曾經需要保存程序的配置數據但卻不知應將數據存儲在哪裡? 雖然您可以使用屬性文件或資源束獲取這些信息,但 Java 平台從未指定過用於存儲這些文件的標准位置。JSR 10 出現後一切都變了,它為 Java 1.4 API 提供增加的 java.util.prefs 包。存儲機制是特定於實現的細節,但程序員不必知道,也不必操心。對於 Windows 平台,它的位置是在“Windows 注冊表”。 您雖然不能夠對注冊表自由控制,但您的確可以通過一個公共根節點訪問所有的應用程序。
開始
命名很恰當的 Preferences 類提供操作首選項的基本框架。這個類提供了一系列靜態和抽象方法來操作兩套首選項(其中一套是用戶首選項,另一套是系統首選項)中的其中一套。 使用靜態方法,您會得到一個特定於平台的實現,就象 WindowsPreferences 類; 然後您可以使用由這個特定於平台的實現實現的抽象方法來做這項工作。
用包將程序的首選項分組是個好習慣,可以避免與其它應用程序的命名沖突。 當您查找 Preferences 對象時,只需傳遞包的名稱。在使用非靜態方法時, 您可以傳遞對自身的引用(this),程序將為您確定查找的是哪個包,如清單 1 所示。
清單 1. 從非靜態方法獲取 Preferences 對象
Preferences userPrefs = Preferences.userNodeForPackage(this);
Preferences sysPrefs = Preferences.systemNodeForPackage(this);
但是,如果您使用的是靜態方法,您就必須得到根節點並自己提供包,如清單 2 所示。
清單 2. 從靜態方法獲取 Preferences 對象
Preferences userPrefs = Preferences.userRoot().node("/net/zukowski/ibm");
Preferences sysPrefs = Preferences.systemRoot().node("/net/zukowski/ibm");
有了進行操作的節點後,您就可以輕松地設置、獲取、除去和轉儲設置選項。只要把 Preferences 對象當作一個大的鍵 — 值散列表(這個表把樹形結構中的鍵組織起來)。可它不是“集合框架”( Collections Framework)的部件,(關於“集合框架”的更多信息,請參閱參考資料)。
寫數據
我們將從討論如何存儲首選項開始。Preferences 類提供一系列 put() 方法,如下所示,用於存儲值。除支持基本的字符串之外,您還可以存儲布爾值、雙精度數、浮點數、整型數、長整型數和字節數組(考慮序列化)。助手方法采用適當的數據類型並執行必要的轉換以便將數據存儲為字符串。
put(String key, String value)
putBoolean(String key, boolean value)
putByteArray(String key, byte value[])
putDouble(String key, double value)
putFloat(String key, float value)
putInt(String key, int value)
putLong(String key, long value)
所有的 put() 方法都返回一個 void。如果存儲機制不可用,將拋出一個BackingStoreException。
注意:一個特定首選項的鍵長度被限制為 Preferences.MAX_KEY_LENGTH(80)個字符,而它的值被限制為 Preferences.MAX_VALUE_LENGTH(8192)個字符。
讀數據
可通過下面所示的一系列 get() 方法獲取特定的首選項。與寫數據相似,每種受支持的數據類型,都有自己的與眾不同的方法。但與獲取數據時不同的是,在備用存儲不可用,或有些東西尚未保存時您必須提供缺省值。這要求您確保自己的程序至少要有合理的缺省設置選項。
get(String key, String default)
getBoolean(String key, boolean default)
getByteArray(String key, byte default[])
getDouble(String key, double default)
getFloat(String key, float default)
getInt(String key, int default)
getLong(String key, long default)
如果您對首選項名稱不確定,您可以用 keys() 方法查找一列與節點相關聯的鍵。這個方法返回節點的 String[]。 除獲取和存儲個別首選項以及獲取一列關鍵字之外,您還可以用 clear()、remove() 和 removeNode() 除去節點和值。
轉儲數據
如果您想在系統提供的備用存儲器之外保存和恢復首選項,您可以在 XML 格式的文檔中執行這些操作。您可以用 exportNode() 導出一個節點或用 exportSubtree() 導出整個子樹。信息以 UTF-8 格式存儲。然後,當您想恢復信息時,可使用 importPreferences() 方法。
偵聽
“好奇心會害死一只貓”,但如果您對弄清除首選項何時改變很感興趣,您可以注冊一個 NodeChangeListener 或 PreferenceChangeListener,而不考慮隨之而來的後果。NodeChangeListener 負責通知您節點被添加和除去的時間, 而 PreferenceChangeListener 告訴您值的變化。這些都緊跟著基本 JavaBeans 組件事件用 add/removeNodeChangeListener(NodeChangeListener) 和 add/removePreferenceChangeListener() 方法處理結構之後發生。基本上,您先實現偵聽器,然後注冊偵聽器,這樣您會發現將來的變化。
完整的示例
真的就這些。清單 3 為您提供了一個完整的示例來試驗新功能(也可從參考資料下載)。程序運行後會自己清除,所以如果您想在注冊表中找到值,請注釋掉程序尾部的清除代碼。
清單 3. 完整的示例
package net.zukowski.ibm;
import java.io.*;
import java.util.prefs.*;
public class Prefs {
public static void main(String args[]) {
String denominations[] =
{"One", "Two", "Five", "Ten", "Twenty"};
String pictures[] =
{"Washington", "Jefferson", "Lincoln", "Hamilton", "Jackson"};
NodeChangeListener nodeChangeListener =
new NodeChangeListener() {
public void childAdded(NodeChangeEvent event) {
Preferences parent = event.getParent();
Preferences child = event.getChild();
System.out.println(parent.name() + " has a new child " +
child.name());
}
public void childRemoved(NodeChangeEvent event) {
Preferences parent = event.getParent();
Preferences child = event.getChild();
System.out.println(parent.name() + " lost a child " +
child.name());
}
};
PreferenceChangeListener preferenceChangeListener =
new PreferenceChangeListener() {
public void preferenceChange(PreferenceChangeEvent event) {
String key = event.getKey();
String value = event.getNewValue();
Preferences node = event.getNode();
System.out.println(node.name() + " now has a value of " +
value + " for " + key);
}
};
// Look up user root
Preferences prefs =
Preferences.userRoot().node("/net/zukowski/ibm");
// Add listeners
prefs.addNodeChangeListener(nodeChangeListener);
prefs.addPreferenceChangeListener(preferenceChangeListener);
// Save a bunch of key-value pairs
for (int i=0, n=denominations.length; i < n; i++) {
prefs.put(denominations[i], pictures[i]);
}
// Display all the entries
try {
String keys[] = prefs.keys();
for (int i=0, n=keys.length; i < n; i++) {
System.out.println(keys[i] + ": " + prefs.get(keys[i], "Unknown"));
}
} catch (BackingStoreException e) {
System.err.println("Unable to read backing store: " + e);
}
// Create child
Preferences child = Preferences.userRoot().node("/net/zukowski/ibm/foo");
// Save to XML file
try {
FileOutputStream fos = new FileOutputStream("prefs.out");
prefs.exportNode(fos);
} catch (Exception e) {
System.err.println("Unable to export nodes: " + e);
}
// Clean up
try {
prefs.removeNode();
} catch (BackingStoreException e) {
System.err.println("Unable to access backing store: " + e);
}
}
}