Java 應用常需與作業系統交互:執行批處理腳本、啟動第三方 EXE、獲取命令行輸出。本文總結三種主流方案,對比優劣,附生產級代碼範本。
public static String executeCmd(String command) throws IOException {
Runtime runtime = Runtime.getRuntime();
// cmd /c 執行命令後退出
Process process = runtime.exec("cmd /c " + command);
// 必須消費輸出流,否則進程會阻塞
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
StringBuilder output = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
output.append(line).append(System.lineSeparator());
}
int exitCode = process.waitFor();
if (exitCode != 0) {
// 讀取錯誤流輔助排查
try (BufferedReader errReader = new BufferedReader(
new InputStreamReader(process.getErrorStream()))) {
String errLine;
while ((errLine = errReader.readLine()) != null) {
output.append("[ERROR] ").append(errLine).append(System.lineSeparator());
}
}
}
return output.toString();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("命令執行被中斷", e);
}
}
StandardCharsets.UTF_8 或 Charset.forName("GBK")getInputStream()、getErrorStream() 必須讀取,否則緩衝區滿導致進程掛起cmd /c "D:\Program Files\app.exe"public static void startDetachedProgram(String programPath) throws IOException {
if (StringUtils.isBlank(programPath)) return;
File exeFile = new File(programPath);
if (!exeFile.exists()) throw new FileNotFoundException("程式不存在: " + programPath);
String programName = exeFile.getName().substring(0, exeFile.getName().lastIndexOf('.'));
List cmd = new ArrayList<>();
cmd.add("cmd.exe");
cmd.add("/c");
cmd.add("start");
cmd.add("\"" + programName + "\""); // 標題,必須有
cmd.add("\"" + programPath + "\""); // 實際路徑
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.directory(exeFile.getParentFile()); // 設定工作目錄
pb.redirectErrorStream(true); // 合併錯誤流
pb.start(); // 非阻塞啟動
}
ProcessBuilder.startPipeline() 管道操作public static void openWithDesktop(String filePath) throws IOException {
if (!Desktop.isDesktopSupported()) {
throw new UnsupportedOperationException("當前環境不支援 Desktop API");
}
Desktop desktop = Desktop.getDesktop();
File file = new File(filePath);
if (!file.exists()) throw new FileNotFoundException(filePath);
// 自動關聯系統默認程式打開(.exe、.pdf、.url 等)
desktop.open(file);
}
| 維度 | Runtime.exec | ProcessBuilder | Desktop.open |
|---|---|---|---|
| 控制精度 | 低 | 高 | 極低 |
| 獲取輸出 | 支援 | 支援 | 不支援 |
| 非阻塞啟動 GUI | 需技巧 | 原生支援 | 原生支援 |
| 跨平台 | 需適配命令 | 需適配命令 | 完美跨平台 |
| 推薦場景 | 簡單腳本、獲取輸出 | 複雜參數、生產環境 | 打開文檔、URL、關聯程式 |
process.waitFor(timeout, TimeUnit.SECONDS) 防止掛死runas 動詞File.getCanonicalPath() 解決相對路徑、符號鏈接問題無銀彈,按需選型:需捕獲輸出用 ProcessBuilder;單純啟動獨立程式用 Desktop API;簡單腳本兼容舊代碼用 Runtime.exec。核心原則:必須消費流、必須處理異常、必須記錄日誌。