您的位置:首页 > 产品设计 > UI/UE

黑马程序员——java.lang.Process和java.lang.ProcessBuilder

2014-08-24 09:07 204 查看
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

概念

关于java.lang.Process和java.lang.ProcessBuilder类的概念请参考本文最下方链接的文章。

关于API

请参见官方API文档。

只说一下Process的getInputStream、getOutputStream、getErrorStream 3个方法。

站在父进程的角度来看,子进程的输出正好是父进程的输入(子输出给父),而子进程的输入正好是父进程的输出(父输出给子)。

Process类的3个方法是站在父进程的角度来说的,所以说调用getInputStream的到的是子进程的输出流,调用getOutputStream得到的是子进程的输入流,调用getErrorStream得到的是子进程的错误输出流。

ProcessImpl的源码也说明了这个问题,如下所示:

private OutputStream stdin_stream;
private InputStream stdout_stream;
private InputStream stderr_stream;

public OutputStream getOutputStream() {
return stdin_stream;
}

public InputStream getInputStream() {
return stdout_stream;
}

public InputStream getErrorStream() {
return stderr_stream;
}


创建Process的方式

1、调用Runtime.getRuntime().exec(... ...)系列方法。

   (其实exec方法也是通过创建ProcessBuilder对象的方式来创建新进程,下面有具体分析)

2、创建ProcessBuilder对象,再调用其start方法。

   (start方法会返回一个Process实现类的实例,也就是一个ProcessImpl类的实例,你可以在java.lang包中找到这个类)

如下所示:

public Process start() throws IOException {
//... ...
try {
return ProcessImpl.start(cmdarray,
environment,
dir,
redirects,
redirectErrorStream);
} catch (IOException | IllegalArgumentException e) {
//... ...
}
}


示例

在看具体的示例之前先看一部分源码,下面的源码是java.lang.Runtime中exec方法的实现:

//方法1
public Process exec(String command) throws IOException {
return exec(command, null, null);
}
//方法2
public Process exec(String command, String[] envp) throws IOException {
return exec(command, envp, null);
}
//方法3
public Process exec(String command, String[] envp, File dir) throws IOException {
if (command.length() == 0)
throw new IllegalArgumentException("Empty command");

StringTokenizer st = new StringTokenizer(command);
String[] cmdarray = new String[st.countTokens()];
for (int i = 0; st.hasMoreTokens(); i++)
cmdarray[i] = st.nextToken();
return exec(cmdarray, envp, dir);
}
//方法4
public Process exec(String cmdarray[]) throws IOException {
return exec(cmdarray, null, null);
}
//方法5
public Process exec(String[] cmdarray, String[] envp) throws IOException {
return exec(cmdarray, envp, null);
}
//方法6
public Process exec(String[] cmdarray, String[] envp, File dir) throws IOException {
return new ProcessBuilder(cmdarray)
.environment(envp)
.directory(dir)
.start();
}

从上面的源码中,我们可以看出如下2个要点:

1:方法1、方法2调用的是方法3,而方法3调用的是方法6;方法4、方法5调用的也是方法6。

2:方法6创建了一个ProcessBuilder对象。

所以我们主要看一下方法6。

public Process exec(String[] cmdarray, String[] envp, File dir) throws IOException


该方法的3个参数的意义如下:

cmdarray:这是一个String类型的数组,表示需要被调用的命令和这个命令需要的参数。

envp:这也是一个String类型的数组,表示环境变量,如果指定环境变量则它的格式为key=value,如果该参数为null,则创建的子进程将继承其父进程的环境变量。

dir:这是一个File类型的变量,表示子进程的工作目录,如果该参数为null,则子进程将继承父进程的工作目录。

当前的开发环境是JDK1.8,我们写一个程序,让它启动一个子进程并使它运行在JDK1.7的环境中(也就是改变子进程所在环境的JAVA_HOME和path环境变量)。

3个形参的值如下:

cmdarray:{"cmd.exe", "/c", "java", "-version"}

envp: {"JAVA_HOME=C:\Program Files\Java\jdk1.8.0", "path=%JAVA_HOME\bin;%%JAVA_HOME\jre\bin%"}

dir: null

package org.lgy.study.process;

import java.io.File;
//import java.lang.Process;
import java.util.Scanner;
//import.java.lang.StringBuffer;

