java.util.Properties 是 Java 早期引入的配置管理類,繼承自 Hashtable<Object, Object>。雖然現代框架(Spring Boot、MicroProfile Config)提供了更強大的配置抽象,但 Properties 仍廣泛應用於:
System.getProperties())ResourceBundle 底層即基於 Properties)掌握其遍歷方式,是 Java 開發者的基本功。
Properties props = new Properties();
try (InputStream in = ClassLoader.getSystemResourceAsStream("wahaha.properties")) {
if (in == null) throw new IllegalArgumentException("配置文件未找到");
props.load(in); // ISO-8859-1 編碼
} catch (IOException e) {
throw new UncheckedIOException(e);
}
注意: load(InputStream) 預設使用 ISO-8859-1 編碼。若配置含中文,需轉碼或使用 load(Reader) 指定 UTF-8:
try (Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
props.load(reader);
}
propertyNames() — 遍歷所有鍵(推薦)Enumeration<?> keys = props.propertyNames(); // 返回 String 鍵的枚舉
while (keys.hasMoreElements()) {
String key = (String) keys.nextElement();
String value = props.getProperty(key); // 類型安全獲取值
System.out.println(key + " = " + value);
}
Enumeration<String>(Java 8+ 泛型推斷)。Hashtable 的非 String 鍵。getProperty(key) 可獲取預設值、避免空指針。elements() — 遍歷所有值(Hashtable 遺留)Enumeration<?> values = props.elements(); // 返回 Object 值的枚舉
while (values.hasMoreElements()) {
String value = (String) values.nextElement(); // 強轉有風險
System.out.println(value);
}
Hashtable,返回所有值(包括非 String)。entrySet() — 遍歷鍵值對(最通用、最強大)Set<Map.Entry<Object, Object>> entries = props.entrySet();
for (Map.Entry<Object, Object> entry : entries) {
Object key = entry.getKey();
Object value = entry.getValue();
System.out.println(key + " : " + value);
}
Hashtable 實現 Map 接口。props.entrySet().forEach(e -> System.out.println(e.getKey() + "=" + e.getValue()));
// 或轉為 Map 進行流式操作
Map<String, String> map = props.entrySet().stream()
.collect(Collectors.toMap(e -> (String)e.getKey(), e -> (String)e.getValue()));
假設 wahaha.properties 內容:
name1=xxxx
name2=yyyyy
name3=zzzzzzz
| 方法 | 輸出結果 | 適用場景 |
|---|---|---|
propertyNames() | name1 name2 name3 | 僅需遍歷鍵、或需配合 getProperty() 取值 |
elements() | xxxx yyyyy zzzzzzz | 極少用,僅需值且確信全為 String |
entrySet() | name1:xxxx name2:yyyyy name3:zzzzzzz | 最通用,需鍵值對、轉 Map、流式處理 |
getProperty(key, defaultVal)int timeout = Integer.parseInt(props.getProperty("timeout", "3000")); // 預設 3000
boolean debug = Boolean.parseBoolean(props.getProperty("debug", "false"));
// 獲取所有以 "db." 開頭的屬性
props.stringPropertyNames().stream()
.filter(k -> k.startsWith("db."))
.forEach(k -> System.out.println(k + "=" + props.getProperty(k)));
public class ReloadableProps {
private final AtomicReference<Properties> propsRef = new AtomicReference<>();
private final Path path;
private final WatchService watcher;
public ReloadableProps(String filePath) throws IOException {
this.path = Paths.get(filePath);
this.watcher = FileSystems.getDefault().newWatchService();
path.getParent().register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);
reload();
new Thread(this::watchLoop).start();
}
// ... 實現 watchLoop 監聽修改並 reload()
}
store() 寫入時的編碼與註釋try (OutputStream out = new FileOutputStream("out.properties");
Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8)) {
props.store(writer, "更新時間: " + Instant.now()); // 註釋行以 # 開頭
}
store(OutputStream, comments) 同樣預設 ISO-8859-1,建議包裝 Writer 指定 UTF-8。
@ConfigurationProperties(prefix = "app")
@Data
public class AppConfig {
private String name;
private int port;
private List<String> servers;
private Map<String, String> labels;
}
優勢:類型安全、自動綁定、驗證(JSR-303)、熱重載(Spring Cloud Config)、IDE 提示、文檔化。Properties 遍歷僅作為底層理解與舊系統兼容之用。
Properties 遍歷雖簡單,但細節決定質量:首選 propertyNames() 或 entrySet(),避免 elements();注意編碼問題;善用預設值與流式 API。 理解其 Hashtable 血統,有助於排查並發、序列化等邊界問題。新專務請擁抱類型安全的配置綁定框架,將精力集中在業務邏輯而非字串解析上。