15.6 Java虚拟机读写其他进程的数据

15.6 Java虚拟机读写其他进程的数据

在第7章已经介绍过,使用Runtime对象的exec()方法可以运行平台上的其他程序,该方法产生个Process对象,Process对象代表由该Java程序启动的子进程

Process类提供了如下三个方法,用于让程序和其子讲程讲行通信

方法 描述
abstract InputStream getErrorStream() 获取子进程的错误流。
abstract InputStream getInputStream() 获取子进程的输入流。
abstract OutputStream getOutputStream() 获取子进程的输出流。

要从java程序的角度来看输入输出流

此处的输入流、输出流非常容易混淆,如果试图让子进程读取程序中的数据,那么应该用输岀流
要站在Java程序的角度来看问题,而不是从子进程的角度来看问题:
子进程读取Java程序的数据,就是让Java程序把数据输岀到子进程中
这就像Java程序把数据输出到文件中一样,只是现在由子进程节点代替了文件节点,所以
子进程要读取java程序的内容应该使用输出流

程序 java读取子进程的错误输出

下面程序示范了读取其他进程的输出信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.io.*;

public class ReadFromProcess {
public static void main(String[] args) throws IOException {
// 运行javac命令,返回运行该命令的子进程
Process p = Runtime.getRuntime().exec("javac");// 代码1
try (
// 以p进程的错误流创建BufferedReader对象
// 这个错误流对本程序是输入流,对p进程则是输出流
BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream()))// 代码2
) {
String buff = null;
// 采取循环方式来读取p进程的错误输出
while ((buff = br.readLine()) != null) {
System.out.println("子程序的错误输出:"+buff);
}
}
}
}

上面程序中的代码1使用Runtime启动了Javac程序,获得了运行该程序对应的子进程,
代码2以p进程的错误输入流创建了BufferedReader,这个输入流的流向如图15.11所示。
这里有一张图片
如图15.11所示的数据流对p进程(Javac进程)而言,它是输出流;但对本程序(ReadFromProcess)而言,它是输入流。
衡量输入、输出时总是站在运行本程序所在内存的角度,所以该数据流应该是输入流
运行上面程序,会看到如下信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
G:\Desktop\随书源码\疯狂Java讲义(第4版)光盘\codes\15\15.6>javac -encoding utf-8 ReadFromProcess.java

G:\Desktop\随书源码\疯狂Java讲义(第4版)光盘\codes\15\15.6>java ReadFromProcess
子程序的错误输出:用法: javac <options> <source files>
子程序的错误输出:其中, 可能的选项包括:
子程序的错误输出: -g 生成所有调试信息
子程序的错误输出: -g:none 不生成任何调试信息
子程序的错误输出: -g:{lines,vars,source} 只生成某些调试信息
子程序的错误输出: -nowarn 不生成任何警告
子程序的错误输出: -verbose 输出有关编译器正在执行的操作的消息
子程序的错误输出: -deprecation 输出使用已过时的 API 的源位置
子程序的错误输出: -classpath <路径> 指定查找用户类文件和注释处理程序的位置
子程序的错误输出: -cp <路径> 指定查找用户类文件和注释处理程序的位置
子程序的错误输出: -sourcepath <路径> 指定查找输入源文件的位置
子程序的错误输出: -bootclasspath <路径> 覆盖引导类文件的位置
子程序的错误输出: -extdirs <目录> 覆盖所安装扩展的位置
子程序的错误输出: -endorseddirs <目录> 覆盖签名的标准路径的位置
子程序的错误输出: -proc:{none,only} 控制是否执行注释处理和/或编译。
子程序的错误输出: -processor <class1>[,<class2>,<class3>...] 要运行的注释处理程序的名称; 绕过默认的搜索进程
子程序的错误输出: -processorpath <路径> 指定查找注释处理程序的位置
子程序的错误输出: -parameters 生成元数据以用于方法参数的反射
子程序的错误输出: -d <目录> 指定放置生成的类文件的位置
子程序的错误输出: -s <目录> 指定放置生成的源文件的位置
子程序的错误输出: -h <目录> 指定放置生成的本机标头文件的位置
子程序的错误输出: -implicit:{none,class} 指定是否为隐式引用文件生成类文件
子程序的错误输出: -encoding <编码> 指定源文件使用的字符编码
子程序的错误输出: -source <发行版> 提供与指定发行版的源兼容性
子程序的错误输出: -target <发行版> 生成特定 VM 版本的类文件
子程序的错误输出: -profile <配置文件> 请确保使用的 API 在指定的配置文件中可用
子程序的错误输出: -version 版本信息
子程序的错误输出: -help 输出标准选项的提要
子程序的错误输出: -A关键字[=值] 传递给注释处理程序的选项
子程序的错误输出: -X 输出非标准选项的提要
子程序的错误输出: -J<标记> 直接将 <标记> 传递给运行时系统
子程序的错误输出: -Werror 出现警告时终止编译
子程序的错误输出: @<文件名> 从文件读取选项和文件名
子程序的错误输出:

程序 java输出数据到子进程

不仅如此,也可以通过ProcessgetOutputStream方法获得向子进程输入数据的流(该流对Java程序来说是输出流,对子进程来说是输入流),如下程序实现了在Java程序中启动Java虚拟机运行另一个Java程序,并向另一个Java程序中输入数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import java.io.*;
import java.util.*;

public class WriteToProcess {
public static void main(String[] args) throws IOException {
// 运行java ReadStandard命令,返回运行该命令的子进程
Process p = Runtime.getRuntime().exec("java ReadStandard");// 代码1
try (
// 以p进程的输出流创建PrintStream对象
// 这个输出流对本程序是输出流,对p进程则是输入流
PrintStream ps = new PrintStream(p.getOutputStream())// 代码2
) {
// 向ReadStandard程序写入内容,这些内容将被ReadStandard读取
ps.println("普通字符串");
ps.println(new WriteToProcess());
}
}
}

// 定义一个ReadStandard类,该类可以接受标准输入,
// 并将标准输入写入out.txt文件。
class ReadStandard {
public static void main(String[] args) {
try (
// 使用System.in创建Scanner对象,用于获取标准输入
Scanner sc = new Scanner(System.in);
PrintStream ps = new PrintStream(new FileOutputStream("out.txt"))) {
// 增加下面一行将只把回车作为分隔符
sc.useDelimiter("\n");
// 判断是否还有下一个输入项
while (sc.hasNext()) {
// 输出输入项
ps.println("键盘输入的内容是:" + sc.next());
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}

上面程序中的ReadStandard是一个使用Scanner获取标准输入的类,该类提供了main()方法,可以被运行,但此处不打算直接运行该类,而是由WriteToProcess类来运行ReadStandard类。

在程序的代码1中,程序使用Runtimeexec()方法运行了java Readstandard命令,该命令将运行ReadStandard类,并返回运行该程序的子进程;
程序的代码2获得当前程序对进程p的输出流:
程序通过该输出流向进程p(也就是ReadStandard程序)输出数据,这些数据将被ReadStandard类读到。
运行上面的WriteToProcess类,程序运行结束将看到产生了一个out.txt文件,该文件由ReadStandard类产生,该文件的内容由WriteToProcess类写入ReadStandard进程里,并由ReadStandard读取这些数据,并将这些数据保存到out.txt文件中.