黑马程序员——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的源码也说明了这个问题,如下所示:
创建Process的方式
1、调用Runtime.getRuntime().exec(... ...)系列方法。
(其实exec方法也是通过创建ProcessBuilder对象的方式来创建新进程,下面有具体分析)
2、创建ProcessBuilder对象,再调用其start方法。
(start方法会返回一个Process实现类的实例,也就是一个ProcessImpl类的实例,你可以在java.lang包中找到这个类)
如下所示:
示例
在看具体的示例之前先看一部分源码,下面的源码是java.lang.Runtime中exec方法的实现:
从上面的源码中,我们可以看出如下2个要点:
1:方法1、方法2调用的是方法3,而方法3调用的是方法6;方法4、方法5调用的也是方法6。
2:方法6创建了一个ProcessBuilder对象。
所以我们主要看一下方法6。
该方法的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
上面程序中的Process是通过Runtime类创建的,下面我们使用ProcessBuilder类来创建。
简单介绍里面用到的几个方法:
1、构造器
ProcessBuilder类只提供了2个构造器,如下
2个构造器都只有1个参数,这2个参数都代表命令的名称和需要的参数(就是上面介绍的exec方法的第一个参数)。
2、设置环境变量
ProcessBuilder类没有提供直接设置环境变量的方法,但是可以通过如下方式进行:
3、设置工作目录
4、标准错误流与标准输出流
在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调用批处理或可执行文件
概念
关于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调用批处理或可执行文件
相关文章推荐
- 08 java.lang.ProcessBuilder
- java.lang.ProcessBuilder类(系统进程)
- java.lang.ProcessBuilder用法
- 本地mapReduce项目报错:java.lang.NullPointerException at java.lang.ProcessBuilder.start...
- java.lang.ProcessBuilder
- java.lang.ProcessBuilder.command(String command)方法实例 代替命令行方式启动程序
- WindowBuilder Design 打不开 java.lang.NoClassDefFoundError:
- java.lang.StringBuilder的使用以及如何post方式post xml
- weblogic10.3 java.lang.ClassCastException: org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
- Axis2 错误:An error occurred while completing process -java.lang.reflect.InvocationTargetException
- [OpenJDK]native.java.lang.UNIXProcess_md.c
- android 自定义接听电话时报错:java.lang.SecurityException: Neither user 10088 nor current process has android.p
- java.lang.Process调用程序阻塞问题解决
- 关于java.lang.ClassCastException: org.jbpm.jpdl.internal.model.JpdlProcessDefinition cannot be cast to java.util.List
- java.lang.IllegalStateException: No FacesContext is available to process this request的另一种处理方法
- 黑马程序员_Java学习日记_JAVA中API中对象String和StringBuffer/StringBuilder
- an error occurred while completing process -java.lang.reflect.InvocationTargetException
- an error occurred while completing process -java.lang.reflect.InvocationTargetEx
- java.lang.Process 阻塞问题
- Axis2 错误:An error occurred while completing process -java.lang.reflect.InvocationTargetException