1. 概述

在 Java 开发中,System.getProperty()System.getenv() 都用于获取配置信息,但它们的来源和用途完全不同。

简单来说:

System.getProperty() 读取的是 JVM 级别的系统属性
System.getenv() 读取的是 操作系统级别的环境变量

搞混这两者,容易在部署、配置管理时踩坑。本文将从实战角度讲清楚它们的区别、使用场景以及常见误区。


2. 使用 System.getProperty()

System.getProperty() 用于获取 JVM 的系统属性。这些属性本质上是一个 Properties 对象,包含了 JVM 启动时的配置信息,比如操作系统类型、文件编码、用户目录等。

常见系统属性示例

String osArch = System.getProperty("os.arch");
String osName = System.getProperty("os.name");
String osVersion = System.getProperty("os.version");
String fileSep = System.getProperty("file.separator");

log.info("operating system name: {}", osName);
log.info("operating system arch: {}", osArch);
log.info("Operation System version: {}", osVersion);
log.info("file separator: {}", fileSep);

在 Arch Linux 上运行输出:

[main] INFO com...SystemPropertyAndEnvUnitTest -- operating system name: Linux
[main] INFO com...SystemPropertyAndEnvUnitTest -- operating system arch: amd64
[main] INFO com...SystemPropertyAndEnvUnitTest -- Operation System version: 6.6.34-1-lts
[main] INFO com...SystemPropertyAndEnvUnitTest -- file separator: /

在 macOS(Apple Silicon)上输出:

[main] INFO com...SystemPropertyAndEnvUnitTest -- operating system name: Mac OS X
[main] INFO com...SystemPropertyAndEnvUnitTest -- operating system arch: aarch64
[main] INFO com...SystemPropertyAndEnvUnitTest -- Operation System version: 15.0
[main] INFO com...SystemPropertyAndEnvUnitTest -- file separator: /

⚠️ 注意:file.separator 在 Windows 是 \,Linux/macOS 是 /,跨平台开发时务必用这个属性,不要硬编码!

查看所有系统属性

System.getProperties().forEach((k, v) -> LOG.info("{} -> {}", k, v));

输出片段:

sun.arch.data.model -> 64
user.timezone -> Europe/Berlin
java.vm.specification.version -> 22
os.name -> Linux
java.class.version -> 66.0

这些属性大部分由 JVM 自动设置,但你也可以自定义。

动态设置系统属性

System.setProperty("nice.tech.site", "Baeldung");
assertEquals("Baeldung", System.getProperty("nice.tech.site"));

✅ 也可以通过启动参数传入:

java -jar app.jar -Dnice.tech.site="Baeldung"

这是生产环境中最常用的配置方式,比如:

java -jar app.jar -Dspring.profiles.active=prod -Dserver.port=8081

System.getProperty() 返回值永远是 String 类型,注意空值处理。


3. 使用 System.getenv()

环境变量是操作系统级别的键值对,通常用于配置应用运行环境,比如数据库地址、密钥、PATH 路径等。

它们在 JVM 启动前就已经存在,Java 程序通过 System.getenv() 读取。

获取单个环境变量

String homeDir = System.getenv("HOME");
String shell = System.getenv("SHELL");
String terminal = System.getenv("TERM");

log.info("User Home: {}", homeDir);
log.info("Shell: {}", shell);
log.info("Terminal: {}", terminal);

输出示例:

[main] INFO com...SystemPropertyAndEnvUnitTest -- User Home: /home/kent
[main] INFO com...SystemPropertyAndEnvUnitTest -- Shell: /bin/zsh
[main] INFO com...SystemPropertyAndEnvUnitTest -- Terminal: xterm-256color

获取所有环境变量

System.getenv().forEach((k, v) -> LOG.info("{} -> {}", k, v));

输出片段:

COLORTERM=truecolor
HOME=/home/kent
LC_CTYPE=UTF-8
LOGNAME=U533276
PATH=/home/kent/bin:/usr/bin:...
SHELL=/bin/zsh
PYTHON2_BIN=/home/kent/.pyenv/shims

⚠️ 注意:System.getenv() 返回的是一个 只读 Map,尝试修改会抛异常:

Map<String, String> sysEnv = System.getenv();
assertThrows(UnsupportedOperationException.class, () -> sysEnv.put("TECH_SITE", "Baeldung"));

如何在运行时设置环境变量?

❌ 直接通过 System.getenv().put() 是行不通的 —— 这是只读的。

但可以通过 ProcessBuilder子进程设置环境变量:

ProcessBuilder pb = new ProcessBuilder("/bin/sh", "-c", "echo $TECH_SITE");
Map<String, String> env = pb.environment();
env.put("TECH_SITE", "Baeldung");

try (BufferedReader output = new BufferedReader(new InputStreamReader(pb.start().getInputStream()))) {
    String result = output.readLine();
    log.info("TECH_SITE in the new process: {}", result);
}

log.info("TECH_SITE in the current process: {}", System.getenv("TECH_SITE"));

输出:

[main] INFO com...SystemPropertyAndEnvUnitTest -- TECH_SITE in the new process: Baeldung
[main] INFO com...SystemPropertyAndEnvUnitTest -- TECH_SITE in the current process: null

✅ 结论:

  • 环境变量 TECH_SITE 只在子进程中生效
  • 当前 JVM 进程无法修改自己的环境变量(除非用反射黑科技,但不推荐)

4. 核心区别总结

特性 System.getProperty() System.getenv()
作用域 JVM 级别 操作系统级别
来源 JVM 启动参数(-D)、代码中 System.setProperty() 系统全局环境变量、shell 脚本、.env 文件等
可变性 ✅ 可在运行时修改 ❌ 只读(当前进程无法修改)
典型用途 JVM 配置、应用 Profile、日志路径等 数据库密码、API Key、CI/CD 环境标识等
设置方式 -Dkey=value 启动参数 export KEY=VALUE 或系统设置

使用建议

  • ✅ 配置应用行为?优先用 System.getProperty()(通过 -D 传参)
  • ✅ 敏感信息(如密码)?用 System.getenv(),避免出现在进程命令行中
  • ✅ 跨平台路径处理?用 System.getProperty("file.separator"),别硬编码 /\
  • ✅ 部署到 Docker/K8s?环境变量更友好,配合 envFromconfigMap

5. 总结

System.getProperty()System.getenv() 是 Java 中获取外部配置的两个核心手段:

  • System.getProperty():面向 JVM,灵活可变,适合应用内部配置
  • System.getenv():面向操作系统,安全隔离,适合环境敏感信息

理解它们的差异,能帮你写出更健壮、可移植的 Java 应用。尤其是在微服务、容器化部署场景下,合理使用环境变量是最佳实践之一。

💡 小技巧:开发时可以用 .env 文件 + 工具(如 dotenv-java)模拟环境变量,生产环境由运维注入,实现配置解耦。


原始标题:Java System.getProperty vs System.getenv | Baeldung