5.3 System和Runtime类:系统信息与运行时管理

系列 - Java API
摘要
本实验学习Java中两个重要的系统级工具类:System类和Runtime类。System类部分包括:高效的数组拷贝操作、精确的时间测量方法、系统环境信息获取、以及内存垃圾回收控制;Runtime类部分涵盖:虚拟机资源信息监控(处理器数量、内存使用情况)、外部程序执行和进程管理等高级操作。

在Java编程中,有时候我们需要与"电脑系统"打交道,比如查看内存使用情况、获取系统时间、甚至启动其他程序。这时候就需要用到System类Runtime类这两个"系统管理员"。

学习目标

通过本节学习,你将掌握:

  1. System类应用 - 学会数组操作、时间测量和系统信息获取
  2. Runtime类使用 - 掌握虚拟机监控和进程管理技巧
  3. 实际应用 - 能够在项目中合理使用这些系统工具
  4. 性能优化 - 理解如何用这些工具进行程序调优

System类就像电脑里的"系统工具箱",提供了各种实用的功能。我们来逐一学习它的主要功能。

当你需要把一个数组的一部分内容复制到另一个数组时,System.arraycopy()就像一个"高效的搬运工"。

System.arraycopy()方法参数

语法: System.arraycopy(源数组, 源起始位置, 目标数组, 目标起始位置, 拷贝长度)

参数说明:

  • srcArray:源数组(从哪里拷贝)
  • srcPos:源数组的起始位置
  • destArray:目标数组(拷贝到哪里)
  • destPos:目标数组的起始位置
  • length:要拷贝的元素个数

java

/**  
 * 演示数组高效拷贝操作
 */
public class ArrayCopyDemo {  
    public static void main(String[] args) {  
        // 原始数组:存放学生分数
        int[] classAScores = {85, 92, 78, 96, 88, 91, 73};  
        // 目标数组:用于存放优秀学生分数
        int[] topStudents = new int[10];
        
        System.out.println("原始班级成绩:");
        printArray(classAScores);
        
        // 拷贝操作:从第2个元素开始,拷贝4个元素到新数组的第1个位置
        System.arraycopy(classAScores, 2, topStudents, 1, 4);  
  
        System.out.println("\n拷贝后的优秀学生数组:");  
        printArray(topStudents);
        
        // 显示拷贝的具体内容
        System.out.println("\n具体拷贝情况:");
        System.out.println("从 classAScores[索引 2] 开始的 4 个元素:78, 96, 88, 91");
        System.out.println("拷贝到 topStudents[索引 1] 开始的位置");
    }
    
    // 辅助方法:打印数组内容
    public static void printArray(int[] array) {
        for (int i = 0; i < array.length; i++) {
            System.out.printf("索引%d: %d  ", i, array[i]);
        }
        System.out.println();
    }
}
使用场景
  • 数据迁移:大数组的部分内容转移
  • 数组拼接:将多个小数组合并成大数组
  • 数据备份:快速创建数组的副本
  • 性能优化:比手动循环复制快得多

当你想知道一段代码跑了多久时,System.currentTimeMillis()就像一个"精确的秒表"。

java

/**  
 * 演示程序性能测量工具
 */  
public class PerformanceTimer {  
    public static void main(String[] args) {  
        System.out.println("=== 程序性能测试工具 ===");
        
        // 测试不同的字符串拼接方式
        testStringConcatenation();
        
        // 测试数组初始化方式
        testArrayInitialization();
    }  
    
    /**
     * 测试字符串拼接性能
     */
    public static void testStringConcatenation() {
        System.out.println("\n测试项目:字符串拼接性能对比");
        
        // 记录开始时间
        long startTime = System.currentTimeMillis();  
        
        // 执行要测试的操作:使用+号拼接字符串
        String result = "";  
        for (int i = 0; i < 10000; i++) {  
            result += "test";  
        }  

        // 记录结束时间
        long endTime = System.currentTimeMillis();  
        
        // 计算并显示执行时间
        long duration = endTime - startTime;
        System.out.println("使用 + 号拼接 10,000 次耗时:" + duration + "ms");  
        System.out.println("最终字符串长度:" + result.length());
    }
    
