二十八、API之《System 类》——与系统交互的“桥梁”

在 Java 开发中,System 类是一个特殊且核心的存在。

它不像普通类那样需要创建实例,而是直接通过静态成员与系统资源交互,提供了获取系统信息、控制程序生命周期、操作标准流等关键能力。

无论是日常开发中的控制台输出,还是性能优化时的执行时间统计,System 类都扮演着不可或缺的角色。本文将从核心字段、常用方法、实际场景三个维度,带你彻底掌握 System 类的用法。

一、先搞懂:System 类的"特殊身份"

在学习具体功能前,先明确 System 类的设计逻辑:

  • 无需实例化System 类的构造方法被声明为 private,禁止外部创建对象,所有功能都通过静态字段静态方法 提供,直接用 System.xxx 调用即可。
  • 与系统强绑定:它封装了 JVM 与操作系统交互的底层逻辑,比如获取系统时间、操作标准输入输出流、控制虚拟机退出等,是 Java 程序与系统资源沟通的"直接通道"。

二、核心字段:3个常用的"系统流"

System 类提供了三个静态字段,分别对应系统的标准输入、输出和错误输出流,是日常开发中"输入输出"的基础。

字段名 类型 作用说明 默认关联设备
System.in InputStream 标准输入流:用于从外部读取数据(比如接收用户输入),是字节流的源头。 键盘
System.out PrintStream 标准输出流:用于向外部打印"正常信息"(比如日志、结果展示),支持字符输出。 控制台
System.err PrintStream 标准错误输出流:专门打印"错误信息"(比如异常堆栈、程序报错),优先级更高。 控制台

关键区别:System.out 与 System.err

很多初学者会混淆这两个输出流,它们虽然默认都打印到控制台,但在用途行为上有本质区别:

  1. 用途不同(语义区分):

    • System.out:打印"正常业务信息",比如程序运行结果、调试日志(如 System.out.println("计算完成"))。
    • System.err:仅打印"错误信息",比如捕获异常时输出堆栈(如 e.printStackTrace() 本质就是通过 System.err 输出)。
  2. 输出优先级不同

    • System.out 可能有缓冲(需积累一定数据才输出),而 System.err 通常无缓冲,错误信息会"立即输出"。

    • 示例:当两者同时打印时,错误信息可能比正常信息更早显示(即使代码中 System.out 写在前面):

      java 复制代码
      public class SystemStreamDemo {
          public static void main(String[] args) {
              System.out.println("这是正常输出"); // 可能后显示
              System.err.println("这是错误输出"); // 可能先显示
          }
      }
  3. 实际应用场景

    • 开发时,用 System.out 做临时调试;上线后,错误信息需通过 System.err 定向到日志文件(便于排查问题),而正常信息可单独处理。

三、常用方法:4个高频场景实战

System 类的方法不多,但每一个都很实用,以下是开发中最常用的4个方法,结合场景讲解用法。

1. currentTimeMillis():获取系统时间(毫秒级)

方法定义
java 复制代码
public static long currentTimeMillis()

返回从1970年1月1日 00:00:00 UTC (称为"历元时间"或"时间戳起点")到当前时刻的毫秒数 (1秒 = 1000毫秒),返回值类型为 long(避免时间溢出)。

3个核心应用场景
场景1:统计代码执行耗时

这是最常用的场景!通过"执行前记录时间 → 执行后记录时间 → 计算差值",即可得到代码块的运行时间,用于性能分析。

示例:统计"计算1到100万的和"所需时间

java 复制代码
public class TimeDemo {
    public static void main(String[] args) {
        // 1. 记录代码执行前的时间
        long startTime = System.currentTimeMillis();
        
        // 2. 执行要测试的代码
        int sum = 0;
        for (int i = 1; i <= 1_000_000; i++) {
            sum += i;
        }
        
        // 3. 记录代码执行后的时间
        long endTime = System.currentTimeMillis();
        
        // 4. 计算耗时(毫秒)
        System.out.println("1到100万的和:" + sum);
        System.out.println("代码执行耗时:" + (endTime - startTime) + " ms");
    }
}