public class ListNetStatus{
public static String executeCommand(String[] cmdarray, String[] envp, File dir) throws Exception{
Process p = Runtime.getRuntime().exec(cmdarray, envp, dir);

Scanner scanner1 = new Scanner(p.getInputStream());
Scanner scanner2 = new Scanner(p.getErrorStream());
StringBuffer sb = new StringBuffer();

while(scanner1.hasNextLine()){
sb.append(scanner1.nextLine());
sb.append(System.lineSeparator());
}

while(scanner2.hasNextLine()){
sb.append(scanner2.nextLine());
sb.append(System.lineSeparator());
}

scanner1.close();
scanner2.close();

return sb.toString();
}

public static void main(String[] args) throws Exception{
String[] envp = {"JAVA_HOME=C:/Program Files/Java/jdk1.7.0",
"PATH=C:/Program Files/Java/jdk1.7.0/bin;C:/Program Files/Java/jdk1.7.0/jre/bin%"};
File dir = new File("D:/Study");
System.out.println(ListNetStatus.executeCommand(new String[]{"cmd.exe", "/c", "java","-version"}, envp, null));
System.out.println(ListNetStatus.executeCommand(new String[]{"cmd.exe", "/c", "javac", "-d", "classes", "Test.java"}, envp, dir));
System.out.println(IPConfig.executeCommand(new String[]{"cmd.exe", "/c", "ipconfig"}, null, null));
}
}


上面程序中的Process是通过Runtime类创建的,下面我们使用ProcessBuilder类来创建。

package org.lgy.study.process;

import java.io.File;
import java.util.Scanner;

public class ProcessBuilderTest{
public static void main(String[] args) throws Exception{
ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/c", "java", "-version");
//重新设置环境变量
pb.environment().put("JAVA_HOME","C:/Program Files/Java/jdk1.7.0");
pb.environment().put("PATH","C:/Program Files/Java/jdk1.7.0/bin;C:/Program Files/Java/jdk1.7.0/jre/bin");
//将子进程的标准输出流和标准错误输出流合并
pb.redirectErrorStream(true);

Process p = pb.start();

Scanner scanner1 = new Scanner(p.getInputStream());
StringBuffer sb = new StringBuffer();

while(scanner1.hasNextLine()){
sb.append(scanner1.nextLine());
sb.append(System.lineSeparator());
}
scanner1.close();
System.out.println(sb.toString());

pb = new ProcessBuilder("cmd.exe", "/c", "javac -d classes Test.java");
//设置工作目录
pb.directory(new File("D:/Study"));
pb.environment().put("JAVA_HOME", "C:/Program Files/Java/jdk1.7.0");
pb.environment().put("PATH", "C:/Program Files/Java/jdk1.7.0/bin;C:/Program Files/Java/jdk1.7.0/jre/bin");
pb.redirectErrorStream(true);
sb = new StringBuffer();

p = pb.start();
scanner1 = new Scanner(p.getInputStream());

while(scanner1.hasNextLine()){
sb.append(scanner1.nextLine());
sb.append(System.lineSeparator());
}
scanner1.close();
System.out.println(sb.toString());
}
}


简单介绍里面用到的几个方法:

1、构造器

ProcessBuilder类只提供了2个构造器,如下

ProcessBuilder(List<String> command)
//Constructs a process builder with the specified operating system program and arguments.
ProcessBuilder(String... command)
//Constructs a process builder with the specified operating system program and arguments.


2个构造器都只有1个参数,这2个参数都代表命令的名称和需要的参数(就是上面介绍的exec方法的第一个参数)。

2、设置环境变量

ProcessBuilder类没有提供直接设置环境变量的方法,但是可以通过如下方式进行:

pb.environment().put("JAVA_HOME", "C:/Program Files/Java/jdk1.7.0");
pb.environment().put("PATH", "C:/Program Files/Java/jdk1.7.0/bin;C:/Program Files/Java/jdk1.7.0/jre/bin");


3、设置工作目录

File directory()
//Returns this process builder's working directory.
ProcessBuilder	directory(File directory)
//Sets this process builder's working directory.


4、标准错误流与标准输出流

boolean	redirectErrorStream()
//Tells whether this process builder merges standard error and standard output.
ProcessBuilder	redirectErrorStream(boolean redirectErrorStream)
//Sets this process builder's redirectErrorStream property.


在ProcessBuilder类中有一个boolean类型的、名为redirectErrorStream的Field,用于表示是否已将标准错误与标准输出合并,为true则表示已合并,为false则表示未合并。

第一个方法查看是否将标准错误与标准输出合并。

对于第二个方法,如果设置为 true,则将标准错误与标准输出合并。这使得关联错误消息和相应的输出变得更容易。        

在此情况下,合并的数据可从 Process.getInputStream() 返回的流读取

5、start方法

注意:start方法的返回值是一个Process子类的实例,不是ProcessBuilder的实例。

注意:

1、在java中表示路径不同于在windows,“\”在java中是转义字符的前缀,所以如果需要使用它,必须用“\\”的方式。也可以使用“/”代替“\\”。

2、用以上方式执行cmd.exe时,必须加“/c”参数,否则会使当前线程阻塞住。(原因不详)

9efc
3、在设置path环境变量时,不能使用%JAVA_HOME%代替C:/Program Files/Java/jdk1.7.0,必须使用完整的格式。

参考

51CTO技术博客的2篇文章:

深入研究java.lang.Process类 深入研究java.lang.ProcessBuilder类

博客园的1篇文章:

利用java.lang.Process和ProcessBuilder创建本地应用程序进程

java调用批处理或可执行文件
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息