在 Java 開發中,.properties 配置文件因其簡潔易讀、適合存儲鍵值對配置,被廣泛應用於項目中。本文將系統梳理讀取屬性文件的幾種主流方式,並通過代碼示例幫助你快速掌握核心用法。
java.util.Properties 類(JDK 原生方案)Properties 繼承自 Hashtable,實現了 Map 接口。雖然可用 put/putAll,但官方建議使用 setProperty/getProperty 確保鍵值均為字符串類型。持久化時使用 store()(save() 已棄用)。加載配置文件提供兩種 load 重載:
load(InputStream inStream)load(Reader reader)// 1. 通過當前類的類加載器(推薦用於 classpath 下資源)
InputStream in = TestProperties.class.getClassLoader().getResourceAsStream("test.properties");
// 2. 直接從文件系統讀取
InputStream in = new FileInputStream(new File("filePath"));
// 3. 通過系統類加載器
InputStream in = ClassLoader.getSystemResourceAsStream("filePath");
// 4. Servlet 環境下通過 ServletContext 獲取
InputStream in = context.getResourceAsStream("filePath");
// 5. 通過 URL 打開流
URL url = new URL("path");
InputStream in = url.openStream();
Properties prop = new Properties();
prop.load(inStream);
String username = prop.getProperty("username"); // 推薦
// String username = (String) prop.get("username"); // 不推薦,返回 Object 需強轉
java.util.ResourceBundle 類(更簡便的資源綁定)ResourceBundle 專為國際化設計,讀取 .properties 時無需加後綴名,自動按 Locale 匹配。主要有兩種用法:
getBundle()// 文件位於包 com.mmq 下,名為 test.properties
ResourceBundle bundle = ResourceBundle.getBundle("com/mmq/test");
String username = bundle.getString("username");
PropertyResourceBundleInputStream in = ...; // 獲取流的方式同上
ResourceBundle bundle = new PropertyResourceBundle(in);
路徑注意事項:
- 文件在包下:Properties用com/mmq/test.properties,ResourceBundle用com/mmq/test
- 文件在 src 根目錄:直接用test.properties或test
下面展示一個生產級的封裝示例,包含單例加載器、併發緩存工具類及常量定義,支援外部配置路徑覆蓋。
package com.bijian.study;
import java.io.File;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public final class ResourceLoader {
private static final ResourceLoader INSTANCE = new ResourceLoader();
private static final Map<String, Properties> CACHE = new HashMap<>();
private ResourceLoader() {}
public static ResourceLoader getInstance() {
return INSTANCE;
}
public Properties getPropFromProperties(String fileName) throws Exception {
// 1. 優先從緩存獲取
Properties cached = CACHE.get(fileName);
if (cached != null) return cached;
// 2. 確定文件路徑:優先讀取 JVM 參數 configurePath,否則從 classpath 獲取
String filePath;
String configPath = System.getProperty("configurePath");
if (configPath == null) {
filePath = this.getClass().getClassLoader().getResource(fileName).getPath();
} else {
filePath = configPath + "/" + fileName;
}
// 3. 加載並緩存
Properties prop = new Properties();
prop.load(new FileInputStream(new File(filePath)));
CACHE.put(fileName, prop);
return prop;
}
}
package com.bijian.study;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class PropertiesUtil {
private static final ResourceLoader LOADER = ResourceLoader.getInstance();
private static final ConcurrentMap<String, String> CONFIG_CACHE = new ConcurrentHashMap<>();
private static final String DEFAULT_FILE = "test.properties";
public static String getStringByKey(String key, String propName) {
try {
Properties prop = LOADER.getPropFromProperties(propName);
key = key.trim();
// 雙重檢查鎖定模式避免重複解析
return CONFIG_CACHE.computeIfAbsent(key, k -> prop.getProperty(k));
} catch (Exception e) {
throw new RuntimeException("加載配置失敗: " + propName, e);
}
}
public static String getStringByKey(String key) {
return getStringByKey(key, DEFAULT_FILE);
}
public static Properties getProperties() {
try {
return LOADER.getPropFromProperties(DEFAULT_FILE);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
// Constant.java
package com.bijian.study;
public class Constant {
public static final String TEST = PropertiesUtil.getStringByKey("test", "test.properties");
}
// Main.java
package com.bijian.study;
public class Main {
public static void main(String[] args) {
System.out.println(Constant.TEST);
}
}
Properties.load() 或 ResourceBundle.getBundle() 即可。ConcurrentHashMap 保證線程安全。-DconfigurePath=/external/config 實現配置文件外部化,便於部署變更。Properties.load(InputStream) 默認 ISO-8859-1,若含中文請改用 load(Reader) 配合 InputStreamReader(stream, "UTF-8")。掌握這幾種方式,靈活選擇,即可輕鬆應對各類配置讀取需求。