【Java常用API】-----System 与 标准 I/O流
1. System 类
1.1 System 类简介
System 是 Java 中一个非常重要的工具类,提供了与操作系统和运行环境相关的实用方法。它封装了系统级的操作,如退出程序、获取时间戳、数组拷贝等,是开发中频繁使用的内置类。
与 Math 类类似,System 类也遵循“工具类”设计模式:
特点:
- 私有化构造方法:不能通过
new创建对象; - 所有方法均为静态:可以直接通过类名调用,例如
System.exit(0)。 - 高度优化:底层直接对接操作系统或 JVM 内部机制;
- 线程安全:大多数方法在多线程环境下可安全使用。
注意:
System.out.println()实际上是调用了PrintStream的输出流,而System.out是标准输出流的引用,指向控制台。同理,System.in是标准输入流,通常对应键盘输入;System.err是标准错误流,专门用于报告异常信息。
1.2 System 类的常用方法
以下是 System 类中最常用的几个静态方法及其说明:
| 方法名 | 说明 |
|---|---|
public static void exit(int status) | 终止当前运行的 Java 虚拟机 |
public static long currentTimeMillis() | 返回从 1970 年 1 月 1 日 00:00:00 UTC 到现在的毫秒数 |
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) | 高效地复制数组元素 |
public static final InputStream in | 标准输入流(通常为键盘) |
public static final PrintStream out | 标准输出流(通常为控制台) |
public static final PrintStream err | 标准错误流(用于输出错误信息) |
注意:
in、out、err是System类的静态字段,而非方法,但它们构成了 Java I/O 的基础入口。
1.3 具体方法详解与示例
1.3.1 exit() - 终止虚拟机
System.exit(int status) 方法用于立即终止当前正在运行的 Java 虚拟机,并返回一个状态码给操作系统。
1.3.1.1 基本用法示例:
public class demo {public static void main(String[] args) {System.out.println("程序开始执行...");System.exit(0); // 正常退出System.out.println("这行不会被执行"); // 不会打印}
}
输出:
1.3.1.2 参数含义:status
status = 0:表示程序正常结束;status ≠ 0:表示程序异常或错误退出,通常用于通知外部进程出错。
示例:异常退出
public class demo {public static void main(String[] args) {boolean someCondition = true;if (someCondition) {System.err.println("配置文件缺失!");System.exit(1); // 错误码 1 表示配置问题}}
}
输出:
⚠️ 注意:
System.exit()会强制关闭所有线程、资源和 I/O 流,可能导致未保存的数据丢失;- 在 GUI 或服务器应用中应谨慎使用,避免服务中断;
- 使用时建议先清理资源(如关闭数据库连接),再调用
exit()。
1.3.2 currentTimeMillis() - 获取时间戳
System.currentTimeMillis() 是 Java 中最常用的时间测量工具,返回自Unix 时间原点以来的毫秒数。
1.3.2.1 时间原点(Epoch)
Unix 时间原点:1970年1月1日 00:00:00 UTC(协调世界时)
由于中国位于东八区(UTC+8),因此:
- 当前北京时间比 UTC 时间快 8 小时;
- 比如:当 UTC 是 1970-01-01 00:00:00 时,北京是 1970-01-01 08:00:00。
历史背景:
1969年8月,贝尔实验室程序员肯·汤普逊(Ken Thompson)利用妻子离开一个月的机会,在老旧的 PDP-7 机器上开发出了 Unix 的早期版本。随后他与丹尼斯·里奇(Dennis Ritchie)共同创造了 C 语言,并重写了 UNIX。
1970年1月1日 被定为 Unix 时间的起点,也被称为“C语言的生日”。
1.3.2.2 单位换算关系
| 单位 | 换算关系 |
|---|---|
| 1 秒 | = 1000 毫秒 |
| 1 毫秒 | = 1000 微秒 |
| 1 微秒 | = 1000 纳秒 |
| 更小单位 | 皮秒、飞秒、阿托秒、仄秒、幺秒(用于科学计算) |
1.3.2.3 基本用法示例:
public class demo {public static void main(String[] args) {long startTime = System.currentTimeMillis();// 执行一段耗时操作for (int i = 0; i < 1000000; i++) {Math.sqrt(i);}long endTime = System.currentTimeMillis();System.out.println("起始时间:"+startTime + " 毫秒");System.out.println("结束时间:"+endTime+" 毫秒");System.out.println("耗时:" + (endTime - startTime) + " 毫秒");}
}
输出:
优点:
- 简单高效,适合性能测试;
- 不依赖外部库,无需引入额外依赖;
- 支持跨平台一致性。
1.3.3 arraycopy() - 数组拷贝
System.arraycopy() 是 Java 中最快、最高效的数组复制方法,其底层由本地代码实现(native),性能远超手动循环。
1.3.3.1 方法签名 与 参数说明:
public static void arraycopy(Object src, // 源数组int srcPos, // 源数组起始索引Object dest, // 目标数组int destPos, // 目标数组起始索引int length // 复制元素个数
)
| 参数 | 说明 |
|---|---|
src | 源数组,必须是非 null |
srcPos | 源数组中开始复制的位置(包含) |
dest | 目标数组,必须足够大以容纳复制的数据 且非null |
destPos | 目标数组中开始写入的位置(包含) |
length | 要复制的元素数量,必须 ≥ 0 |
1.3.3.2 基本用法示例:
public class demo {public static void main(String[] args) {int[] source = {1, 2, 3, 4, 5};int[] target = new int[10];// 将 source 数组的第 1~3 个元素(即 2,3,4)复制到 target 的第 2~4 个位置System.arraycopy(source, 1, target, 2, 3);// 输出 target 数组for (int x : target) {System.out.print(x + " ");}}
}
输出:
1.3.3.3 进阶用法示例:引用类型与类型兼容性
System.arraycopy() 不仅支持基本类型数组(如 int[]、double[]),也支持引用类型数组(如 String[]、自定义对象数组等)。在引用类型场景下,Java 的多态机制允许一种特殊的类型转换:
当 源数组 和 目标数组 都是 引用类型数组时,只要源数组 元素的类型 是 目标数组 组件类型 的子类型(或相同类型),复制操作就是合法的。
即:子类数组 → 父类数组(合法)
注意:父类数组 → 子类数组(非法)
这体现了 Java 数组的协变(Covariance)特性——子类数组可视为父类数组的子类型。
示例:
父类:
public class Person {private String name;private int age;public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}
子类:
public class Student extends Person{private String className;public Student() {super();}public Student(String name, int age, String className) {super(name, age);this.className = className;}public String getClassName() {return className;}public void setClassName(String className) {this.className = className;}
}
测试类:
public class demo6 {public static void main(String[] args) {Student s1 = new Student("张三", 18, "1班");Student s2 = new Student("李四", 24, "2班");Student s3 = new Student("王五", 20, "3班");Student[] arr1 = {s1, s2, s3}; // 子类数组(Student 是 Person 的子类)Person[] arr2 = new Person[3]; // 父类数组//合法:将子类数组拷贝到父类数组(向上转型,类型兼容)System.arraycopy(arr1, 0, arr2, 0, 3);for (int i = 0; i < arr2.length; i++) {// 需要显式向下转型:因为 arr2[i] 编译时类型是 Person,实际是 StudentStudent s = (Student) arr2[i];System.out.println(s.getName() + "---" + s.getAge() + "---" + s.getClassName());}}
}
输出:
1.3.3.4 常见异常
如果参数不合法,会抛出以下异常:
| 异常类型 | 触发条件 |
|---|---|
NullPointerException | src 或 dest 为 null |
ArrayIndexOutOfBoundsException | srcPos < 0、destPos < 0、length < 0 或超出数组边界 |
ArrayStoreException | 源数组和目标数组类型不兼容(如把 int[] 复制到 String[]) |
示例:NULL异常
public class demo6 {public static void main(String[] args) {try {int[] a = {1, 2, 3};int[] b = null; // 目标数组为 nullSystem.arraycopy(a, 0, b, 0, 2);} catch (Exception e) {System.err.println("数组拷贝失败:" + e.getMessage());}}
}
输出:
示例:越界异常:
public class demo6 {public static void main(String[] args) {try {int[] a = {1, 2, 3};int[] b = new int[2];System.arraycopy(a, 0, b, 0, 3); // 试图复制 3 个元素,但 b 只有 2 个空间} catch (Exception e) {System.err.println("数组拷贝失败:" + e.getMessage());}}
}
输出:
示例:不兼容异常
public class demo6 {public static void main(String[] args) {try {int[] src = {10, 20, 30};String[] dest = new String[3]; // 类型不匹配:int[] → String[]System.arraycopy(src, 0, dest, 0, 3);} catch (Exception e) {System.err.println("数组拷贝失败:" + e.getMessage());}}
}
输出:
1.3.4 标准输入/输出流
| 字段 | 说明 |
|---|---|
System.in | 标准输入流(键盘输入) |
System.out | 标准输出流(控制台输出) |
System.err | 标准错误流(用于输出错误信息) |
这些流是 Java I/O 体系的起点,也是命令行交互的基础。
1.3.5 标准输入与输出流详解(System.in / System.out / System.err)
Java 程序默认与操作系统通过三个标准流进行通信,System 类将这三个流封装为静态字段,使得开发者可以轻松实现人机交互和日志输出。
1.3.5.1 System.out —— 标准输出流
- 类型:
PrintStream - 用途:输出程序的正常结果、提示信息、调试日志等。
- 默认指向:控制台(终端)
示例:
System.out.print("欢迎使用计算器!\n");
System.out.println("请输入第一个数字:");
技巧:
System.out是线程安全的,但频繁调用会影响性能。在高性能日志系统中,建议使用BufferedWriter或日志框架(如 Log4j、SLF4J)。
1.3.5.2 System.err —— 标准错误流
- 类型:
PrintStream - 用途:专门用于输出错误、异常、警告等非正常信息
- 默认指向:控制台(通常以红色高亮显示)
示例:
if (args.length == 0) {System.err.println("错误:缺少命令行参数!");System.exit(1);
}
注意:
- IDE(如 IntelliJ IDEA、Eclipse)通常将
err内容标红,便于快速定位问题。
所有异常堆栈、配置错误、用户输入错误都应通过System.err输出,而不是System.out。
1.3.5.3 System.in —— 标准输入流
- 类型:
InputStream - 用途:读取用户的键盘输入或管道输入
- 默认来源:键盘
由于 System.in 是字节流,通常需要包装为字符流或使用 Scanner 简化操作。
示例:使用 Scanner 读取输入
import java.util.Scanner;public class InputDemo {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);System.out.print("请输入您的姓名:");String name = scanner.nextLine(); // 读取一行System.out.print("请输入年龄:");int age = scanner.nextInt(); // 读取整数System.out.println("您好," + name + ",您今年 " + age + " 岁。");scanner.close(); // 关闭资源}
}
注意:
System.in是阻塞式输入,程序会等待用户输入;- 在服务器或自动化环境中,
System.in可能被重定向为文件或网络流;- 使用完毕后建议关闭
Scanner,避免资源泄漏。