    /**
     * 测试数组初始化性能
     */
    public static void testArrayInitialization() {
        System.out.println("\n测试项目:大数组初始化性能");
        
        long startTime = System.currentTimeMillis();
        
        // 创建并初始化一个大数组
        int[] largeArray = new int[1000000];
        for (int i = 0; i < largeArray.length; i++) {
            largeArray[i] = i * 2;
        }
        
        long endTime = System.currentTimeMillis();
        
        System.out.println("初始化 100万个整数的数组耗时:" + (endTime - startTime) + "ms");
        System.out.println("数组的前5个元素:" + largeArray[0] + ", " + largeArray[1] + ", " + largeArray[2] + ", " + largeArray[3] + ", " + largeArray[4]);
    }
}
常用的性能测量场景
  • 算法效率比较:比较不同排序算法的速度
  • 数据库查询时间:监控SQL语句执行耗时
  • 网络请求延迟:测量API调用的响应时间
  • 文件读写性能:对比不同文件操作的效率

有时候程序需要知道运行环境的信息,比如操作系统类型、Java版本等。System.getProperties()就像"系统信息查询器"。

java

import java.util.Properties;  
  
/**  
 * 系统环境信息查询器
 */  
public class SystemInfoDemo {  
    public static void main(String[] args) {  
        System.out.println("=== 系统环境信息查询 ===");
        
        // 显示常用的系统信息
        showCommonSystemInfo();
        
        // 显示所有系统属性(可选)
        // showAllSystemProperties();
    }
    
    /**
     * 显示常用的系统信息
     */
    public static void showCommonSystemInfo() {
        System.out.println("\n--- 常用系统信息 ---");
        
        // 获取常用的系统属性
        System.out.println("操作系统名称:" + System.getProperty("os.name"));
        System.out.println("操作系统版本:" + System.getProperty("os.version"));
        System.out.println("用户名称:" + System.getProperty("user.name"));
        System.out.println("用户主目录:" + System.getProperty("user.home"));
        System.out.println("当前工作目录:" + System.getProperty("user.dir"));
        System.out.println("Java版本:" + System.getProperty("java.version"));
        System.out.println("Java安装目录:" + System.getProperty("java.home"));
        System.out.println("文件分隔符:" + System.getProperty("file.separator"));
        System.out.println("路径分隔符:" + System.getProperty("path.separator"));
    }
    
    /**
     * 显示所有系统属性(了解即可)
     */
    public static void showAllSystemProperties() {
        System.out.println("\n--- 所有系统属性 ---");
        
        Properties prop = System.getProperties();  
        
        // 使用增强for循环遍历所有属性
        for (Object keyObj : prop.keySet()) {
            String key = (String) keyObj;
            String value = prop.getProperty(key);
            System.out.println(key + " = " + value);
        }
    }
}
实际应用场景
  • 跨平台开发:根据操作系统类型选择不同的处理逻辑
  • 文件路径处理:使用正确的文件分隔符
  • 环境检测:检查Java版本是否符合要求
  • 配置管理:根据用户目录设置默认配置文件位置

Java会自动回收不用的内存,但有时我们希望手动"催一催"垃圾回收器工作。

java

/**
 * 演示垃圾回收机制
 */
 
class Student {  
    private String name;  
    private int id;
    
    public Student(String name, int id) {  
        this.name = name;  
        this.id = id;
        System.out.println("学生 " + name + "(学号:" + id + ") 被创建了");
    }  
  
    // finalize方法会在对象被垃圾回收时调用
    @Override
    protected void finalize() throws Throwable {  
        System.out.println("学生 " + name + "(学号:" + id + ") 被垃圾回收器回收了");  
        super.finalize();
    }  
}  
  
