Process process = Runtime.getRuntime().exec(".\\p.exe");
process.waitFor(); // 等待子進程結束
exec() 創建子進程,返回 Process 對象控制其生命週期。waitFor() 阻塞當前線程直到子進程退出。
內部命令屬於命令解釋器,不能直接執行。兩種方案:
// Windows NT/2000/XP/7/10
Runtime.getRuntime().exec("cmd.exe /c dir");
// Windows 95/98 (歷史)
Runtime.getRuntime().exec("command.exe /c dir");
/c 參數:執行完畢關閉窗口。跨平台需檢測 OS 版本動態選擇。exec("my_dir.bat"),無需關心解釋器差異,移植性更好。❌ 直接 exec("dir") 會拋異常。
利用系統文件關聯,兩種寫法:
// 方案 1:start 命令(推薦,簡潔)
Runtime.getRuntime().exec("start .\\a.doc");
// 方案 2:指定完整程序路徑(脆弱,路徑易變)
Runtime.getRuntime().exec("C:\\Program Files\\Microsoft Office\\Office\\winWord.exe .\\a.doc");
現象:子進程輸出過多,緩衝區填滿 → 進程阻塞 → waitFor() 永不返回 → 主程序掛死。
根因:Java 未及時讀取子進程 stdout/stderr 緩衝區。
解決:waitFor() 前務必消費輸出流:
Process process = Runtime.getRuntime().exec("cmd /c dir \\Windows");
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream())
);
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line); // 或寫日志、丟棄
}
process.waitFor(); // 此刻緩衝區已空,安全返回
進階:同時消費 getErrorStream(),建議用獨立線程讀取雙流,防單流塞滿死鎖。
場景:自動更新下載 ZIP → 解壓 → 刪除 ZIP。Java ZIP 流未關閉導致文件句柄佔用,常規 file.delete() 失敗。
暴力繞過:調用系統 del 命令(OS 層面強制刪除,不受 JVM 句柄限制):
// ✅ 正確:包裝 cmd /c
Runtime.getRuntime().exec("cmd /c del f:\\aaa.doc");
// ❌ 錯誤:直接 exec del(非可執行文件)
Runtime.getRuntime().exec("del f:\\aaa.doc");
⚠️ 注意:這是應急手段。正規做法是嚴格關閉所有流(try-with-resources)、使用 Files.deleteIfExists() (NIO.2)、或延遲刪除 (JVM 退出鉤子)。
| 場景 | 推薦做法 | 關鍵點 |
|---|---|---|
| 外部可執行程序 | Runtime.exec / ProcessBuilder | 異步讀雙流、超時控制、退出碼檢查 |
| DOS 內部命令 | cmd /c command 或 .bat 文件 | 跨版本兼容、路徑含空格加引號 |
| 打開關聯文件 | start filePath | 依賴系統關聯、無需硬編碼程序路徑 |
| 文件刪除失敗 | 先查流關閉、再試 NIO.2、最後 cmd /c del | 治本優於治標、記錄日誌排查佔用者 |
現代替代方案:Java 7+ 推薦使用 ProcessBuilder(靈活配置環境變量、工作目錄、重定向)、Files API(文件操作)、Apache Commons Exec(高級進程控制)。