java.lang.System 是 Java 核心类库中的一个工具类,提供了一系列与系统环境、输入输出、时间、属性等相关的静态方法。它在 Java 程序中使用极为频繁,是开发者必须掌握的基础类之一。


一、核心概念

1. 类定义

  • 包路径java.lang.System
  • 访问修饰public final class System
  • 不可继承final 类,不能被继承
  • 无实例:构造方法为私有,不能实例化
  • 静态方法为主:所有方法均为 static,直接通过类名调用

2. 核心功能

  • 标准输入/输出/错误流in, out, err
  • 系统属性与环境变量getProperty, getenv
  • 时间与计时currentTimeMillis, nanoTime
  • 数组复制arraycopy
  • JVM 操作exit, gc, runFinalization
  • 系统信息getProperties, lineSeparator

二、操作步骤(详细)

1. 使用标准输入流读取用户输入

步骤:

  1. 导入 java.util.Scanner
  2. 创建 Scanner 实例,传入 System.in
  3. 调用 nextXXX() 方法读取不同类型数据
  4. 使用后关闭 Scanner
import java.util.Scanner;

public class InputExample {
    public static void main(String[] args) {
        // 步骤1:创建 Scanner 对象
        Scanner scanner = new Scanner(System.in);
        
        // 步骤2:提示用户输入
        System.out.print("请输入姓名:");
        
        // 步骤3:读取字符串
        String name = scanner.nextLine();
        
        System.out.print("请输入年龄:");
        // 步骤4:读取整数
        int age = scanner.nextInt();
        
        // 步骤5:输出结果
        System.out.println("姓名:" + name + ",年龄:" + age);
        
        // 步骤6:关闭资源
        scanner.close();
    }
}

⚠️ 注意:nextInt() 不会读取换行符,后续 nextLine() 会立即返回空字符串,建议使用 nextLine() 读取后转换类型。


2. 输出信息到控制台

步骤:

  1. 使用 System.out.println() 输出并换行
  2. 使用 System.out.print() 输出不换行
  3. 使用 System.out.printf() 格式化输出
public class OutputExample {
    public static void main(String[] args) {
        // 步骤1:输出字符串
        System.out.println("Hello, World!");
        
        // 步骤2:不换行输出
        System.out.print("Java ");
        System.out.print("Programming\n");
        
        // 步骤3:格式化输出
        String name = "Alice";
        int score = 95;
        System.out.printf("学生:%s,成绩:%d%n", name, score);
    }
}

3. 获取系统属性

步骤:

  1. 调用 System.getProperty(key)
  2. 可选:提供默认值 System.getProperty(key, defaultValue)
  3. 常用属性键名:
public class PropertyExample {
    public static void main(String[] args) {
        // 步骤1:获取 Java 版本
        String javaVersion = System.getProperty("java.version");
        System.out.println("Java 版本:" + javaVersion);
        
        // 步骤2:获取操作系统名称
        String osName = System.getProperty("os.name");
        System.out.println("操作系统:" + osName);
        
        // 步骤3:获取用户主目录
        String userHome = System.getProperty("user.home");
        System.out.println("用户主目录:" + userHome);
        
        // 步骤4:获取自定义属性(启动时设置:-Dmy.app.name=MyApp)
        String appName = System.getProperty("my.app.name", "DefaultApp");
        System.out.println("应用名称:" + appName);
    }
}

4. 获取环境变量

步骤:

  1. 使用 System.getenv() 获取所有环境变量(Map)
  2. 使用 System.getenv(key) 获取单个变量
import java.util.Map;

public class EnvExample {
    public static void main(String[] args) {
        // 步骤1:获取 PATH 环境变量
        String path = System.getenv("PATH");
        System.out.println("PATH: " + path);
        
        // 步骤2:获取所有环境变量
        Map<String, String> env = System.getenv();
        for (String key : env.keySet()) {
            System.out.println(key + " = " + env.get(key));
        }
    }
}

⚠️ 注意:Windows 与 Linux 环境变量名大小写敏感性不同。


5. 测量代码执行时间

步骤(推荐使用 nanoTime):

  1. 调用 System.nanoTime() 记录开始时间
  2. 执行目标代码
  3. 再次调用 System.nanoTime() 记录结束时间
  4. 计算差值
public class TimeExample {
    public static void main(String[] args) {
        // 步骤1:记录开始时间(纳秒)
        long startTime = System.nanoTime();
        
        // 步骤2:执行耗时操作
        for (int i = 0; i < 1000000; i++) {
            Math.sqrt(i);
        }
        
        // 步骤3:记录结束时间
        long endTime = System.nanoTime();
        
        // 步骤4:计算耗时(毫秒)
        long durationMs = (endTime - startTime) / 1_000_000;
        System.out.println("执行耗时:" + durationMs + " 毫秒");
    }
}

