早期 Java 應用常用 Properties 讀寫配置文件,但面臨「配置文件放哪兒」的棘手問題:用戶目錄?安裝目錄?還是注冊表?JDK 1.4 引入 java.util.prefs.Preferences,提供統一、跨平台、自適應底層存儲(Windows 注冊表、Linux 文件系統)的配置持久化方案,徹底解決此痛點。
Preferences 採用層次化節點樹結構,類似文件系統路徑。每個應用通過包名路徑或自定義路徑定位節點,實現隔離。
| 作用域 | 適用場景 | Windows 存儲位置 | Linux 存儲位置 |
|---|---|---|---|
| User Preferences | 用戶個性化設置(窗口位置、最近文件、主題) | HKEY_CURRENT_USER\Software\JavaSoft\Prefs | ~/.java/.userPrefs |
| System Preferences | 全局配置(授權信息、默認策略、系統級參數) | HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Prefs | /etc/.java/.systemPrefs (需 root 權限) |
// 用戶級節點:/com/mycompany/myapp
Preferences userPrefs = Preferences.userNodeForPackage(MyApp.class);
// 系統級節點
Preferences sysPrefs = Preferences.systemNodeForPackage(MyApp.class);
自動以類的包路徑作為節點路徑,避免硬編碼字符串,重構安全。
Preferences userPrefs = Preferences.userRoot().node("/com/mycompany/shared");
Preferences sysPrefs = Preferences.systemRoot().node("/com/mycompany/global");
Preferences 提供 7 種基本類型的 putXxx/getXxx 方法,讀取時必須提供默認值,避免空指針:
// 寫入
prefs.put("window.title", "My Application");
prefs.putInt("window.width", 800);
prefs.putBoolean("auto.save", true);
prefs.putLong("last.modified", System.currentTimeMillis());
prefs.putByteArray("icon.data", iconBytes);
// 讀取(第二個參數為默認值)
String title = prefs.get("window.title", "Default Title");
int width = prefs.getInt("window.width", 600);
boolean autoSave = prefs.getBoolean("auto.save", false);
注意:getXxx 從不拋出異常,鍵不存在或類型不匹配時靜默返回默認值。
// 導出單個節點
prefs.exportNode(new FileOutputStream("config.xml"));
// 導出整個子樹
prefs.exportSubtree(new FileOutputStream("config-full.xml"));
// 導入(靜態方法)
Preferences.importPreferences(new FileInputStream("config.xml"));
XML 格式標準化,便於版本控制、跨機器同步、人工編輯。
// 刪除節點及所有子節點(謹慎使用)
prefs.removeNode();
// 刷新到持久存儲(通常自動刷新,關鍵操作後可顯式調用)
prefs.flush();
// 監聽變更
prefs.addPreferenceChangeListener(evt ->
System.out.println("Key: " + evt.getKey() + " New: " + evt.getNewValue()));
prefs.addNodeChangeListener(evt ->
System.out.println("Child node added/removed: " + evt.getChild().absolutePath()));
public class AppConfig {
private static final Preferences PREFS = Preferences.userNodeForPackage(AppConfig.class);
// 窗口狀態持久化
public void saveWindowState(Rectangle bounds) {
PREFS.putInt("win.x", bounds.x);
PREFS.putInt("win.y", bounds.y);
PREFS.putInt("win.w", bounds.width);
PREFS.putInt("win.h", bounds.height);
}
public Rectangle loadWindowState(Rectangle defaultBounds) {
return new Rectangle(
PREFS.getInt("win.x", defaultBounds.x),
PREFS.getInt("win.y", defaultBounds.y),
PREFS.getInt("win.w", defaultBounds.width),
PREFS.getInt("win.h", defaultBounds.height)
);
}
// 最近文件列表(最多 10 個)
public void addRecentFile(String file) {
String[] recent = getRecentFiles();
List list = new ArrayList<>(Arrays.asList(recent));
list.remove(file);
list.add(0, file);
if (list.size() > 10) list = list.subList(0, 10);
for (int i = 0; i < list.size(); i++) {
PREFS.put("recent." + i, list.get(i));
}
PREFS.putInt("recent.count", list.size());
}
public String[] getRecentFiles() {
int count = PREFS.getInt("recent.count", 0);
String[] files = new String[count];
for (int i = 0; i < count; i++) {
files[i] = PREFS.get("recent." + i, "");
}
return files;
}
}
SecurityExceptionexportSubtree 到用戶可見位置,便於故障恢復Preferences API 是 Java 標準庫中「被低估的寶藏」。它屏蔽了平台差異,提供類型安全、分層隔離、可導出的配置管理能力。對於桌面應用、服務端輕量配置、插件系統,它是零依賴、零配置、開箱即用的最佳選擇。下次再為「配置文件放哪」糾結時,請直接 Preferences.userNodeForPackage(YourClass.class) —— 然後專注於業務邏輯吧。