首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 在 Windows 下調用 CMD 命令與啟動 EXE 程式的三種實戰方案

     編輯:Java 相關知識

場景引入

Java 應用常需與作業系統交互:執行批處理腳本、啟動第三方 EXE、獲取命令行輸出。本文總結三種主流方案,對比優劣,附生產級代碼範本。

方案一:Runtime.exec + cmd /c —— 經典通用

核心代碼

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);
    }
}

關鍵點

  • 編碼指定:Windows 預設 GBK,建議顯式用 StandardCharsets.UTF_8 或 Charset.forName("GBK")
  • 流消費:getInputStream()、getErrorStream() 必須讀取,否則緩衝區滿導致進程掛起
  • 路徑含空格:命令整體用雙引號包裹,如 cmd /c "D:\Program Files\app.exe"

方案二:ProcessBuilder —— 靈活控制、推薦生產環境

啟動獨立 GUI 程式(非阻塞)

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(); // 非阻塞啟動
}

優勢

  • 可精確控制工作目錄、環境變量、流重定向
  • 支援參數列表化,避免字符串拼接的轉義地獄
  • Java 9+ 支援 ProcessBuilder.startPipeline() 管道操作

方案三:Desktop API —— 純 Java 跨平台啟動關聯程式

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.execProcessBuilderDesktop.open
控制精度低高極低
獲取輸出支援支援不支援
非阻塞啟動 GUI需技巧原生支援原生支援
跨平台需適配命令需適配命令完美跨平台
推薦場景簡單腳本、獲取輸出複雜參數、生產環境打開文檔、URL、關聯程式

生產級避坑清單

  1. 超時控制:使用 process.waitFor(timeout, TimeUnit.SECONDS) 防止掛死
  2. 權限問題:Windows 下啟動需管理員權限的 EXE,需配置 Manifest 或用 runas 動詞
  3. 路徑規範化:統一用 File.getCanonicalPath() 解決相對路徑、符號鏈接問題
  4. 日誌記錄:記錄完整命令、退出碼、耗時、輸出摘要,便於事後排查

總結

無銀彈,按需選型:需捕獲輸出用 ProcessBuilder;單純啟動獨立程式用 Desktop API;簡單腳本兼容舊代碼用 Runtime.exec。核心原則:必須消費流、必須處理異常、必須記錄日誌。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved