在 Java 中调用系统命令主要有两种方式ProcessBuilder和Runtime.exec()。ProcessBuilder功能更强大、更灵活是更推荐的选择。以下是这两个类的方法及功能对比表⚖️ 核心方法对比表功能维度Runtime.exec()ProcessBuilder说明执行命令exec(String cmd)exec(String[] cmdarray)start()ProcessBuilder需要先配置好命令最后调用start()。命令格式字符串或数组列表或可变参数ProcessBuilder的构造函数接受ListString或String...。设置工作目录❌ 不支持directory(File dir)ProcessBuilder可以指定子进程的运行目录。设置环境变量❌ 不支持environment()返回一个Map可修改或继承环境变量。错误流处理❌ 需手动合并redirectErrorStream(boolean)ProcessBuilder可将 stderr 合并到 stdout防止死锁。输入/输出重定向❌ 不支持redirectInput/Output/Error()ProcessBuilder支持直接重定向到文件 (Redirect.PIPE,Redirect.INHERIT等)。进程销毁destroy()/destroyForcibly()继承自 Process 类两者返回的都是Process对象销毁方法相同。等待完成waitFor()继承自 Process 类两者都通过返回的Process对象调用waitFor()。️ ProcessBuilder 常用方法详解ProcessBuilder是一个可变的配置类配置完成后通过start()生成Process对象。构造方法ProcessBuilder(String... command)作用使用指定的命令和参数构建一个新的进程构建器。示例new ProcessBuilder(ls, -l)ProcessBuilder(ListString command)作用同上但参数通过列表传递。配置方法command(String... command)作用更新要执行的命令。返回this以便链式调用。directory(File directory)作用设置子进程的工作目录。如果为null则使用当前 Java 进程的工作目录。environment()作用返回一个字符串映射MapString, String它是子进程环境属性的视图。你可以直接修改这个 Map 来添加或删除环境变量。redirectErrorStream(boolean redirectErrorStream)作用告诉操作系统是否应该将子进程的标准错误流合并到标准输出流中。强烈建议设置为true以防止子进程因错误流缓冲区满而阻塞。启动方法start()作用使用此进程构建器的属性启动一个新进程。返回一个Process对象。 Runtime.exec() 常用方法详解Runtime类是一个单例通常通过Runtime.getRuntime()获取实例。获取实例Runtime.getRuntime()作用返回与当前 Java 应用程序相关的运行时对象。执行方法 (重载较多)exec(String command)作用在单独的进程中执行指定的字符串命令。注意如果命令包含空格如文件路径这种方法很容易出错因为它只是简单地按空格分割。exec(String[] cmdarray)作用在单独的进程中执行指定命令和变量。这是更安全的方式。exec(String command, String[] envp)作用执行命令并指定新的环境变量。exec(String[] cmdarray, String[] envp, File dir)作用执行命令指定环境变量和工作目录。这是Runtime中最灵活的方法但参数列表很长不如ProcessBuilder直观。 总结与建议首选ProcessBuilder如果你需要设置工作目录、修改环境变量或者处理复杂的命令参数必须使用ProcessBuilder。它的 API 设计更面向对象更易于阅读和维护。Runtime.exec()的局限性虽然它写起来稍微快一点少几行代码但在处理带空格的路径或参数时非常容易踩坑。它无法优雅地处理环境变量的追加只能完全替换或手动解析。关于Process对象无论使用哪种方式最终都会得到一个java.lang.Process对象。你必须处理process.getInputStream()和process.getErrorStream()否则子进程可能会因为缓冲区满而死锁。使用process.waitFor()来等待命令执行结束。 方法一使用 ProcessBuilder (推荐)ProcessBuilder提供了更丰富的功能例如设置工作目录、修改环境变量、合并错误流等是处理复杂命令场景的首选。核心代码示例这个例子展示了如何使用ProcessBuilder执行命令并正确处理标准输出和错误输出避免程序阻塞。import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; public class ProcessBuilderExample { public static void main(String[] args) { ProcessBuilder processBuilder new ProcessBuilder(); // 判断操作系统构建不同的命令 boolean isWindows System.getProperty(os.name).toLowerCase().startsWith(windows); if (isWindows) { // Windows 命令列出 C 盘根目录 processBuilder.command(cmd.exe, /c, dir, C:\\); } else { // Linux/Unix 命令列出根目录 processBuilder.command(sh, -c, ls /); } // (可选) 设置命令执行的工作目录 // processBuilder.directory(new File(System.getProperty(user.home))); // (可选) 将错误流合并到标准输出流简化处理 // processBuilder.redirectErrorStream(true); try { // 启动进程 Process process processBuilder.start(); // 读取标准输出 BufferedReader reader new BufferedReader(new InputStreamReader(process.getInputStream())); String line; while ((line reader.readLine()) ! null) { System.out.println(line); } reader.close(); // 读取错误输出 (如果未合并错误流) BufferedReader errorReader new BufferedReader(new InputStreamReader(process.getErrorStream())); while ((line errorReader.readLine()) ! null) { System.err.println(line); } errorReader.close(); // 等待进程结束并获取退出码 int exitCode process.waitFor(); System.out.println(命令执行完毕退出码: exitCode); } catch (IOException | InterruptedException e) { e.printStackTrace(); } } } 方法二使用 Runtime.exec()Runtime.exec()是一种更直接、简单的方式但功能相对有限。核心代码示例import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class RuntimeExample { public static void main(String[] args) { boolean isWindows System.getProperty(os.name).toLowerCase().startsWith(windows); String command; if (isWindows) { command cmd.exe /c dir C:\\; } else { command sh -c ls /; } try { // 执行命令 Process process Runtime.getRuntime().exec(command); // 读取标准输出 BufferedReader reader new BufferedReader(new InputStreamReader(process.getInputStream())); String line; while ((line reader.readLine()) ! null) { System.out.println(line); } reader.close(); // 读取错误输出 BufferedReader errorReader new BufferedReader(new InputStreamReader(process.getErrorStream())); while ((line errorReader.readLine()) ! null) { System.err.println(line); } errorReader.close(); // 等待进程结束并获取退出码 int exitCode process.waitFor(); System.out.println(命令执行完毕退出码: exitCode); } catch (IOException | InterruptedException e) { e.printStackTrace(); } } } 关键要点与最佳实践必须处理输出流这是最常见的问题。如果不读取或关闭子进程的标准输出流(getInputStream()) 和错误输出流(getErrorStream())当输出缓冲区满时子进程将会阻塞导致 Java 程序挂起。正确处理带空格的参数当命令包含带空格的参数时例如git commit -m initial commit使用Runtime.exec(String command)可能会解析失败。错误方式:runtime.exec(git commit -m initial commit)正确方式: 使用字符串数组java// 适用于 Runtime.exec() runtime.exec(new String[]{git, commit, -m, initial commit}); // 适用于 ProcessBuilder new ProcessBuilder(git, commit, -m, initial commit);