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

JDK 5.0 ProcessBuilder 實戰:跨平台獲取網卡物理地址 (MAC Address)

     編輯:Java 相關知識

JDK 5.0 引入 ProcessBuilder 替代 Runtime.exec(),提供更靈活的進程控制:環境變數隔離、工作目錄指定、輸入輸出流重定向。本文以獲取本機網卡 MAC 地址為例,展示 ProcessBuilder 核心用法與跨平台適配要點。

核心代碼實現

package com.kuaff.jdk5package;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

public class ProcessBuilderShow {
    
    public static List<String> getPhysicalAddress() {
        Process process = null;
        List<String> addresses = new ArrayList<>();
        
        try {
            // Windows: ipconfig /all
            process = new ProcessBuilder("ipconfig", "/all").start();
        } catch (IOException e) {
            return addresses;
        }
        
        // 讀取進程輸出
        StringBuilder output = new StringBuilder();
        try (InputStream in = process.getInputStream()) {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = in.read(buffer)) > 0) {
                output.append(new String(buffer, 0, len));
            }
        } catch (IOException e) {
            // 忽略讀取異常,嘗試解析已獲取內容
        }
        
        // 解析輸出提取 MAC 地址
        String content = output.toString();
        int index = content.indexOf("Physical Address");
        while (index >= 0) {
            // 定位到冒號後的地址部分
            int colon = content.indexOf(':', index);
            if (colon >= 0) {
                String candidate = content.substring(colon + 1).trim();
                // MAC 格式: XX-XX-XX-XX-XX-XX (17 字符含分隔符)
                if (candidate.length() >= 17) {
                    addresses.add(candidate.substring(0, 17).toUpperCase());
                }
            }
            index = content.indexOf("Physical Address", index + 1);
        }
        
        return addresses;
    }
    
    public static void main(String[] args) {
        List<String> macs = getPhysicalAddress();
        for (String mac : macs) {
            System.out.printf("物理網卡地址: %s%n", mac);
        }
    }
}

ProcessBuilder 關鍵優勢

特性Runtime.exec()ProcessBuilder
命令參數傳遞單一字符串,需手動處理空格/引號List<String> 或 可變參數,自動處理
環境變數控制繼承 JVM 進程環境,難以隔離environment() 返回 Map,可增刪改
工作目錄固定為 JVM 啟動目錄directory(File) 任意指定
輸入輸出重定向需手動管道連接redirectInput/Output/Error() 支援文件/PIPE/INHERIT
啟動管道鏈極其繁瑣ProcessBuilder.startPipeline() (JDK 9+)

跨平台適配策略

MAC 獲取命令因作業系統而異:

// 偽代碼邏輯
String os = System.getProperty("os.name").toLowerCase();
List<String> cmd;
if (os.contains("win")) {
    cmd = Arrays.asList("ipconfig", "/all");
    parsePattern = "Physical Address.*: ([0-9A-F-]{17})";
} else if (os.contains("linux") || os.contains("mac")) {
    cmd = Arrays.asList("sh", "-c", "ip link show | grep -oE '([0-9a-f]{2}:){5}[0-9a-f]{2}'");
    parsePattern = "([0-9a-f]{2}:){5}[0-9a-f]{2}";
} else {
    // 兜底:Java 6+ NetworkInterface API (純 Java 無外部依賴)
    return getMacByNetworkInterface();
}

現代最佳實踐:純 Java API (JDK 6+)

強烈建議優先使用 java.net.NetworkInterface,無外部進程開銷、跨平台穩定、無解析風險:


import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Collections;
import java.util.List;

public class MacAddressUtil {
    public static List<String> getAllMacAddresses() throws SocketException {
        List<String> macs = new ArrayList<>();
        for (NetworkInterface ni : Collections.list(NetworkInterface.getNetworkInterfaces())) {
            if (!ni.isUp() || ni.isLoopback() || ni.isVirtual()) continue;
            byte[] mac = ni.getHardwareAddress();
            if (mac != null) {
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < mac.length; i++) {
                    sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : ""));
                }
                macs.add(sb.toString());
            }
        }
        return macs;
    }
}

進階場景:進程輸出即時處理


// 非阻塞讀取,避免緩衝區滿導致子進程阻塞
new Thread(() -> {
    try (BufferedReader reader = new BufferedReader(
            new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
        String line;
        while ((line = reader.readLine()) != null) {
            // 即時處理每行輸出
            handleLine(line);
        }
    } catch (IOException e) { /* handle */ }
}).start();

// 同樣處理 error stream,防止死鎖

總結

  • ProcessBuilder 是 JDK 5+ 執行外部命令的標準方式,功能完善、API 清晰
  • 獲取 MAC 地址首選純 Java NetworkInterface API,僅在特殊環境 (容器無權限、特殊虛擬網卡) 回退命令行
  • 生產代碼必須處理:編碼指定、錯誤流讀取、超時控制、非零退出碼異常
  • JDK 9+ 新增 startPipeline()、toCommand()、redirectOutput(Redirect.appendTo(file)) 等實用增強
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved