Java常用系统工具类库与System

高精度计时应选System.nanoTime();它提供纳秒级单调时间,不受系统时钟调整影响,适合性能压测和算法耗时对比,而System.currentTimeMillis()仅适用于表示当前时刻,精度低且易受NTP同步干扰。

System.currentTimeMillis() 和 System.nanoTime() 怎么选

需要高精度计时(比如性能压测、算法耗时对比)时,System.nanoTime() 是唯一可靠选择;System.currentTimeMillis() 只适合表示“当前时刻”(如日志时间戳、缓存过期计算),它受系统时钟调整影响,可能回拨或跳变。

常见错误是用 currentTimeMillis() 做短于 10ms 的耗时统计,结果出现负数或忽大忽小——因为它的底层依赖系统时钟,精度通常只有 10–15ms(Windows)或 1–10ms(Linux),且会被 NTP 同步干扰。

  • System.nanoTime() 返回纳秒级单调递增值,不受系统时钟修改影响,但不表示真实时间,不能和日期类互转
  • 两次 nanoTime() 相减得到的是纳秒差,除以 1_000_000 得毫秒,注意别直接除 1000(那是微秒)
  • 不要用 nanoTime() 存储或跨 JVM 传递,它没有全局意义,只在单次运行内有效

System.getProperty() 常用键名与陷阱

读取 JVM 启动参数或环境信息最常用的方式,但很多键名不是标准定义,不同 JDK 版本/厂商行为不一致。关键要区分哪些是规范保证的,哪些是“大家习惯这么用但没写进 spec”。

以下键名在 OpenJDK、Oracle JDK、Zulu 等主流实现中基本可用:

  • java.version:JD

    K 主版本号(如 "17.0.1"
  • os.name:操作系统名称("Linux""Windows 10""Mac OS X"
  • user.home:当前用户主目录路径("/home/xxx""C:\\Users\\xxx"
  • file.separator:文件路径分隔符("/""\\"),比硬写 "/" 更安全

容易踩的坑:

  • os.arch 返回值不可靠,OpenJDK 17 在 Apple M1 上返回 "aarch64",但某些旧工具链仍期望 "arm64"
  • java.io.tmpdir 路径末尾不带斜杠,拼接文件名时必须手动加 File.separator 或用 Path.of()
  • 自定义参数(如 -Dmy.config=prod)必须用 System.getProperty("my.config"),不能漏掉前缀 "my."

System.setSecurityManager() 已被移除,替代方案是什么

从 JDK 17 开始,System.setSecurityManager() 被彻底移除(JEP 411),调用会抛出 UnsupportedOperationException。这不是临时禁用,而是永久删除——因为 Security Manager 设计复杂、性能差、实际拦截能力弱,且现代容器/云环境已用更细粒度的隔离机制替代。

如果你的代码还在调用它(比如老项目升级到 JDK 17+),必须改写:

  • 日志、配置、网络访问等敏感操作,改用显式权限检查 + 自定义策略(如 Spring Security 的 @PreAuthorize、Shiro 的 Subject.isPermitted()
  • 禁止反射调用私有方法?用模块系统(module-info.java)控制包导出,或运行时用 Method.setAccessible(false) 配合安全管理器遗留逻辑(仅限 JDK 16 及以前)
  • 沙箱执行第三方脚本?改用独立进程(ProcessBuilder)+ 资源限制(cgroups / Docker),或嵌入 GraalVM 的 Context 沙箱

注意:JDK 17 的 java.security.manager 系统属性已无效,启动加 -Djava.security.manager=allow 不起作用。

Arrays、Objects、Objects.requireNonNull() 这些“工具类”和 System 有什么关系

它们不属于 System 类,但常被误认为是“系统级工具”,其实都是 java.util 包下的普通工具类,和 System 没继承或依赖关系。真正和 System 强相关的只有 System.outSystem.inSystem.err 这三个静态字段。

容易混淆的点:

  • Objects.requireNonNull() 是空指针防护工具,但它不读系统属性、不触发 GC、不调用 native 方法——它只是个纯 Java 方法,性能开销极小
  • Arrays.toString()System.out.println() 经常一起用,但前者不依赖后者;你可以把数组字符串存到文件或发 HTTP 请求,完全绕过 System.out
  • System.gc() 是建议 JVM 执行 GC,但不保证立即执行;而 Runtime.getRuntime().gc() 效果相同,两者无实质区别,都不该在生产代码中主动调用
String[] arr = {"a", "b", "c"};
System.out.println(Arrays.toString(arr)); // 常见组合,但二者解耦
// 等价于:
String s = Arrays.toString(arr);
System.out.println(s); // 显式拆开,更清楚职责

真正需要警惕的是把 System 当成“万能工具箱”——它只提供最底层、最不可替代的系统交互入口(时间、属性、IO 流、退出),其余都该交给专门的工具类或框架。