System.getProperty读JVM启动参数和内置属性,System.getenv读操作系统环境变量快照;二者来源、用途、可变性均不同,不可混用。

System.getProperty 和 System.getenv 是 Java 中两个完全独立的配置读取机制,来源不同、用途不同、生命周期也不同。不能混用,也不能互相替代。
来源和本质完全不同
System.getProperty 读的是 JVM 自己维护的一套键值对,包括两类:
- JVM 启动时内置的系统属性(如 os.name、java.version、file.separator),由 JVM 根据运行平台自动设置;
- 通过 -Dkey=value 参数显式传入的启动参数(如 -Dapp.env=prod),只有这类才能被 System.getProperty("app.env") 正确读到。
System.getenv 读的是操作系统进程启动那一刻继承来的环境变量快照,比如 shell 中的 export DATABASE_URL=...,或 Docker 的 -e DATABASE_URL=...。它不经过 JVM 解析,也不受 -D 影响。
可变性与安全性差异明显
System.getProperty 支持运行时修改:
- System.setProperty("my.key", "new.value") 可以写入(但部分属性如 user.dir 实际无效);
- 很多框架(如 Spring)默认只信任启动时的 -D 参数,运行时 set 的值可能被忽略;
- 敏感信息(如密码)不应通过 -D 传入,因为 ps aux 或 JVM dump 可能暴露。
System.getenv 返回的是只读 Map:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- System.getenv().put("KEY", "val") 会直接抛 UnsupportedOperationException;
- 环境变量本身在进程启动后就固定了,Java 无法修改宿主机环境;
- 但环境变量容易被外部查看(ps、容器 inspect),所以密码类信息仍需谨慎使用。
实际使用中的常见陷阱
读不到值?先确认“谁启动了你的 Java 进程”:
- 在终端 export MY_VAR=123 后直接 java MyApp → 能读到;
- 用 IntelliJ 点运行 → 默认不继承终端环境,必须在 Run Configuration → Environment variables 里手动添加;
- Docker 容器中 → 宿主机 export 无效,必须用 -e MY_VAR=123 或 ENV 指令透传;
- Kubernetes Pod → 需在 env: 字段显式定义,不能依赖节点环境。
大小写敏感性也要留意:
- Linux/macOS 下 System.getenv("PATH") 和 System.getenv("path") 结果不同;
- Windows 下通常不区分,但不建议依赖该行为。
怎么选:看配置归属谁管
用 System.getProperty 当:
- 读取 JVM 内置信息(os.arch、user.home);
- 接收运维通过启动参数控制的行为(-Dlog.level=debug);
- 非敏感、启动即确定的配置项。
用 System.getenv 当:
- 对接外部服务地址(DATABASE_URL、REDIS_HOST);
- 读取部署环境标识(ENV=staging、NAMESPACE=default);
- 需要与操作系统或其他语言服务共享同一份配置。
安全读取建议:永远检查 null,别直接调方法链。
- 错:System.getenv("PORT").trim() → NullPointerException;
- 对:System.getenv().getOrDefault("PORT", "8080");
- 强约束:Objects.requireNonNull(System.getenv("API_KEY"), "API_KEY required")。

















