新聞中心
在編寫Java程序時,有時候需要在Java程序中執(zhí)行另外一個程序。

創(chuàng)新互聯(lián)憑借專業(yè)的設(shè)計團隊扎實的技術(shù)支持、優(yōu)質(zhì)高效的服務(wù)意識和豐厚的資源優(yōu)勢,提供專業(yè)的網(wǎng)站策劃、成都網(wǎng)站設(shè)計、網(wǎng)站制作、外貿(mào)營銷網(wǎng)站建設(shè)、網(wǎng)站優(yōu)化、軟件開發(fā)、網(wǎng)站改版等服務(wù),在成都10多年的網(wǎng)站建設(shè)設(shè)計經(jīng)驗,為成都上1000家中小型企業(yè)策劃設(shè)計了網(wǎng)站。
1、啟動程序
Java提供了兩種方法用來啟動其它程序:
(1)使用Runtime的exec()方法
(2)使用ProcessBuilder的start()方法
不管在哪種操作系統(tǒng)下,程序具有基本類似的一些屬性。一個程序啟動后就程序操作系統(tǒng)的一個進程,進程在執(zhí)行的時候有自己的環(huán)境變量、有自己的工作目錄。Runtime和ProcessBuilder提供了不同的方式來啟動程序,設(shè)置啟動參數(shù)、環(huán)境變量和工作目錄。
能夠在Java中執(zhí)行的外部程序,必須是一個實際存在的可執(zhí)行文件,對于shell下的內(nèi)嵌命令是不能直接執(zhí)行的。
采用Runtime的exec執(zhí)行程序時,首先要使用Runtime的靜態(tài)方法得到一個Runtime,然后調(diào)用Runtime的exec方法??梢詫⒁獔?zhí)行的外部程序和啟動參數(shù)、環(huán)境變量、工作目錄作為參數(shù)傳遞給exec方法,該方法執(zhí)行后返回一個Process代表所執(zhí)行的程序。
Runtime有六個exec方法,其中兩個的定義為:
public Process exec(String[] cmdarray, String[] envp, File dir)
public Process exec(String command, String[] envp, File dir)
cmdarray和command為要執(zhí)行的命令,可以將命令和參數(shù)作為一個字符串command傳遞給exec()方法,也可以將命令和參數(shù)一個一個的方在數(shù)組cmdarray里傳遞給exec()方法。
envp為環(huán)境變量,以name=value的形式放在數(shù)組中。dir為工作目錄。
可以不要dir參數(shù),或者不要envp和dir參數(shù),這樣就多出了其它4個exec()方法。如果沒有dir參數(shù)或者為null,那么新啟動的進程就繼承當(dāng)前java進程的工作目錄。如果沒有envp參數(shù)或者為null,那么新啟動的進程就繼承當(dāng)前java進程的環(huán)境變量。
也可以使用ProcessBuilder類啟動一個新的程序,該類是后來添加到JDK中的,而且被推薦使用。通過構(gòu)造函數(shù)設(shè)置要執(zhí)行的命令以及參數(shù),或者也可以通過command()方法獲取命令信息后在進行設(shè)置。通過directory(File directory) 方法設(shè)置工作目錄,通過environment()獲取環(huán)境變量信息來修改環(huán)境變量。
在使用ProcessBuilder構(gòu)造函數(shù)創(chuàng)建一個新實例,設(shè)置環(huán)境變量、工作目錄后,可以通過start()方法來啟動新程序,與Runtime的exec()方法一樣,該方法返回一個Process對象代表啟動的程序。
ProcessBuilder與Runtime.exec()方法的不同在于ProcessBuilder提供了redirectErrorStream(boolean redirectErrorStream) 方法,該方法用來將進程的錯誤輸出重定向到標(biāo)準(zhǔn)輸出里。即可以將錯誤輸出都將與標(biāo)準(zhǔn)輸出合并。
2、Process
不管通過那種方法啟動進程后,都會返回一個Process類的實例代表啟動的進程,該實例可用來控制進程并獲得相關(guān)信息。Process 類提供了執(zhí)行從進程輸入、執(zhí)行輸出到進程、等待進程完成、檢查進程的退出狀態(tài)以及銷毀(殺掉)進程的方法:
(1) void destroy()
殺掉子進程。
一般情況下,該方法并不能殺掉已經(jīng)啟動的進程,不用為好。
(2) int exitValue()
返回子進程的出口值。
只有啟動的進程執(zhí)行完成、或者由于異常退出后,exitValue()方法才會有正常的返回值,否則拋出異常。
(3)InputStream getErrorStream()
獲取子進程的錯誤流。
如果錯誤輸出被重定向,則不能從該流中讀取錯誤輸出。
(4)InputStream getInputStream()
獲取子進程的輸入流。
可以從該流中讀取進程的標(biāo)準(zhǔn)輸出。
(5)OutputStream getOutputStream()
獲取子進程的輸出流。
寫入到該流中的數(shù)據(jù)作為進程的標(biāo)準(zhǔn)輸入。
(6) int waitFor()
導(dǎo)致當(dāng)前線程等待,如有必要,一直要等到由該 Process 對象表示的進程已經(jīng)終止。
通過該類提供的方法,可以實現(xiàn)與啟動的進程之間通信,達到交互的目的。
3、從標(biāo)準(zhǔn)輸出和錯誤輸出流讀取信息
從啟動其他程序的Java進程看,已啟動的其他程序輸出就是一個普通的輸入流,可以通過getInputStream()和getErrorStream來獲取。
對于一般輸出文本的進程來說,可以將InputStream封裝成BufferedReader,然后就可以一行一行的對進程的標(biāo)準(zhǔn)輸出進行處理。
4、舉例
(1)Runtime.exec()
| import java.io.BufferedReader; import java.io.File; import java.io.InputStreamReader; public class Test1 { //list the files and directorys under C:\ //echo the value of NAME } |
(2)ProcessBuilder
5、獲取進程的返回值
通常,一個程序/進程在執(zhí)行結(jié)束后會向操作系統(tǒng)返回一個整數(shù)值,0一般代表執(zhí)行成功,非0表示執(zhí)行出現(xiàn)問題。有兩種方式可以用來獲取進程的返回值。一是利用waitFor(),該方法是阻塞的,執(zhí)導(dǎo)進程執(zhí)行完成后再返回。該方法返回一個代表進程返回值的整數(shù)值。另一個方法是調(diào)用exitValue()方法,該方法是非阻塞的,調(diào)用立即返回。但是如果進程沒有執(zhí)行完成,則拋出異常。
6、阻塞的問題
由Process代表的進程在某些平臺上有時候并不能很好的工作,特別是在對代表進程的標(biāo)準(zhǔn)輸入流、輸出流和錯誤輸出進行操作時,如果使用不慎,有可能導(dǎo)致進程阻塞,甚至死鎖。
如果將以上事例中的從標(biāo)準(zhǔn)輸出重讀取信息的語句修改為從錯誤輸出流中讀取:
那么程序?qū)l(fā)生阻塞,不能執(zhí)行完成,而是hang在那里。
當(dāng)進程啟動后,就會打開標(biāo)準(zhǔn)輸出流和錯誤輸出流準(zhǔn)備輸出,當(dāng)進程結(jié)束時,就會關(guān)閉他們。在以上例子中,錯誤輸出流沒有數(shù)據(jù)要輸出,標(biāo)準(zhǔn)輸出流中有數(shù)據(jù)輸出。由于標(biāo)準(zhǔn)輸出流中的數(shù)據(jù)沒有被讀取,進程就不會結(jié)束,錯誤輸出流也就不會被關(guān)閉,因此在調(diào)用readLine()方法時,整個程序就會被阻塞。為了解決這個問題,可以根據(jù)輸出的實際先后,先讀取標(biāo)準(zhǔn)輸出流,然后讀取錯誤輸出流。
但是,很多時候不能很明確的知道輸出的先后,特別是要操作標(biāo)準(zhǔn)輸入的時候,情況就會更為復(fù)雜。這時候可以采用線程來對標(biāo)準(zhǔn)輸出、錯誤輸出和標(biāo)準(zhǔn)輸入進行分別處理,根據(jù)他們之間在業(yè)務(wù)邏輯上的關(guān)系決定讀取那個流或者寫入數(shù)據(jù)。
針對標(biāo)準(zhǔn)輸出流和錯誤輸出流所造成的問題,可以使用ProcessBuilder的redirectErrorStream()方法將他們合二為一,這時候只要讀取標(biāo)準(zhǔn)輸出的數(shù)據(jù)就可以了。
當(dāng)在程序中使用Process的waitFor()方法時,特別是在讀取之前調(diào)用waitFor()方法時,也有可能造成阻塞??梢杂镁€程的方法來解決這個問題,也可以在讀取數(shù)據(jù)后,調(diào)用waitFor()方法等待程序結(jié)束。
總之,解決阻塞的方法應(yīng)該有兩種:
(1)使用ProcessBuilder類,利用redirectErrorStream方法將標(biāo)準(zhǔn)輸出流和錯誤輸出流合二為一,在用start()方法啟動進程后,先從標(biāo)準(zhǔn)輸出中讀取數(shù)據(jù),然后調(diào)用waitFor()方法等待進程結(jié)束。
如:
(2)使用線程
7、在Java中執(zhí)行Java程序
執(zhí)行一個Java程序的關(guān)鍵在于:
(1)知道JAVA虛擬機的位置,即java.exe或者java的路徑
(2)知道要執(zhí)行的java程序的位置
(3)知道該程序所依賴的其他類的位置
舉一個例子,一目了然。
(1)待執(zhí)行的Java類
public class MyTest {
public static void main(String[] args) {
System.out.println("OUTPUT one");
System.out.println("OUTPUT two");
System.err.println("ERROR 1");
System.err.println("ERROR 2");
for(int i = 0; i < args.length; i++)
{
System.out.printf("args[%d] = %s.", i, args[i]);
}
}
}
(2)執(zhí)行該類的程序
| import java.util.*; import java.io.*; class StreamWatch extends Thread { String type; List output = new ArrayList (); boolean debug = false; StreamWatch(InputStream is, String type) { StreamWatch(InputStream is, String type, boolean debug) { public void run() { InputStreamReader isr = new InputStreamReader(is); public List getOutput() { public class Test6 { // process error and output message // start to watch //wait for exit //print the content from ERROR and OUTPUT } catch (Throwable t) { |
【編輯推薦】
- 我們?yōu)槭裁匆獙W(xué)習(xí)Java:Java的八大優(yōu)點
- 淺談Java中使用遞歸方法刪除文件
- JavaFX和Java之間的互操作性
當(dāng)前標(biāo)題:如何在Java中執(zhí)行其它程序
本文URL:http://fisionsoft.com.cn/article/dhioeej.html


咨詢
建站咨詢
