在Java中想调用外部程序,或者执行命令和可运行文件时,网上的典型实例一般都是通过Runtime.getTime().exec()【 java.lang包】去执行相应的操作。看源码才发现还有Process和ProcessBuilder类,来具体看看它们的区别和用法。

一、Runtime类

Runtime类采用的饿汉式单例设计模式(定义了私有类变量和私有构造方法,通过静态方法返回该类的唯一实例)。每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。应用程序不能创建自己的Runtime 类实例。只能通过 getRuntime 方法获取当前运行时对象的引用。

  • 查看虚拟机相关状态
Runtime rt = Runtime.getRuntime(); //获取类的实例对象
System.out.println(rt.availableProcessors());// 返回可用于Java虚拟机的处理器数量
System.out.println(rt.freeMemory());// 返回Java虚拟机中的可用内存量 (单位:字节)
System.out.println(rt.maxMemory());// 返回Java虚拟机将尝试使用的最大内存量
System.out.println(rt.totalMemory());// 返回Java虚拟机中的内存总量
//--------------------------------------------------------------------------
// System中的gc(),exit(),load() 等都是调用了该类中的同名方法
rt.gc(); // 运行垃圾回收器
rt.exit(0); // 通过启动其关闭序列来终止当前正在运行的Java虚拟机,底层调用Shutdown.exit()实现
rt.halt(0); // 强制终止当前正在运行的Java虚拟机(杀进程)
  • 通过钩子,保证java程序安全退出
Runtime rt = Runtime.getRuntime();
// java 1.8的写法
Thread t =new Thread(
() -> System.out.println(Thread.currentThread().getName()+" +++++++++ ")
);
rt.addShutdownHook(t); //注册新的虚拟机来关闭钩子
new Thread(
new Runnable() {
private int i;
@Override
public void run() {
for(;i<10;i++){
System.out.println(Thread.currentThread().getName()+" --- "+i);
}
}
}
).start();
//rt.exit(0); // 若使用exit() 会等待钩子运行后再进行退出
//rt.halt(0); // 若这使用此方法,直接强制终止当前虚拟机,不会执行钩子
//rt.removeShutdownHook(t); // 注册的钩子会被取消,不会打印 "+++++"
  • 执行操作方法 Runtime.exec (返回用于管理子进程的新的Process对象 )

    1. Process exec(String command) 在单独的进程中执行指定的字符串命令
    2. Peocess exec(String[] cmdarray) 在单独的进程中执行指定的命令和参数
    3. Process exec(String command, String[] envp) 在单独的进程中执行指定的字符串命令
    4. Process exec(String[] cmdarray, String[] envp) 在指定环境的单独进程中执行指定的命令和参数
    5. Process exec(String command, String[] envp, File dir) 在指定的环境和工作目录的单独进程中执行指定的字符串命令
    6. Process exec(String[] cmdarray, String[] envp, File dir) 在指定的环境和工作目录的单独进程中执行指定的命令和参数

这6个重载方法(方法名一样,参数个数不一样,参数类型不一样)。实际上2、4、5调用的都是6这个方法,而1、3调用的是方法5。先来看一下方法6中实现:

return new ProcessBuilder(cmdarray).environment(envp).directory(dir).start();

可以看出来,实际上Runtime.getRuntime().exec(...)是通过 ProcessBuilder.start()去实现创建进程的。再来看方法5:

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);

首先是判断传入的命令参数长度是否等于0,是则抛出异常。然后通过StringTokenizer类对传入的命令字符串进行解析(这里使用的默认分隔符集,即" \t\n\r\f" :空格字符,制表符,换行字符,回车字符和换页符)。并将解析后的Token使用for循环赋值给String数组的每个元素,最后将数组传给方法6。所以1-6方法实质上传入String类型的参数和传入String []数组参数是一样的。不过传入String [] 的优势在于:如果String类型输入的命令缺少空格,将会导致运行的命令错误。

实例:

