核心概念:java.util.Properties 类
无论使用哪种方法,最终都会用到 java.util.Properties 这个核心类,它是一个 Hashtable 的子类,专门用于处理键值对形式的配置信息,键和值都是 String 类型。

它的主要方法包括:
load(InputStream inStream): 从输入流中加载属性列表。load(Reader reader): 从字符输入流中加载属性列表(推荐用于处理非 ASCII 字符)。getProperty(String key): 用指定的键在此属性列表中搜索属性。getProperty(String key, String defaultValue): 同上,如果找不到键,则返回默认值。setProperty(String key, String value): 设置一个属性,如果已存在则覆盖。store(OutputStream out, String comments): 将属性列表写入输出流。store(Writer writer, String comments): 将属性列表写入字符输出流。
使用 ClassLoader (最经典、最常用)
这是最传统和推荐的方法,因为它能利用 Java 类路径(Classpath)机制,自动从 src/main/resources 目录(或 Maven/Gradle 的相应目录)下查找文件。
适用场景:当 .properties 文件作为项目资源被打包到 JAR 或 WAR 文件中时。
步骤:

- 将
.properties文件放在项目的类路径下,src/main/resources/config.properties。 - 在 Java 代码中,通过
ClassLoader获取文件的输入流。 - 使用
Properties对象的load()方法加载流。 - 通过
getProperty()方法获取值。
代码示例:
假设我们有一个文件 src/main/resources/config.properties:
# Database Configuration db.url=jdbc:mysql://localhost:3306/mydb db.username=admin db.password=secret app.name=MyAwesomeApp app.version=1.0.0
对应的 Java 代码:
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class PropertiesReaderExample {
public static void main(String[] args) {
// 1. 创建 Properties 对象
Properties props = new Properties();
// 使用 try-with-resources 确保 InputStream 被自动关闭
try (InputStream input = PropertiesReaderExample.class.getClassLoader().getResourceAsStream("config.properties")) {
// 2. 检查文件是否存在
if (input == null) {
System.out.println("Sorry, unable to find config.properties");
return;
}
// 3. 加载属性文件
props.load(input);
// 4. 获取属性值
String dbUrl = props.getProperty("db.url");
String username = props.getProperty("db.username");
String password = props.getProperty("db.password");
String appName = props.getProperty("app.name", "DefaultApp"); // 提供默认值
String appVersion = props.getProperty("app.version");
// 打印结果
System.out.println("App Name: " + appName);
System.out.println("App Version: " + appVersion);
System.out.println("Database URL: " + dbUrl);
System.out.println("Database User: " + username);
System.out.println("Database Password: " + password);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
优点:

- 简单、直接。
- 文件作为资源,打包和部署非常方便。
- 是 Java 生态中最标准的做法。
缺点:
- 只能读取类路径下的文件,无法直接读取文件系统中的任意文件(除非手动设置类路径)。
使用 Files (Java 7+,现代 NIO 方式)
如果你的 .properties 文件位于文件系统的任意位置,并且你使用的是 Java 7 或更高版本,可以使用 java.nio.file.Files 和 java.nio.charset.StandardCharsets 来读取。
适用场景:当配置文件位于项目外部(如 /etc/myapp/config.properties)时。
代码示例:
假设文件在 C:/temp/config.properties。
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Properties;
public class FilesPropertiesReader {
public static void main(String[] args) {
// 1. 定义文件路径
Path path = Paths.get("C:/temp/config.properties");
// 2. 创建 Properties 对象
Properties props = new Properties();
try {
// 3. 使用 Files.newBufferedReader 读取文件
// try-with-resources 确保 Reader 被自动关闭
try (var reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
// 4. 加载属性
props.load(reader);
// 5. 获取并打印属性值
System.out.println("App Name: " + props.getProperty("app.name"));
System.out.println("Database URL: " + props.getProperty("db.url"));
}
} catch (IOException e) {
System.err.println("Error reading properties file: " + e.getMessage());
e.printStackTrace();
}
}
}
优点:
- 灵活性高,可以读取文件系统上任何位置的文件。
- 使用 NIO API,性能更好,对文件编码处理更优雅(明确指定
UTF-8)。
缺点:
- 需要硬编码或通过参数传入文件路径,降低了可移植性。
- 相比 ClassLoader 方法,代码稍显繁琐。
使用 ResourceBundle (国际化场景)
ResourceBundle 专门用于国际化(i18n),但它也可以非常方便地加载 .properties 文件,它会根据默认区域设置或指定的区域设置来加载不同的属性文件(如 config_en_US.properties)。
适用场景:需要多语言支持时,或者只是想利用其资源加载机制。
命名规则:
- 基础文件名(如
config) - 语言代码(可选,如
en) - 国家/地区代码(可选,如
US) - 后缀
.properties
config.properties (默认), config_zh_CN.properties (简体中文)。
代码示例:
假设文件 config.properties 在 src/main/resources 下。
import java.util.ResourceBundle;
public class ResourceBundleExample {
public static void main(String[] args) {
// 1. 获取资源包
// 文件名不带 .properties 后缀,也不包含路径
// ResourceBundle 会自动从类路径下查找
ResourceBundle labels = ResourceBundle.getBundle("config");
// 2. 获取属性值
// 如果键不存在,会抛出 MissingResourceException
try {
String appName = labels.getString("app.name");
String dbUrl = labels.getString("db.url");
System.out.println("App Name from ResourceBundle: " + appName);
System.out.println("Database URL from ResourceBundle: " + dbUrl);
// 尝试获取一个不存在的键
// String nonExistent = labels.getString("non.existent.key"); // 会抛出异常
} catch (Exception e) {
System.err.println("Error: " + e.getMessage());
}
}
}
优点:
- 专为国际化设计,可以轻松切换不同语言的配置。
- 自动处理资源加载,代码简洁。
- 性能较好,加载后会被缓存。
缺点:
- 不适合需要动态修改配置的场景,因为
ResourceBundle是只读的。 - 键不存在时会抛出异常,不如
Properties.getProperty()的默认值机制灵活。
总结与最佳实践
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
ClassLoader |
标准项目资源(JAR/WAR 内部) | 最标准、最简单、推荐 | 只能读类路径下文件 |
Files (NIO) |
外部文件(任意文件系统路径) | 非常灵活,编码处理清晰 | 路径硬编码,可移植性差 |
ResourceBundle |
国际化(i18n) 或只读配置 | 专为多语言设计,性能好 | 只读,无默认值机制 |
最佳实践建议:
-
首选
ClassLoader方法:对于绝大多数 Java 项目,将配置文件放在src/main/resources目录下,并使用ClassLoader.getResourceAsStream()是最健壮、最标准的做法,它确保了配置文件与应用程序一同打包和部署。 -
处理编码问题:
.properties文件中包含非 ASCII 字符(如中文),请务必使用InputStreamReader包装InputStream并指定正确的字符集(如UTF-8),或者直接使用Properties.load(Reader)方法。// 更好的编码处理方式 try (InputStream input = getClass().getClassLoader().getResourceAsStream("config.properties"); Reader reader = new InputStreamReader(input, StandardCharsets.UTF_8)) { props.load(reader); } -
提供默认值:使用
props.getProperty("key", "default_value")可以在配置文件中缺少某个键时避免NullPointerException,使程序更健壮。 -
处理异常:始终对
IOException进行捕获和处理,不要让程序因读取配置失败而意外终止。 -
考虑使用更强大的库:对于大型或复杂的应用,可以考虑使用更高级的配置库,如 Apache Commons Configuration 或 Spring Boot 的
@ConfigurationProperties,它们提供了类型安全的绑定、属性自动刷新、环境变量集成等更强大的功能。
