在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. 爬取页面InsecureRequestWarning: 警告解决笔记

    InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is s ...

  2. [转帖] CA如何保护自己的私钥

    作者:Gh0u1L5链接:https://www.zhihu.com/question/22260090/answer/648910720来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业 ...

  3. python之路3-元组、列表、字典、集合

    1.元组 特点:一旦创建,内容不可修改,又叫只读列表 a= ('wang','zhang','zhao') print(a.count('zhao')) print(a.index('wang')) ...

  4. Win10图片打不开文件系统错误2147416359解决方法

    该问题表现为win10打开所有图片都会提示这个‘文件系统错误-2147416359’,打开其他文件没问题.此问题应该是win10自带的图片查看器出了故障. 在网上找到如下方案,但是我的服务列表里没有这 ...

  5. BZOJ3028食物——生成函数+泰勒展开

    题目描述 明明这次又要出去旅游了,和上次不同的是,他这次要去宇宙探险!我们暂且不讨论他有多么NC,他又幻想了他应 该带一些什么东西.理所当然的,你当然要帮他计算携带N件物品的方案数.他这次又准备带一些 ...

  6. C# 中ref与out关键字区别

    ref 关键字通过引用传递的参数的内存地址,而不是值.简单点说就是在方法中对参数的任何改变都会改变调用方的基础参数中.代码举例: class RefExample { static void Meth ...

  7. 使用js获取页面参数

    方法一 function GetUrlParam (name) { return decodeURIComponent((new RegExp('[?|&]' + name + '=' + ' ...

  8. springboot 拦截器

    拦截器的实现: 创建自定义拦截器CustomInterceptor: package com.xc.boot.handler; import org.springframework.stereotyp ...

  9. nginx日志相关的查询

    IP相关统计 统计IP访问量(独立ip访问数量) awk '{print $1}' access.log | sort -n | uniq | wc -l 查看某一时间段的IP访问量(4-5点) gr ...

  10. CF226D The table

    题目链接 题意 给出一个\(n\times m\)的矩阵,可以把某些行和某些列上面的数字变为相反数.问修改那些行和哪些列可以使得所有行和所有列之和都为非负数. 思路 每次将负数的行或者列变为相反数.因 ...