注意:如果代码执行时间极短(比如微秒级),可改用 System.nanoTime()(返回纳秒级时间,1毫秒 = 1000000纳秒),精度更高。

场景2:生成"唯一临时标识"

利用时间戳的唯一性,可生成简单的临时ID(如订单号前缀、临时文件名),避免重复。

示例:生成临时文件名

java 复制代码
String tempFileName = "temp_" + System.currentTimeMillis() + ".txt";
System.out.println("临时文件名:" + tempFileName); 
// 输出示例:temp_1696000000000.txt
场景3:转换为"可读时间"

currentTimeMillis() 返回的是毫秒数,可通过 SimpleDateFormat 或 Java 8+ 的 LocalDateTime 转换为"年-月-日 时:分:秒"格式。

示例(Java 8+ 写法):

java 复制代码
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

public class TimeConvertDemo {
    public static void main(String[] args) {
        // 1. 获取当前时间戳(毫秒)
        long timeMillis = System.currentTimeMillis();
        
        // 2. 转换为LocalDateTime(需指定时区,避免时区偏差)
        LocalDateTime dateTime = LocalDateTime.ofInstant(
            Instant.ofEpochMilli(timeMillis), 
            ZoneId.systemDefault() // 系统默认时区(如Asia/Shanghai)
        );
        
        // 3. 格式化输出
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String readableTime = dateTime.format(formatter);
        System.out.println("当前时间:" + readableTime); 
        // 输出示例:2024-05-20 14:30:00
    }
}

2. exit(int status):强制退出虚拟机

方法定义
java 复制代码
public static void exit(int status)
  • 作用:立即终止 Java 虚拟机(JVM),程序后续代码不再执行。
  • 参数 status :退出状态码,0 表示"正常终止",非 0 表示"异常终止"(具体值可自定义,用于告知调用者程序退出原因)。
应用场景:程序需要"主动终止"的场景

比如用户输入"退出"指令时,或检测到致命错误(如配置文件缺失)时,主动终止程序。

示例:检测到错误时退出程序

java 复制代码
public class ExitDemo {
    public static void main(String[] args) {
        // 模拟检测配置文件
        boolean configExists = checkConfig();
        if (!configExists) {
            System.err.println("错误:配置文件缺失,程序无法运行!");
            System.exit(1); // 非0状态码,标识异常终止
        }
        
        // 如果上面执行了exit,下面代码不会执行
        System.out.println("程序正常启动...");
    }
    
    // 模拟检查配置文件
    private static boolean checkConfig() {
        return false; // 模拟配置文件不存在
    }
}

注意:exit() 是"强制退出",会跳过 finally 块(如果 finally 中没有 exit() 本身),使用时需确保资源已释放(如关闭文件流、数据库连接)。

3. gc():建议执行垃圾回收

方法定义
java 复制代码
public static void gc()
  • 作用:向 JVM 发送"垃圾回收建议",告知 JVM"当前可以回收无用对象的内存"。
  • 关键注意点gc() 是"建议"而非"强制"------JVM 会根据自身的内存管理策略(如内存使用率、GC 算法)决定是否立即执行垃圾回收,开发者无法精确控制。
应用场景:内存敏感场景的"辅助回收"

比如程序执行完一个内存消耗极大的任务(如处理大量临时对象)后,可调用 gc() 建议回收内存,避免后续操作内存不足。

示例:处理大量临时对象后建议GC

java 复制代码
public class GCDemo {
    public static void main(String[] args) {
        // 模拟创建大量临时对象(消耗内存)
        for (int i = 0; i < 10_000; i++) {
            new Object(); // 临时对象,使用后无引用,成为"垃圾"
        }
        
        // 任务完成后,建议JVM回收垃圾
        System.gc();
        System.out.println("已建议JVM执行垃圾回收");
    }
}

误区提醒:不要依赖 gc() 解决内存泄漏问题!如果对象存在无效引用(如静态集合未清空),即使调用 gc(),这些对象也无法被回收。gc() 仅能回收"无引用的对象"。

4. arraycopy():高效复制数组