public class GarbageCollectionDemo {  
    public static void main(String[] args) {  
        System.out.println("=== 垃圾回收机制演示 ===");
        
        // 创建一些对象
        Student student1 = new Student("张三", 001);  
        Student student2 = new Student("李四", 002);  
        Student student3 = new Student("王五", 003);

        System.out.println("\n对象创建完成,现在将引用设为null...");
        // 取消对象引用,使对象可以被垃圾回收
        student1 = null;  
        student2 = null;  
        student3 = null;

        System.out.println("调用 System.gc() 建议进行垃圾回收...");
        // 建议系统进行垃圾回收(注意:只是建议,不是强制)
        System.gc();  

        // 稍微等待一下,让垃圾回收器有机会工作
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println("程序结束。");
    }  
}
关于垃圾回收的重要说明
  • System.gc()只是建议:调用后系统可能不会立即执行垃圾回收
  • finalize()不推荐使用:执行时机不确定,并且影响性能
  • 实际开发中:不要依赖手动垃圾回收,Java会自动管理内存
  • 合理使用:只在内存紧张时偶尔使用

Runtime类就像Java虚拟机的"体检中心",可以查看内存使用情况、处理器信息,甚至启动其他程序。

就像手机里的"设置"能看到存储空间使用情况一样,Runtime让你能监控Java程序的资源使用。

java

/**  
 * 虚拟机资源监控器
 */  
public class VMResourceMonitor {  
    public static void main(String[] args) {  
        System.out.println("=== Java虚拟机资源监控 ===");
        
        // 获取Runtime实例(单例模式)
        Runtime runtime = Runtime.getRuntime();  
        
        // 显示系统基本信息
        showSystemInfo(runtime);
        
        // 显示内存使用情况
        showMemoryUsage(runtime);
        
        // 模拟一些内存操作后再次查看
        simulateMemoryUsage(runtime);
    }
    
    /**
     * 显示系统基本信息
     */
    public static void showSystemInfo(Runtime runtime) {
        System.out.println("\n--- 系统基本信息 ---");
        System.out.println("处理器核心数:" + runtime.availableProcessors() + "个");  
        System.out.println("操作系统:" + System.getProperty("os.name"));
        System.out.println("Java版本:" + System.getProperty("java.version"));
    }
    
    /**
     * 显示内存使用情况
     */
    public static void showMemoryUsage(Runtime runtime) {
        System.out.println("\n--- 内存使用情况 ---");
        
        long totalMemory = runtime.totalMemory();
        long freeMemory = runtime.freeMemory();
        long maxMemory = runtime.maxMemory();
        long usedMemory = totalMemory - freeMemory;
        
        System.out.printf("已分配内存:%.2f MB\n", totalMemory / 1024.0 / 1024.0);
        System.out.printf("已使用内存:%.2f MB\n", usedMemory / 1024.0 / 1024.0);
        System.out.printf("空闲内存:%.2f MB\n", freeMemory / 1024.0 / 1024.0);
        System.out.printf("最大可用内存:%.2f MB\n", maxMemory / 1024.0 / 1024.0);
        
        // 计算内存使用率
        double usagePercentage = (double) usedMemory / totalMemory * 100;
        System.out.printf("内存使用率:%.2f%%\n", usagePercentage);
    }
    
    /**
     * 模拟内存使用并监控变化
     */
    public static void simulateMemoryUsage(Runtime runtime) {
        System.out.println("\n--- 模拟内存操作 ---");
        
        // 创建一个大数组来模拟内存使用
        System.out.println("正在创建大数组...");
        int[] largeArray = new int[1000000]; // 创建100万个整数的数组
        
        // 再次查看内存使用情况
        showMemoryUsage(runtime);
        
        // 释放引用并建议进行垃圾回收
        largeArray = null;
        System.out.println("\n已释放数组引用,建议进行垃圾回收...");
        runtime.gc();
        
        // 稍微等待后再次查看
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        showMemoryUsage(runtime);
    }
}
内存监控的实际应用
  • 性能优化:及时发现内存泄露问题
  • 资源管理:监控程序的内存使用情况
  • 服务器部署:确保内存配置合理
  • 调试优化:找到占用内存较多的代码段

