java.lang.System是Java核心类库中系统级操作的入口,它提供了一系列静态方法和变量,用于访问与操作系统、JVM相关的系统资源和执行系统级操作。System类被设计为不可实例化、不可继承的工具类,其功能覆盖标准IO流、系统属性与环境变量、数组拷贝、JVM退出、垃圾回收触发、时间获取等核心场景,是Java程序与底层系统交互的重大桥梁。

本文将从System类的核心特性、核心功能模块(附源码与示例)、关键注意事项及应用场景等维度解析System类。
一、System类的核心特性与类结构
1. 类的基本特性
System类位于java.lang包下,其源码定义体现了典型的工具类设计范式:
public final class System {
// 私有构造方法:禁止实例化
private System() {}
// 静态成员变量与方法...
}
核心特性总结:
|
特性 |
说明 |
|
final修饰 |
无法被继承,避免子类重写系统级方法导致安全问题 |
|
私有构造方法 |
无法通过new关键字实例化,所有功能通过静态成员访问 |
|
静态成员为主 |
所有变量(如in/out/err)和方法(如getProperty、arraycopy)均为静态 |
|
与JVM深度绑定 |
部分方法(如gc、exit)直接调用JVM底层接口,具有系统级权限 |
|
线程安全 |
大部分方法是线程安全的(如getProperty、arraycopy),但部分操作(如setOut)非线程安全 |
2. 核心依赖与初始化
System类的初始化由JVM在启动时完成,其中核心的静态变量(如in、out、err)会被JVM初始化为与控制台关联的流对象。此外,System类依赖java.lang.Runtime类(JVM运行时环境),部分方法(如gc()、exit())本质是调用Runtime.getRuntime()的对应方法。
二、System类的核心功能模块
模块1:标准IO流(in、out、err)
System类提供了三个静态的流对象,对应标准输入、标准输出和标准错误输出,是Java程序与控制台交互的基础。
1. 核心变量定义
// 标准输入流(默认关联键盘输入,字节流)
public static final InputStream in;
// 标准输出流(默认关联控制台输出,字符流包装后的打印流)
public static final PrintStream out;
// 标准错误输出流(默认关联控制台,用于输出错误信息,优先级高于out)
public static final PrintStream err;
2. 关键说明与示例
- System.in:字节输入流,默认读取键盘输入,可通过System.setIn(InputStream in)重定向(如读取文件内容);
- System.out:打印流,用于输出普通信息,可通过System.setOut(PrintStream out)重定向(如输出到文件);
- System.err:打印流,用于输出错误信息,输出时不会被缓存(与out的缓冲特性不同),可通过System.setErr(PrintStream err)重定向。
示例1:标准IO的基本使用
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class SystemIODemo {
public static void main(String[] args) {
// 1. System.out输出普通信息
System.out.println("请输入您的姓名:");
// 2. System.in读取键盘输入(需包装为字符流)
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
String name = br.readLine();
System.out.println("您好," + name);
// 3. System.err输出错误信息
if (name.isEmpty()) {
System.err.println("错误:姓名不能为空!");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
示例2:IO流重定向(输出到文件)
import java.io.FileOutputStream;
import java.io.PrintStream;
public class SystemRedirectDemo {
public static void main(String[] args) throws Exception {
// 保存原有的out流
PrintStream originalOut = System.out;
// 重定向out到文件
System.setOut(new PrintStream(new FileOutputStream("output.txt")));
System.out.println("这段内容会输出到文件中");
// 恢复原有的out流
System.setOut(originalOut);
System.out.println("这段内容会输出到控制台");
// 重定向err到文件
System.setErr(new PrintStream(new FileOutputStream("error.txt")));
System.err.println("这段错误信息会输出到error.txt");
}
}
模块2:系统属性(Properties)
System类提供了访问和修改JVM系统属性的方法,系统属性是键值对形式的配置信息,包含JVM版本、操作系统类型、用户目录、classpath等核心信息。
1. 核心方法
|
方法 |
说明 |
|
static Properties getProperties() |
获取所有系统属性的Properties对象(包含默认属性和自定义属性) |
|
static String getProperty(String key) |
根据键获取单个系统属性值,不存在则返回null |
|
static String getProperty(String key, String def) |
根据键获取属性值,不存在则返回默认值def |
|
static void setProperty(String key, String value) |
设置自定义系统属性(键值对) |
|
static void clearProperty(String key) |
移除指定键的系统属性(JDK 1.5+) |
2. 常用系统属性键
|
键名 |
含义 |
|
java.version |
Java运行时环境版本(如1.8.0_301) |
|
java.home |
Java安装目录(如/opt/jdk1.8.0_301/jre) |
|
os.name |
操作系统名称(如Windows 10、Linux) |
|
os.arch |
操作系统架构(如amd64、aarch64) |
|
user.name |
当前用户名称 |
|
user.dir |
当前工作目录(程序运行的根目录) |
|
classpath |
Java类路径(等同于-cp参数) |
|
file.separator |
文件分隔符(Windows为,Linux为/) |
|
line.separator |
行分隔符(Windows为 |
|
path.separator |
路径分隔符(Windows为;,Linux为:) |
3. 示例:操作系统属性
import java.util.Properties;
import java.util.Set;
public class SystemPropertiesDemo {
public static void main(String[] args) {
// 1. 获取单个系统属性
String javaVersion = System.getProperty("java.version");
String osName = System.getProperty("os.name");
String userDir = System.getProperty("user.dir");
System.out.println("Java版本:" + javaVersion);
System.out.println("操作系统:" + osName);
System.out.println("当前工作目录:" + userDir);
// 2. 获取不存在的属性,返回默认值
String customProp = System.getProperty("custom.prop", "默认值");
System.out.println("自定义属性:" + customProp);
// 3. 设置自定义系统属性
System.setProperty("custom.prop", "自定义值");
System.out.println("修改后自定义属性:" + System.getProperty("custom.prop"));
// 4. 遍历所有系统属性
Properties props = System.getProperties();
Set<String> propNames = props.stringPropertyNames();
System.out.println("
所有系统属性:");
for (String name : propNames) {
System.out.println(name + " = " + props.getProperty(name));
}
// 5. 移除自定义属性
System.clearProperty("custom.prop");
System.out.println("移除后自定义属性:" + System.getProperty("custom.prop", "已移除"));
}
}
模块3:环境变量(Environment Variables)
环境变量是操作系统级别的键值对配置(如Windows的PATH、JAVA_HOME,Linux的HOME),System类提供了访问环境变量的方法。
1. 核心方法
|
方法 |
说明 |
|
static Map<String, String> getenv() |
获取所有环境变量的Map对象(键值对) |
|
static String getenv(String name) |
根据名称获取单个环境变量值,不存在则返回null |
2. 关键区别:系统属性 vs 环境变量
|
维度 |
系统属性(System.getProperty) |
环境变量(System.getenv) |
|
所属级别 |
JVM级别(仅当前JVM进程有效) |
操作系统级别(全局有效,所有进程共享) |
|
设置方式 |
可通过System.setProperty动态设置 |
不可通过Java代码修改(只读) |
|
数据类型 |
仅字符串键值对 |
仅字符串键值对 |
|
配置方式 |
启动JVM时通过-Dkey=value参数设置 |
操作系统中配置(如Windows的环境变量面板、Linux的/etc/profile) |
|
示例 |
java.version、user.dir |
PATH、JAVA_HOME、HOME |
3. 示例:访问环境变量
import java.util.Map;
public class SystemEnvDemo {
public static void main(String[] args) {
// 1. 获取单个环境变量
String javaHome = System.getenv("JAVA_HOME");
String path = System.getenv("PATH");
String home = System.getenv("HOME"); // Linux/Mac为HOME,Windows为USERPROFILE
System.out.println("JAVA_HOME:" + javaHome);
System.out.println("PATH:" + path);
System.out.println("用户主目录:" + home);
// 2. 遍历所有环境变量
Map<String, String> envMap = System.getenv();
System.out.println("
所有环境变量:");
for (Map.Entry<String, String> entry : envMap.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
}
}
模块4:系统级操作(exit、gc、runFinalization等)
System类提供了直接操作JVM和系统的方法,这些方法具有最高的系统级权限,需谨慎使用。
1. 退出JVM:System.exit(int status)
- 功能:终止当前运行的JVM进程,参数status为退出状态码(0表明正常退出,非0表明异常退出);
- 原理:调用Runtime.getRuntime().exit(status),会触发JVM的关闭钩子(Shutdown Hook)执行;
- 注意:exit方法执行后,后续代码不会执行,且无法被捕获(即使在try-catch中调用,也会直接退出)。
示例:退出JVM与关闭钩子
public class SystemExitDemo {
public static void main(String[] args) {
// 注册关闭钩子(JVM退出前执行)
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("JVM即将退出,执行关闭钩子:清理资源...");
}));
System.out.println("程序开始执行");
// 模拟异常,退出状态码为1
boolean error = true;
if (error) {
System.err.println("发生异常,退出程序");
System.exit(1); // 非0表明异常退出
}
// 以下代码不会执行
System.out.println("程序执行完毕");
}
}
2. 触发垃圾回收:System.gc()
- 功能:向JVM发送垃圾回收提议,JVM可选择执行或忽略;
- 原理:调用Runtime.getRuntime().gc(),本质是通知JVM的垃圾收集器进行回收;
- 注意:gc()方法不保证立即执行垃圾回收,仅为提议;手动调用gc()会破坏JVM的垃圾回收优化策略,一般不提议使用(除非特殊场景,如大量对象创建后需立即释放内存)。
3. 触发对象终态化:System.runFinalization()
- 功能:强制JVM执行未执行的对象finalize()方法(对象被回收前的最后执行方法);
- 原理:调用Runtime.getRuntime().runFinalization();
示例:gc与runFinalization
class MyObject {
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("MyObject被回收,执行finalize()");
}
}
public class SystemGcDemo {
public static void main(String[] args) {
// 创建大量对象
for (int i = 0; i < 10000; i++) {
new MyObject();
}
// 提议垃圾回收
System.gc();
// 强制执行finalize()
System.runFinalization();
// 注意:以上操作不保证立即回收,输出结果可能不稳定
}
}
4. 获取系统时间:currentTimeMillis() & nanoTime()
- System.currentTimeMillis():返回当前时间与1970年1月1日00:00:00 GMT的毫秒数(时间戳),适用于日期时间计算;
- System.nanoTime():返回JVM启动后的纳秒数(高精度时间),适用于短时间间隔的准确测量(如性能测试)。
关键区别:
|
方法 |
精度 |
用途 |
受系统时间影响 |
|
currentTimeMillis() |
毫秒(ms) |
日期时间戳、长时间计算 |
是(系统时间修改会影响) |
|
nanoTime() |
纳秒(ns) |
短时间间隔测量、性能测试 |
否(仅与JVM启动时间相关) |
示例:时间测量
public class SystemTimeDemo {
public static void main(String[] args) {
// 1. 获取当前时间戳
long timestamp = System.currentTimeMillis();
System.out.println("当前时间戳(ms):" + timestamp);
// 2. 测量代码执行时间(高精度)
long start = System.nanoTime();
// 模拟耗时操作
for (int i = 0; i < 1000000; i++) {
Math.sqrt(i);
}
long end = System.nanoTime();
long duration = end - start;
System.out.println("代码执行时间(ns):" + duration);
System.out.println("代码执行时间(ms):" + duration / 1000000.0);
}
}
模块5:数组拷贝:System.arraycopy()
System.arraycopy()是Java中最高效的数组拷贝方法(由本地方法实现,底层基于C/C++,比Arrays.copyOf和手动循环拷贝快得多),支持数组的批量复制。
1. 方法定义
// 本地方法(native),底层由JVM实现
public static native void arraycopy(
Object src, // 源数组
int srcPos, // 源数组的起始位置
Object dest, // 目标数组
int destPos, // 目标数组的起始位置
int length // 要拷贝的元素个数
);
2. 关键说明
- 支持基本类型和引用类型数组:如int[]、String[]、Object[];
- 浅拷贝:对于引用类型数组,拷贝的是对象引用,而非对象本身;
- 数组越界:若srcPos、destPos或length超出数组范围,会抛出ArrayIndexOutOfBoundsException;
- 类型不匹配:若源数组和目标数组的类型不兼容(如int[]拷贝到String[]),会抛出ArrayStoreException;
- native方法:性能远高于手动循环拷贝,是Java数组拷贝的首选。
3. 示例:数组拷贝
public class SystemArrayCopyDemo {
public static void main(String[] args) {
// 1. 基本类型数组拷贝
int[] src = {1, 2, 3, 4, 5};
int[] dest = new int[10];
// 拷贝src的前3个元素到dest的起始位置(0)
System.arraycopy(src, 0, dest, 0, 3);
System.out.print("基本类型数组拷贝结果:");
for (int i = 0; i < dest.length; i++) {
System.out.print(dest[i] + " "); // 输出:1 2 3 0 0 0 0 0 0 0
}
System.out.println();
// 2. 引用类型数组拷贝(浅拷贝)
String[] srcStr = {"a", "b", "c"};
String[] destStr = new String[5];
System.arraycopy(srcStr, 0, destStr, 1, 2);
System.out.print("引用类型数组拷贝结果:");
for (String s : destStr) {
System.out.print(s + " "); // 输出:null a b null null
}
System.out.println();
// 3. 数组自身拷贝(实现元素移动)
int[] arr = {1, 2, 3, 4, 5};
// 将arr的第3个元素开始的2个元素(3,4)拷贝到第1个位置
System.arraycopy(arr, 2, arr, 0, 2);
System.out.print("数组自身拷贝结果:");
for (int i : arr) {
System.out.print(i + " "); // 输出:3 4 3 4 5
}
}
}
模块6:其他核心方法
1. System.identityHashCode(Object x)
- 功能:返回对象的原生哈希码(基于对象的内存地址),与Object.hashCode()的区别是:即使对象重写了hashCode()方法,identityHashCode仍返回原生值;
- 用途:判断两个对象是否为同一个实例(identityHashCode(a) == identityHashCode(b) 等价于 a == b)。
示例:
class Person {
@Override
public int hashCode() {
return 1; // 重写hashCode,固定返回1
}
}
public class SystemIdentityHashCodeDemo {
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person();
Person p3 = p1;
// 重写后的hashCode返回一样值
System.out.println(p1.hashCode() == p2.hashCode()); // true
// identityHashCode返回原生值,区分不同对象
System.out.println(System.identityHashCode(p1) == System.identityHashCode(p2)); // false
System.out.println(System.identityHashCode(p1) == System.identityHashCode(p3)); // true
}
}
2. System.console()
- 功能:返回与当前JVM关联的Console对象,用于读取密码(无回显)、控制台交互;
- 注意:在IDE中运行时,console()可能返回null(IDE的控制台并非原生控制台),需在命令行中运行。
示例:读取密码
import java.io.Console;
public class SystemConsoleDemo {
public static void main(String[] args) {
Console console = System.console();
if (console == null) {
System.err.println("无法获取Console对象(请在命令行中运行)");
return;
}
String username = console.readLine("请输入用户名:");
char[] password = console.readPassword("请输入密码:"); // 密码无回显
System.out.println("用户名:" + username);
System.out.println("密码:" + new String(password)); // 实际场景中应立即销毁密码数组
// 清空密码数组,避免内存泄露
java.util.Arrays.fill(password, ' ');
}
}
三、System类的关键注意事项
1. 权限限制
部分System类的方法(如setOut、setIn、setSecurityManager)在安全管理器(SecurityManager)启用时,会抛出SecurityException(如Applet环境、安全策略限制的场景)。
2. 线程安全问题
- 线程安全的方法:getProperty、getenv、arraycopy、currentTimeMillis、nanoTime、identityHashCode等方法是线程安全的;
- 非线程安全的方法:setOut、setIn、setErr、setProperty、clearProperty等方法非线程安全,多线程环境下需加锁保护。
3. 避免滥用系统级方法
- System.gc():手动调用会干扰JVM的垃圾回收策略,仅在特殊场景下使用;
- System.exit():除非必要,否则不要在应用程序中调用(尤其是Web应用、框架中,会导致容器崩溃);
- arraycopy的浅拷贝:对于引用类型数组,需注意对象引用的共享问题,避免意外修改。
四、常见问题解答
Q: System.gc() 真的会立即进行垃圾回收吗?
A: 不会。System.gc() 只是向 JVM 发出垃圾回收的提议,JVM 可以选择忽略这个提议或者延迟执行。
Q: System.out 和 System.err 有什么区别?
A: 两者都是输出流,但:
System.out 用于正常程序输出
System.err 用于错误信息输出
在某些环境中,它们可能被重定向到不同的目的地
Q: 什么时候应该使用 System.exit()?
A: 仅在以下情况使用:
命令行工具需要返回退出状态码
发生不可恢复的错误需要立即终止程序
脚本或批处理程序
日常应用开发中应优先使用异常处理或正常返回。
五、总结
System类是Java程序与系统交互的核心入口,其设计体现了工具类的典型范式(final、私有构造、静态成员)。核心要点如下:
- 核心功能:覆盖标准IO、系统属性/环境变量、数组拷贝、系统级操作(退出、GC)、时间获取等;
- 性能优势:arraycopy是原生方法,是Java中最高效的数组拷贝方式;
- 注意事项:避免滥用gc()和exit(),注意线程安全和权限限制;
- 应用场景:控制台交互、配置获取、性能测试、数组操作、资源清理等。



