Runtime.getRuntime().exec(...) —— 傳統 API,方法多載豐富。ProcessBuilder.start() —— JDK 1.5+ 推薦,物件導向、支援更靈活的環境變數與工作目錄設定。注意:僅能啟動實際存在的可執行檔,Shell 內建指令(如 dir, echo)需透過 cmd.exe /C 包裝。
Process無論哪種啟動方式,皆回傳 Process 物件代表子進程,提供:
getInputStream():讀取子進程標準輸出。getErrorStream():讀取子進程錯誤輸出。getOutputStream():向子進程標準輸入寫入資料。waitFor():阻塞等待子進程結束,回傳退出碼。exitValue():非阻塞取得退出碼,若進程未結束拋出例外。destroy():嘗試終止子進程(不保證成功)。子進程若同時輸出標準與錯誤流,且 Java 端只讀取其中一個,另一個緩衝區填滿會導致子進程阻塞,造成雙方死鎖。解決方案:
pb.redirectErrorStream(true),只需讀取單一流。getInputStream() 與 getErrorStream()。// 範例 1:合併流 + 同步讀取
ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/c", "dir");
pb.directory(new File("C:\\"));
pb.redirectErrorStream(true);
Process p = pb.start();
try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
String line;
while ((line = br.readLine()) != null) System.out.println(line);
}
int exitCode = p.waitFor();
// 範例 2:多執行緒分別消費 stdout/stderr
class StreamWatch extends Thread {
private final InputStream is; private final String type;
private final List output = new ArrayList<>();
StreamWatch(InputStream is, String type) { this.is = is; this.type = type; }
public void run() {
try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
String line; while ((line = br.readLine()) != null) output.add(line);
} catch (IOException e) { e.printStackTrace(); }
}
public List getOutput() { return output; }
}
// 使用
Process p = new ProcessBuilder("cmd.exe", "/c", "dir").start();
StreamWatch out = new StreamWatch(p.getInputStream(), "OUT");
StreamWatch err = new StreamWatch(p.getErrorStream(), "ERR");
out.start(); err.start();
int code = p.waitFor();
System.out.println("OUT: " + out.getOutput());
System.out.println("ERR: " + err.getOutput());
關鍵:取得 java.home 定位 java.exe,設定 -classpath,傳入主類別名與參數。
String java = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
String cp = System.getProperty("java.class.path");
List cmd = Arrays.asList(java, "-classpath", cp, MyTest.class.getName(), "arg1", "arg2");
ProcessBuilder pb = new ProcessBuilder(cmd);
// ... 後續同前述處理輸出流