有时候Java程序需要调用其他程序,比如打开计算器、记事本等。Runtime.exec()就像一个"程序启动器"。

java

import java.io.IOException;

/**
 * 外部程序执行管理器
 */
public class ProcessExecutor {  
    public static void main(String[] args) {  
        System.out.println("=== 外部程序执行演示 ===");
        
        Runtime runtime = Runtime.getRuntime();  
        
        // 演示不同的程序启动方式
        demonstrateProcessExecution(runtime);
    }
    
    /**
     * 演示程序执行的不同方式
     */
    public static void demonstrateProcessExecution(Runtime runtime) {
        try {
            System.out.println("\n1. 尝试打开记事本...");
            
            // 根据操作系统选择不同的命令
            String osName = System.getProperty("os.name").toLowerCase();
            
            if (osName.contains("windows")) {
                // Windows系统打开记事本
                runtime.exec("notepad.exe");
                System.out.println("已在Windows上启动记事本");
                
            } else if (osName.contains("mac")) {
                // macOS系统打开文本编辑器
                runtime.exec("open -a TextEdit");
                System.out.println("已在macOS上启动TextEdit");
                
            } else {
                // Linux系统(可能需要根据具体发行版调整)
                runtime.exec("gedit");
                System.out.println("已在Linux上尝试启动gedit");
            }
            
        } catch (IOException e) {  
            System.out.println("程序启动失败:" + e.getMessage());
            System.out.println("可能的原因:程序不存在或没有权限启动");
        }
        
        // 演示其他常用程序
        demonstrateOtherPrograms(runtime);
    }
    
    /**
     * 演示启动其他常用程序
     */
    public static void demonstrateOtherPrograms(Runtime runtime) {
        System.out.println("\n2. 其他常用程序演示:");
        
        try {
            String osName = System.getProperty("os.name").toLowerCase();
            
            if (osName.contains("windows")) {
                System.out.println("尝试打开计算器...");
                runtime.exec("calc.exe");
                
            } else if (osName.contains("mac")) {
                System.out.println("尝试打开计算器...");
                runtime.exec("open -a Calculator");
            }
            
        } catch (IOException e) {
            System.out.println("程序启动失败:" + e.getMessage());
        }
    }
}
进程管理示例

自动关闭程序示例:

java

public class ProcessAutoClose {  
    public static void main(String[] args) {  
        Runtime runtime = Runtime.getRuntime();  
        Process process = null;
        
        try {  
            System.out.println("打开记事本,3秒后自动关闭...");
            process = runtime.exec("notepad.exe");  // 启动记事本
            
            Thread.sleep(3000);  // 等待3秒
            
            process.destroy();  // 关闭进程
            System.out.println("记事本已自动关闭");  
            
        } catch (Exception e) {  
            System.out.println("操作失败:" + e.getMessage());  
        } finally {
            // 确保进程被正确关闭
            if (process != null && process.isAlive()) {
                process.destroyForcibly();
            }
        }
    }  
}
安全注意事项
  • 路径安全:不要执行用户输入的任意命令
  • 权限控制:确保程序有执行相应命令的权限
  • 资源管理:及时关闭启动的进程,避免资源泄露
  • 异常处理:充分处理IOException等异常情况
本节收获

System类掌握 - 学会了数组拷贝、时间测量和系统信息获取
Runtime类应用 - 掌握了虚拟机监控和进程管理技巧
性能优化 - 理解了如何用这些工具进行程序调优
实际应用 - 能够在项目中合理使用这些系统工具

关键要点:

  • 📈 System类适用于:数组操作、性能测量、系统信息获取
  • 🖥️ Runtime类适用于:内存监控、资源管理、进程执行
  • ⚠️ 注意安全:谨慎使用进程执行功能,防止安全风险
  • 🚀 实用建议:在大型项目中用于性能监控和优化

现在你已经掌握了Java系统级编程的重要工具!