✅ 使用 nanoTime 而非 currentTimeMillis,因为前者精度更高,不受系统时钟调整影响。


6. 数组复制(高效方式)

步骤:

  1. 调用 System.arraycopy()
  2. 指定源数组、源位置、目标数组、目标位置、长度
public class ArrayCopyExample {
    public static void main(String[] args) {
        int[] src = {1, 2, 3, 4, 5};
        int[] dest = new int[5];
        
        // 步骤1:复制整个数组
        System.arraycopy(src, 0, dest, 0, src.length);
        
        // 步骤2:复制部分元素(从索引2开始,复制3个)
        int[] partial = new int[3];
        System.arraycopy(src, 2, partial, 0, 3);
        
        // 输出验证
        System.out.println("完整复制:" + java.util.Arrays.toString(dest));
        System.out.println("部分复制:" + java.util.Arrays.toString(partial));
    }
}

7. 正常退出 JVM

步骤:

  1. 调用 System.exit(status)
  2. status = 0 表示正常退出
  3. status ≠ 0 表示异常退出
public class ExitExample {
    public static void main(String[] args) {
        try {
            // 正常逻辑
            System.out.println("程序执行中...");
        } catch (Exception e) {
            System.err.println("发生错误:" + e.getMessage());
            // 步骤:异常退出
            System.exit(1);
        }
        
        // 步骤:正常退出
        System.exit(0);
    }
}

⚠️ 注意:System.exit() 会终止整个 JVM,慎用于 Web 应用或容器环境。


三、常见错误

错误 原因 解决方案
NullPointerException 使用 System.out JVM 未正确初始化或 out 被重定向为 null 检查 JVM 状态,避免手动设置 System.setOut(null)
读取输入时跳过数据 nextInt()/nextDouble() 不读取换行符 使用 nextLine() 读取后转换,或在 nextInt() 后调用 nextLine() 清空缓冲区
SecurityException 调用 exit() 安全管理器禁止退出 检查安全管理器策略,或在受限环境中避免使用
时间测量不准确 使用 currentTimeMillis() 改用 System.nanoTime()
数组复制越界 源/目标位置 + 长度 超出数组边界 检查数组长度和参数范围

四、注意事项

  1. System 类不可实例化:直接调用静态方法。
  2. outerr 是打印流System.out 用于正常输出,System.err 用于错误信息(通常红色显示)。
  3. 线程安全System.out.println() 是线程安全的,但多线程输出可能交错。
  4. 重定向流:可通过 System.setIn(), System.setOut(), System.setErr() 重定向流(如重定向到文件)。
  5. 环境变量 vs 系统属性
    • 环境变量:由操作系统设置,全局有效
    • 系统属性:JVM 启动时设置(-Dkey=value),或通过代码设置
  6. gc() 不保证立即执行:调用 System.gc() 仅建议 JVM 进行垃圾回收,不强制执行。

五、使用技巧

  1. 快速获取换行符

    String lineSep = System.lineSeparator(); // 比 "\n" 更跨平台
    
  2. 批量设置系统属性

    System.setProperty("http.proxyHost", "proxy.example.com");
    System.setProperty("http.proxyPort", "8080");
    
  3. 临时重定向输出

    PrintStream originalOut = System.out;
    System.setOut(new PrintStream("output.log"));
    System.out.println("这将写入文件");
    System.setOut(originalOut); // 恢复
    
  4. 性能测试模板

    long start = System.nanoTime();
    // 代码块
    long elapsed = System.nanoTime() - start;
    System.out.printf("耗时: %.3f ms%n", elapsed / 1_000_000.0);
    

六、最佳实践与性能优化

  1. 优先使用 nanoTime 进行性能测量

    • 高精度、单调递增、不受系统时钟调整影响。
  2. 避免在循环中频繁调用 System.out.println

    • 大量输出时,考虑使用 StringBuilder 缓冲后一次性输出。
  3. 使用 arraycopy 替代手动循环复制

    • System.arraycopy() 是本地方法,性能远高于 Java 循环。
  4. 错误信息使用 System.err

    • 便于用户区分正常输出与错误信息,尤其在重定向时。
  5. 合理使用系统属性配置应用

    • 启动时通过 -Dconfig.file=path/to/config.properties 传递配置。
  6. 避免滥用 System.exit()

    • 在 Web 应用、微服务中应抛出异常或返回错误码,由容器处理。
  7. 环境变量用于敏感信息

    • 如数据库密码、API 密钥,避免硬编码。

总结

System 类是 Java 开发的基石工具,掌握其核心方法和最佳实践,能显著提升开发效率和程序健壮性。重点掌握:

  • 标准 I/O 流的使用
  • 系统属性与环境变量的获取
  • 高精度时间测量
  • 高效数组复制
  • 安全的 JVM 退出