方法定义
java 复制代码
public static void arraycopy(
    Object src,      // 源数组(要复制的数组)
    int srcPos,      // 源数组的起始复制位置
    Object dest,     // 目标数组(复制到的数组)
    int destPos,     // 目标数组的起始存放位置
    int length       // 要复制的元素个数
)
  • 作用 :从源数组的指定位置,复制指定长度的元素到目标数组的指定位置,是 Java 中复制数组的"底层高效方法"(比 for 循环手动复制快,因为是 native 方法,直接调用操作系统底层)。
应用场景:数组扩容、元素迁移

比如 ArrayList 的扩容逻辑,底层就是通过 System.arraycopy() 实现旧数组到新数组的复制。

示例:复制数组的部分元素

java 复制代码
public class ArrayCopyDemo {
    public static void main(String[] args) {
        // 源数组:[1, 2, 3, 4, 5]
        int[] srcArray = {1, 2, 3, 4, 5};
        // 目标数组:长度为3的空数组
        int[] destArray = new int[3];
        
        // 复制源数组的第2个元素(索引1)开始的3个元素,到目标数组的第0个位置
        System.arraycopy(srcArray, 1, destArray, 0, 3);
        
        // 打印目标数组
        for (int num : destArray) {
            System.out.print(num + " "); // 输出:2 3 4
        }
    }
}

四、扩展:其他实用方法

除了上述高频方法,System 类还有两个常用的"系统信息相关方法":

方法名 作用说明
System.getProperties() 获取当前系统的所有属性(如操作系统名称、Java版本、用户目录等),返回 Properties 对象。
System.getProperty(String key) 根据指定的"属性键",获取对应的系统属性值(如 System.getProperty("os.name") 获取操作系统名称)。

示例:获取系统关键信息

java 复制代码
public class SystemInfoDemo {
    public static void main(String[] args) {
        // 获取操作系统名称
        String osName = System.getProperty("os.name");
        // 获取Java版本
        String javaVersion = System.getProperty("java.version");
        // 获取用户的主目录
        String userHome = System.getProperty("user.home");
        
        System.out.println("操作系统:" + osName);      // 输出:Windows 10 或 macOS...
        System.out.println("Java版本:" + javaVersion); // 输出:1.8.0_301 或 17.0.1...
        System.out.println("用户主目录:" + userHome);  // 输出:C:\Users\XXX 或 /Users/XXX...
    }
}

五、总结:System 类的核心价值

System 类虽然简单,但却是 Java 程序与系统交互的"基石",其核心价值在于:

  1. 简化系统交互:无需关注底层实现,直接通过静态成员操作标准流、获取系统时间、控制虚拟机。
  2. 提升开发效率 :提供 arraycopy() 等高效方法,避免重复造轮子。
  3. 辅助调试与优化 :通过 currentTimeMillis() 统计性能,通过 gc() 辅助内存管理。

掌握 System 类的用法,能让你在日常开发中更高效地处理输入输出、时间统计、程序控制等场景,是每个 Java 开发者的必备基础。

相关推荐
counting money11 分钟前
Spring框架基础(配置篇)
java·后端·spring
秋91 小时前
OceanBase与GreatSQL在Java应用中的性能调优方法有哪些?
java·开发语言·oceanbase
今天又在写代码1 小时前
并发问题解决
java·开发语言·数据库
老王以为1 小时前
前端视角下的 Java
java·javascript·程序员
看腻了那片水1 小时前
开源一个对业务代码零侵入的透明数据治理框架 —— 【sangsang】
java·mybatis
Nyarlathotep01131 小时前
JUC工具(3):StampedLock的基础和原理
java·后端
呱牛do it2 小时前
企业级门户网站设计与实现:基于SpringBoot + Vue3的全栈解决方案(Day 7)
java·vue
NE_STOP2 小时前
Redis--SDS字符串与集合的底层实现原理
java
直奔標竿2 小时前
Java开发者AI转型第二十二课!Spring AI 个人知识库实战(一)——架构搭建与核心契约落地
java·人工智能·后端·spring·架构