import java.io.*;
import java.util.concurrent.TimeUnit; public class Test002 {
public static void main(String [] args){
Runtime rt = Runtime.getRuntime();
try {
//Process p = rt.exec("cmd /c dir /s/b | findstr \"Test[0-9]*.java\"");// 当前文件中查找Test开头含多个数字的java文件
//Process p1 = rt.exec(new String[]{"cmd", "/c", "javac"});
Process p1 = rt.exec("cmd /c java -version"); // 将会通过 errorStream输出相关信息 PrintContext error = new PrintContext(p1.getErrorStream(), "Error");
PrintContext print = new PrintContext(p1.getInputStream(), "Print");
error.start();
print.start(); /*BufferedReader br = new BufferedReader(new InputStreamReader(p1.getInputStream(), "GBK"));
String line;
while((line = br.readLine())!=null){
System.out.println(line); }
System.out.println("---------");
p1.waitFor(1,TimeUnit.SECONDS); // 等待1 s
BufferedReader br1 = new BufferedReader(new InputStreamReader(p1.getErrorStream(), "GBK"));
String line1;
while((line1 = br1.readLine())!=null){
System.err.println(line1);
}*/
} catch (IOException e) {
e.printStackTrace();
}
}
} class PrintContext extends Thread {
private InputStream is;
private String type; public PrintContext(InputStream is, String type) {
this.is = is;
this.type = type;
} public void run() {
BufferedReader br = null;
try {
// 控制台输出为GBK编码
br = new BufferedReader(new InputStreamReader(is,"GBK"));
String line;
while ((line = br.readLine()) != null) {
if (type.equals("Error")) {
System.out.println("Error:" + line);
} else {
System.out.println("Print:" + line);
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(br != null){
try{
br.close();
}catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
}

二、Process类

Process 类是一个抽象类,其内部提供了6个抽象方法。用于执行进程输入,执行到进程的输出,等待进程完成,检查进程的退出状态以及破坏(杀死)进程的方法。创建的子进程没有自己的终端或控制台。 其所有的标准I / O(即标准输入,标准输出,标准错误)操作将被重定向到父进程。 父进程使用流将输入提供给子进程并从子进程获取输出。 因为一些本地平台只为标准输入和输出流提供有限的缓冲区大小,因此无法及时写入输入流或读取子进程的输出流可能导致子进程阻塞甚至死锁。

 Runtime rt = Runtime.getRuntime();
Process p,p1=null;
try {
p = rt.exec("notepad.exe");
if(p.isAlive()){ // 判断Process进程是否存活
System.out.println(" p start...");
try {
p.waitFor(3, TimeUnit.SECONDS); // 等待3秒
p1 = rt.exec("cmd /c dir");
System.out.println(" p1 start...");
} catch (InterruptedException e) {
e.printStackTrace();
}
p.destroy(); // 杀死子进程
}
p1.waitFor(); // 导致当前线程等待,如有必要,直到由此 Process对象表示的进程已终止
System.out.println("p1 wait done");
p1.destroy(); System.out.println("p:"+p.exitValue()); // 1 非正常退出
System.out.println("p1:"+p1.exitValue()); // 0 正常退出 } catch (IOException|InterruptedException e) {
e.printStackTrace();
}

三、ProcessBuilder类

该类是一个不可变类,用于创建操作系统进程。每个ProcessBuilder实例管理进程属性的集合,start()方法使用这些属性创建一个新的Process实例。ProcessBuilder有个带参构造方法:

ProcessBuilder(List<String> command)  构造具有指定操作系统程序和参数的进程构建器
ProcessBuilder(String... command) 构造具有指定操作系统程序和参数的进程构建器

其实这两个的构造方法区别也不是很大,一个是传入List<String>类型的参数然后初始化,另一个是传入可变的String类型参数,初始化List对象,将参数add到ArraysList里。所以ProcessBuilder不能像Runtime一样直接将整句的命令作为参数传参。

 ProcessBuilder pb = new ProcessBuilder("notepad.exe");
try {
pb.directory(new File("E:\\WorkProjects")); // 设置进程构建器的工作目录
pb.command("cmd","/c","dir"); // 重新设置命令参数
Process p = pb.start(); // 运行进程
System.out.println(pb.directory().getPath()); // 获取进程构建器的工作目录路径
List<String> c = pb.command(); // 获取命令参数
Iterator<String> it = c.iterator();
while(it.hasNext()){
System.out.print(it.next()+"\t");// cmd /c dir
}
p.destroy();
} catch (IOException e) {
e.printStackTrace();
}

Java_Runtime&Process&ProcessBuilder的更多相关文章

  1. [Java][Android][Process] ProcessBuilder与Runtime差别

    在Android中想要进行Ping,在不Root机器的情况下似乎还仅仅能进行底层命调用才干实现. 由于在Java中要进行ICMP包发送须要Root权限. 于是仅仅能通过创建进程来攻克了.创建进程在Ja ...

  2. Java中ProcessBuilder应用实例

    系列说明 浅析Java.lang.Runtime类 浅析Java.lang.Process类 浅析Java.lang.ProcessBuilder类 可以使用java中的ProcessBuilder执 ...

  3. [Java][Android][Process] 暴力的服务能够解决一切,暴力的方式运行命令行语句

    不管是在Java或者Android中运行命令行语句殊途同归都是创建一个子进程运行调用可运行文件运行命令.类似于Windows中的CMD一样. 此时你有两种方式运行:ProcessBuilder与Run ...

  4. 浅析ProcessBuilder

    概述 ProcessBuilder类是J2SE 1.5在java.lang中新添加的一个新类,此类用于创建操作系统进程,它提供一种启动和管理进程(也就是应用程序)的方法.在J2SE 1.5之前,都是由 ...

  5. 多线程 调用多线程的方法 Runtime与ProcessBuilder

    一般我们使用Java运行其他类中的方法的时候,无论是静态调用还是动态调用,都是在当前的进程中执行的.也就是只有一个Java虚拟机实例在运行.有时候需要通过Java代码启动多个Java子进程,这样做会消 ...

  6. Android随笔之——静默安装、卸载

    随笔之所以叫随笔,就是太随意了,说起来,之前的闹钟系列随笔还没写完,争取在十月结束之前找时间把它给写了吧.今天要讲的Android APK的静默安装.卸载.网上关于静默卸载的教程有很多,更有说要调用隐 ...

  7. java执行命令行

    List<String> command = new ArrayList<String>(); command.add("ping"); ProcessBu ...

  8. PDF解决方案(3)--PDF转SWF

    相关专题链接 PDF解决方案(1)--文件上传 PDF解决方案(2)--文件转PDF PDF解决方案(3)--PDF转SWF PDF解决方案(4)--在线浏览 前言:上一篇中介绍了上传的文件转PDF, ...

  9. Scala入门系列(十二):隐式转换

    引言 Scala提供的隐式转换和隐式参数功能,是非常有特色的功能.是Java等编程语言所没有的功能.它可以允许你手动指定,将某种类型的对象转换成其他类型的对象.通过这些功能可以实现非常强大而且特殊的功 ...

随机推荐

  1. VUE 绑定背景图片的写法

    <div v-bind:style='{"background-image":"url("+imgUrl+")"}' >< ...

  2. dubbo在idea下的使用创建 服务者,消费者 注册中心

    1.基于windows 下  spring 下的dubbo  需要书写配置文件 (1).创建带有web工程的项目 创建一个服务者 package cn.edu.aynu.bean; import lo ...

  3. MySQL系列:数据库基本操作(1)

    1. 登录数据库 mysql -h localhost -u root -p 2. 数据库基本操作 2.1 查看数据库 mysql> SHOW DATABASES; +------------- ...

  4. LODOP提示、报错、现象,简短问答

    提示升级提示:“打印控件需要升级!点击这里执行升级,升级后请重新进入."“Web打印服务CLodop需升级!点击这里执行升级,升级后请刷新页面.”(新版提示) 参考http://www.c- ...

  5. Converting PDF to Text in C#

    Parsing PDF files in .NET using PDFBox and IKVM.NET (managed code). Download source files - 82 kB [c ...

  6. Flask的插件session、SQLAlchemy、Script、Migrate

    一.flask-session 1.为什么要使用flask-session 因为flask默认的session是通过请求上下文放入到Local中的,是存在内存的,而使用flask-session可以更 ...

  7. 使用Flink实现索引数据到Elasticsearch

    使用Flink实现索引数据到Elasticsearch  2018-07-28 23:16:36    Yanjun 使用Flink处理数据时,可以基于Flink提供的批式处理(Batch Proce ...

  8. Java入门:基础算法之二进制转换为十进制

    Java有两种方法可以将二进制数转换为十进制数: 1)使用Integer类的Integer.parseInt()方法. 2)自己编写转换逻辑. 方法1:使用Integer.parseInt()实现二进 ...

  9. js上传图片压缩,并转化为base64

    <input type="file" onchange="startUpload(this,'front')" id="renm"/& ...

  10. JAVA IO练习

     停车场有进场和出场的功能1. 进场时:采用键盘录入的方式,录入汽车的品牌.颜色.车牌号. 把品牌.颜色.车牌号,以及进场时间写入car.txt文件中. 2. 出场时:键盘录入车牌号,去文件中查找该车 ...