优先使用 system.getenv("key") 安全读取环境变量,避免 system.getenv().get();需判空处理,注意跨平台大小写差异;system.getproperty() 读取 jvm 参数而非系统环境变量。

怎么安全读取系统环境变量(比如 PWD 或 HOME)
直接调用 System.getenv(String) 是最常用方式,但它返回的是只读快照——进程启动时的环境值,后续外部修改不会反映进来。如果业务依赖实时环境(比如热更新配置路径),这里会出问题。
常见错误是写成 System.getenv().get("PATH"),看似拿到全部再取值,但 System.getenv() 返回的是不可变 Map,且在某些 JDK 版本(如 Java 9+ 模块化后)可能抛 SecurityException(尤其 Web 容器里)。
- 优先用带参数的
System.getenv("HOME"),它比遍历整个 map 更快、更安全 - 不要假设环境变量一定存在:空值很常见,必须判空,比如
Objects.requireNonNullElse(System.getenv("TMPDIR"), "/tmp") - Windows 下变量名不区分大小写,Linux/macOS 区分——跨平台代码别硬写
"path",用"PATH"
System.getProperty() 和环境变量到底啥区别
System.getProperty() 读的是 JVM 启动参数(-Dkey=value)或内置系统属性(如 os.name),和操作系统环境变量完全隔离。很多人混淆这两者,结果在 Docker 里传了 ENV TZ=Asia/Shanghai 却用 System.getProperty("TZ") 去取,当然为空。
- 典型可读属性:
"java.version"、"user.home"、"file.separator"——这些是 JDK 自带的,无需设置 - 自定义属性必须显式加
-D:启动命令写java -Dapp.env=prod MyApp,才能用System.getProperty("app.env") - 注意
user.dir是进程启动目录,不是System.getProperty("user.home")——后者才是用户主目录
为什么 System.arraycopy() 比 for 循环快,但又不能乱用
System.arraycopy() 是 JVM 底层直接调用内存拷贝指令(类似 C 的 memcpy),绕过 Java 数组边界检查和元素复制逻辑,所以大批量数组拷贝时性能优势明显。但它不是万能的“加速器”。
立即学习“Java免费学习笔记(深入)”;
- 源数组和目标数组必须类型兼容,否则抛
ArrayStoreException;基本类型数组之间不能混用(int[]不能拷到long[]) - 目标数组必须已初始化且足够长,否则抛
IndexOutOfBoundsException——它不会自动扩容 - 小数组(比如长度 arraycopy 有 JNI 调用成本
- 多维数组只拷第一层引用,不是深拷贝:
int[][] src = {{1}};拷过去后,修改dst[0][0]会影响src[0][0]
System 类里哪些操作是线程不安全的
System 类本身是 final 工具类,但它的部分方法行为受外部状态影响,容易引发并发问题。最典型的是 System.setSecurityManager()(已废弃)和 System.setOut()/System.setErr()。
-
System.setOut()全局替换 stdout,多个线程同时调用会导致输出混乱,且无法回滚——生产环境绝对禁止动态改 -
System.currentTimeMillis()和System.nanoTime()本身线程安全,但若用它们做时间差计算(比如测耗时),要注意nanoTime()不保证单调性(某些旧 CPU 会有回跳) - 所有
System.setProperty()操作都是全局生效,且没有原子性保障:两个线程同时 set 同一个 key,结果取决于执行顺序
真正麻烦的是,这些方法看起来“只是读个值”,但实际牵扯 JVM 全局状态。一旦在框架底层(比如日志组件)偷偷调了 setOut,上层应用就很难排查输出丢失的问题。









