在Java中如何编写配置文件读取工具_JavaProperties实战解析

Java读取.properties文件最稳妥方式是用Properties类配合ClassLoader.getResourceAsStream()并指定UTF-8编码,避免硬编码路径、文件流及默认编码问题;它不支持嵌套和变量引用,多环境需手动切换文件,Properties非线程安全但只读操作安全,热更新应借助配置中心。

Java里读取.properties文件最稳妥的方式是用Properties类配合

ClassLoader.getResourceAsStream()

硬编码FileInputStream路径或直接用new FileInputStream("config.properties")在打包成.jar后必然失败——资源不在文件系统里,而在JAR包内。必须走类路径加载。

常见错误现象:FileNotFoundException、空配置、中文乱码(尤其Windows下默认GBK写入但UTF-8读取)。

  • 始终用getClass().getClassLoader().getResourceAsStream("config.properties"),不要拼绝对路径
  • 如果文件在src/main/resources下,传入的路径就是"config.properties";若在子目录如conf/,则写"conf/config.properties"
  • 显式指定字符集:properties.load(new InputStreamReader(input, StandardCharsets.UTF_8)),避免JDK版本差异导致默认编码不一致

为什么Properties.load()不支持嵌套和变量引用?

Properties本质是Hashtable的子类,只处理key=value扁平结构,不解析占位符(如${db.url})、不支持层级(如database.host只是普通key,不是嵌套对象)。

这意味着:你不能靠原生Properties实现类似Spring Boot的@Value("${redis.port}")或YAML那种缩进结构。

  • 若需要变量替换,得自己扫描value中的${...}并递归替换(注意循环引用风险)
  • 若需分组管理,只能按命名约定,比如用redis.hostredis.port,再用string.startsWith("redis.")筛选
  • 别试图重写load()去支持JSON/YAML语法——那是配置中心或第三方库(如Apache Commons Configuration)的事

多环境配置怎么切?Java原生Properties没内置profile机制

Spring Boot的application-dev.properties是框架层抽象,java.util.Properties本身不识别环境后缀。你得手动控制加载哪个文件。

典型做法是通过JVM参数或系统属性决定加载路径:

String env = System.getProperty("env", "prod");
String fileName = "config-" + env + ".properties";
InputStream is = MyClass.class.getClassLoader().getResourceAsStream(fileName);
  • 确保所有环境配置文件都放在resources目录下,且命名统一(如config-dev.propertiesconfig-test.properties
  • 启动时加-Denv=dev,否则默认加载config-prod.properties
  • 别依赖os.name自动切环境——开发机是Mac、测试机是Linux、生产是CentOS,逻辑会失控

性能与线程安全:Properties实例能不能复用?

Properties不是线程安全的,但load()只在初始化时调用一次;之后的getProperty()是只读操作,无并发问题。真正要注意的是“何时加载”和“是否重复加载”。

  • 用静态块或static final Properties保证单例加载,避免每次调用都打开流、解析文本
  • 不要在方法里反复new Properties().load(...)——IO开销大,且可能因编码不一致导致两次读出不同结果
  • 如果配置需热更新(如ZooKeeper推送),原生Properties不适用,得自己加监听+原子替换引用(例如用AtomicReference

复杂点在于:一旦引入热加载,就必须考虑旧配置正在被业务线程读取,新旧切换的可见性边界。这时候别硬刚,直接上ConsulNacos客户端更省心。