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. 使用标准输入流读取用户输入
步骤:
- 导入
java.util.Scanner
- 创建
Scanner
实例,传入System.in
- 调用
nextXXX()
方法读取不同类型数据 - 使用后关闭
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. 输出信息到控制台
步骤:
- 使用
System.out.println()
输出并换行 - 使用
System.out.print()
输出不换行 - 使用
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. 获取系统属性
步骤:
- 调用
System.getProperty(key)
- 可选:提供默认值
System.getProperty(key, defaultValue)
- 常用属性键名:
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. 获取环境变量
步骤:
- 使用
System.getenv()
获取所有环境变量(Map) - 使用
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
):
- 调用
System.nanoTime()
记录开始时间 - 执行目标代码
- 再次调用
System.nanoTime()
记录结束时间 - 计算差值
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. 数组复制(高效方式)
步骤:
- 调用
System.arraycopy()
- 指定源数组、源位置、目标数组、目标位置、长度
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
步骤:
- 调用
System.exit(status)
status = 0
表示正常退出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() |
数组复制越界 | 源/目标位置 + 长度 超出数组边界 | 检查数组长度和参数范围 |
四、注意事项
System
类不可实例化:直接调用静态方法。out
和err
是打印流:System.out
用于正常输出,System.err
用于错误信息(通常红色显示)。- 线程安全:
System.out.println()
是线程安全的,但多线程输出可能交错。 - 重定向流:可通过
System.setIn()
,System.setOut()
,System.setErr()
重定向流(如重定向到文件)。 - 环境变量 vs 系统属性:
- 环境变量:由操作系统设置,全局有效
- 系统属性:JVM 启动时设置(
-Dkey=value
),或通过代码设置
gc()
不保证立即执行:调用System.gc()
仅建议 JVM 进行垃圾回收,不强制执行。
五、使用技巧
快速获取换行符:
String lineSep = System.lineSeparator(); // 比 "\n" 更跨平台
批量设置系统属性:
System.setProperty("http.proxyHost", "proxy.example.com"); System.setProperty("http.proxyPort", "8080");
临时重定向输出:
PrintStream originalOut = System.out; System.setOut(new PrintStream("output.log")); System.out.println("这将写入文件"); System.setOut(originalOut); // 恢复
性能测试模板:
long start = System.nanoTime(); // 代码块 long elapsed = System.nanoTime() - start; System.out.printf("耗时: %.3f ms%n", elapsed / 1_000_000.0);
六、最佳实践与性能优化
优先使用
nanoTime
进行性能测量:- 高精度、单调递增、不受系统时钟调整影响。
避免在循环中频繁调用
System.out.println
:- 大量输出时,考虑使用
StringBuilder
缓冲后一次性输出。
- 大量输出时,考虑使用
使用
arraycopy
替代手动循环复制:System.arraycopy()
是本地方法,性能远高于 Java 循环。
错误信息使用
System.err
:- 便于用户区分正常输出与错误信息,尤其在重定向时。
合理使用系统属性配置应用:
- 启动时通过
-Dconfig.file=path/to/config.properties
传递配置。
- 启动时通过
避免滥用
System.exit()
:- 在 Web 应用、微服务中应抛出异常或返回错误码,由容器处理。
环境变量用于敏感信息:
- 如数据库密码、API 密钥,避免硬编码。
总结
System
类是 Java 开发的基石工具,掌握其核心方法和最佳实践,能显著提升开发效率和程序健壮性。重点掌握:
- 标准 I/O 流的使用
- 系统属性与环境变量的获取
- 高精度时间测量
- 高效数组复制
- 安全的 JVM 退出