Java调用批处理或可执行文件

      用Java编写应用时,有时需要在程序中调用另一个现成的可执行程序或系统命令,这时可以通过组合使用Java提供的Runtime类和Process类的方法实现。下面是一种比较典型的程序模式: 

Process process = Runtime.getRuntime().exec(".\\p.exe"); 
 process.waitfor( );

      在上面的程序中,第一行的“.\\p.exe”是要执行的程序名,Runtime.getRuntime()返回当前应用程序的Runtime对象,该对象的exec()方法指示Java虚拟机创建一个子进程执行指定的可执行程序,并返回与该子进程对应的Process对象实例。通过Process可以控制该子进程的执行或获取该子进程的信息。第二条语句的目的等待子进程完成再往下执行。 
但在windows平台上,如果处理不当,有时并不能得到预期的结果。下面是在实际编程中总结的几种需要注意的情况: 
1、执行DOS的内部命令 
如果要执行一条DOS内部命令,有两种方法。一种方法是把命令解释器包含在exec()的参数中。例如,执行dir命令,在NT上, 可写成exec("cmd.exe /c dir"),在windows 95/98下,可写成“command.exe /c dir”,其中参数“/c”表示命令执行后关闭Dos立即关闭窗口。另一种方法是,把内部命令放在一个批命令my_dir.bat文件中,在Java程序中写成exec("my_dir.bat")。如果仅仅写成exec("dir"),Java虚拟机则会报运行时错误。前一种方法要保证程序的可移植性,需要在程序中读取运行的操作系统平台,以调用不同的命令解释器。后一种方法则不需要做更多的处理。 
2、打开一个不可执行的文件 
打开一个不可执行的文件,但该文件存在关联的应用程序,则可以有两种方式。 以打开一个word文档a.doc文件为例,Java中可以有以下两种写法: 

exec("start .\\a.doc"); 
 exec(" c:\\Program Files\\Microsoft Office\\office\\winword.exe .\\a.doc");

显然,前一种方法更为简捷方便。 
3、执行一个有标准输出的DOS可执行程序 
在windows平台上,运行被调用程序的DOS窗口在程序执行完毕后往往并不会自动关闭,从而导致Java应用程序阻塞在waitfor( )。导致该现象的一个可能的原因是,该可执行程序的标准输出比较多,而运行窗口的标准输出缓冲区不够大。解决的办法是,利用Java提供的Process类提供的方法让Java虚拟机截获被调用程序的DOS运行窗口的标准输出,在waitfor()命令之前读出窗口的标准输出缓冲区中的内容。一段典型的程序如下: 

String ls_1; 
 Process process = Runtime.getRuntime().exec("cmd /c dir \\windows"); 
 BufferedReader bufferedReader = new BufferedReader( \ 
 new InputStreamReader(process.getInputStream()); 
 while ( (ls_1=bufferedReader.readLine()) != null) 
 System.out.println(ls_1); 
  
 process.waitfor( );


4、用DEL硬删除aaa.doc

P

rocess process = Runtime.getRuntime().exec("cmd /c del f:\\aaa.doc");

5、从JDK1.5之后,Java开始提供包:java.lang.management

java.lang.management 提供了一系列的用来在运行时管理和监督JVM和OS的管理接口。

将用到的就是这个包中的一个类:ManagementFactory。 


    JVM运行在操作系统上,它只能依赖本地函数库去获取本机信息。其中用到2个不可或缺的类就是Runtime和Process。

  Java.lang.Runtime类


一、概述

应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。
      一般不能实例化一个Runtime对象,应用程序也不能创建自己的 Runtime 类实例,但可以通过 getRuntime 方法获取当前Runtime运行时对象的引用。
      一旦得到了一个当前的Runtime对象的引用,就可以调用Runtime对象的方法去控制Java虚拟机的状态和行为。 
      当Applet和其他不被信任的代码调用任何Runtime方法时,常常会引起SecurityException异常。

二、API预览
    addShutdownHook(Thread hook) 
      注册新的虚拟机来关闭挂钩。 
    availableProcessors() 
      向 Java 虚拟机返回可用处理器的数目。 
    exec(String command) 
      在单独的进程中执行指定的字符串命令。 
    exec(String[] cmdarray) 
      在单独的进程中执行指定命令和变量。 
    exec(String[] cmdarray, String[] envp) 
      在指定环境的独立进程中执行指定命令和变量。 
    exec(String[] cmdarray, String[] envp, File dir) 
      在指定环境和工作目录的独立进程中执行指定的命令和变量。 
    exec(String command, String[] envp) 
      在指定环境的单独进程中执行指定的字符串命令。 
    exec(String command, String[] envp, File dir) 
      在有指定环境和工作目录的独立进程中执行指定的字符串命令。 
    exit(int status) 
      通过启动虚拟机的关闭序列,终止当前正在运行的Java 虚拟机。 
    freeMemory() 
      返回 Java 虚拟机中的空闲内存量。 
    gc() 
      运行垃圾回收器。 
    InputStream getLocalizedInputStream(InputStream in) 
      已过时。 从 JDK 1.1 开始,将本地编码字节流转换为 Unicode 字符流的首选方法是使用 InputStreamReader 和 BufferedReader 类。 
    OutputStream getLocalizedOutputStream(OutputStream out) 
      已过时。 从 JDK 1.1 开始,将 Unicode 字符流转换为本地编码字节流的首选方法是使用OutputStreamWriter、BufferedWriter 和 PrintWriter 类。 
    getRuntime() 
      返回与当前 Java 应用程序相关的运行时对象。 
    halt(int status) 
      强行终止目前正在运行的 Java 虚拟机。 
    load(String filename) 
      加载作为动态库的指定文件名。 
    loadLibrary(String libname) 
      加载具有指定库名的动态库。 
    maxMemory() 
      返回 Java 虚拟机试图使用的最大内存量。 
    removeShutdownHook(Thread hook) 
      取消注册某个先前已注册的虚拟机关闭挂钩。 
    runFinalization() 
      运行挂起 finalization 的所有对象的终止方法。 
    runFinalizersOnExit(value) 
      已过时。 此方法本身具有不安全性。它可能对正在使用的对象调用终结方法,而其他线程正在操作这些对象,从而导致不正确的行为或死锁。 
    totalMemory() 
      返回 Java 虚拟机中的内存总量。 
    traceInstructions(on) 
      启用/禁用指令跟踪。 
    traceMethodCalls(on) 
      启用/禁用方法调用跟踪。

三、常见的应用

1、内存管理:
Java提供了无用单元自动收集机制。通过totalMemory()和freeMemory()方法可以知道对象的堆内存有多大,还剩多少。
Java会周期性的回收垃圾对象(未使用的对象),以便释放内存空间。但是如果想先于收集器的下一次指定周期来收集废弃的对象,可以通过调用gc()方法来根据需要运行无用单元收集器。一个很好的试验方法是先调用gc()方法,然后调用freeMemory()方法来查看基本的内存使用情况,接着执行代码,然后再次调用freeMemory()方法看看分配了多少内存。下面的程序演示了这个构想。


class MemoryDemo{ 
         public static void main(Stringargs[]){ 
                 Runtimer = Runtime.getRuntime(); 
                 long mem1,mem2; 
                 Integersomeints[] = new Integer[1000]; 
                 System.out.println("Totalmemory is  :" +r.totalMemory()); 
                 mem1= r.freeMemory(); 
                 System.out.println("Initialfree is : " + mem1); 
                 r.gc(); 
                 mem1= r.freeMemory(); 
                 System.out.println("Freememory after garbage collection : " +mem1); 
                 //allocateintegers 
                 for(int i=0;i<1000; i++) someints[i] = new Integer(i);    
                 mem2= r.freeMemory(); 
                 System.out.println("Freememory after allocation : " + mem2); 
                 System.out.println("Memoryused by allocation : " +(mem1-mem2));    
                 //discardIntergers 
                 for(int i=0;i<1000; i++) someints[i] = null; 
                 r.gc(); //requestgarbage collection 
                 mem2= r.freeMemory(); 
                 System.out.println("Freememory after collecting " + "discardedintegers : " + mem2); 
         } 
 }

编译后运行结果如下(不同的机器不同时间运行的结果也不一定一样):
Total memory is :2031616
Initial free is : 1818488
Free memory after garbage collection : 1888808
Free memory after allocation : 1872224
Memory used by allocation : 16584
Free memory after collecting discarded integers : 1888808

2、执行其他程序
在安全的环境中,可以在多任务操作系统中使用Java去执行其他特别大的进程(也就是程序)。ecec()方法有几种形式命名想要运行的程序和它的输入参数。ecec()方法返回一个Process对象,可以使用这个对象控制Java程序与新运行的进程进行交互。ecec()方法本质是依赖于环境。
下面的例子是使用ecec()方法启动windows的记事本notepad。这个例子必须在Windows操作系统上运行。


class ExecDemo { 
         public static void main(Stringargs[]){ 
                 Runtimer = Runtime.getRuntime(); 
                 Processp = null; 
                 try{ 
                         p= r.exec("notepad"); 
                 } catch (Exceptione) { 
                         System.out.println("Errorexecuting notepad."); 
                 } 
         } 
 }

ecec()还有其他几种形式,例子中演示的是最常用的一种。ecec()方法返回Process对象后,在新程序开始运行后就可以使用Process的方法了。可以用destory()方法杀死子进程,也可以使用waitFor()方法等待程序直到子程序结束,exitValue()方法返回子进程结束时返回的值。如果没有错误,将返回0,否则返回非0。下面是关于ecec()方法的例子的改进版本。例子被修改为等待,直到运行的进程退出:


class ExecDemoFini {
     public static void main(String args[]){
         Runtime r = Runtime.getRuntime();
         Process p = null;
         try{
             p =r.exec("notepad");
             p.waitFor();
         } catch (Exception e) {
            System.out.println("Error executing notepad.");
         }
         System.out.println("Notepadreturned " + p.exitValue());
     }
 }


下面是运行的结果(当关闭记事本后,会接着运行程序,打印信息):
Notepad returned 0
请按任意键继续. . .

当子进程正在运行时,可以对标准输入输出进行读写。getOutputStream()方法和getInPutStream()方法返回对子进程的标准输入和输出。

 
  Java.lang.Process类

一、概述
      Process类是一个抽象类(所有的方法均是抽象的),封装了一个进程(即一个执行程序)。
      Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。
      ProcessBuilder.start() 和Runtime.exec 方法创建一个本机进程,并返回 Process 子类的一个实例,该实例可用来控制进程并获取相关信息。

     创建进程的方法可能无法针对某些本机平台上的特定进程很好地工作,比如,本机窗口进程,守护进程,Microsoft Windows 上的 Win16/DOS 进程,或者 shell 脚本。创建的子进程没有自己的终端或控制台。它的所有标准 io(即 stdin,stdout,stderr)操作都将通过三个流 (getOutputStream(),getInputStream(),getErrorStream()) 重定向到父进程。父进程使用这些流来提供到子进程的输入和获得从子进程的输出。因为有些本机平台仅针对标准输入和输出流提供有限的缓冲区大小,如果读写子进程的输出流或输入流迅速出现失败,则可能导致子进程阻塞,甚至产生死锁。

     当没有 Process 对象的更多引用时,不是删掉子进程,而是继续异步执行子进程。

二、API预览
 destroy()
   杀掉子进程。
 exitValue()
   返回子进程的出口值。
 InputStream getErrorStream()
   获得子进程的错误流。
 InputStream getInputStream()
   获得子进程的输入流。
 OutputStream getOutputStream()
   获得子进程的输出流。
 waitFor()
   导致当前线程等待,如果必要,一直要等到由该 Process 对象表示的进程已经终止。

三、如何创建Process对象,一般有一下两种方法:

1、每个 ProcessBuilder 实例管理一个进程属性集。start() 方法利用这些属性创建一个新的 Process 实例。start() 方法可以从同一实例重复调用,以利用相同的或相关的属性创建新的子进程。(ProcessBuilder这个是JDK5中新添加的final类)。

2、Runtime.exec()方法创建一个本机进程,并返回 Process 子类的一个实例。。

四、实例

packagecom.ljheee.os.core;

importjava.io.BufferedReader;

importjava.io.IOException;

importjava.io.InputStream;

importjava.io.InputStreamReader;

 

/**
 * 控制台处理工具箱
 * 
 */
public finalclass CmdToolkit {
 
    /**
     * 读取控制命令的输出结果
     * 
     * @param cmd
     *           命令:String,比如可以是“notepad”
     * @param isPrettify
     *           返回的结果是否进行美化(换行),美化意味着换行,默认不进行美化,当此参数为null时也不美化,
     * @return 控制命令的输出结果
     * @throws IOException
     */
    public staticString readConsole(String cmd, Boolean isPrettify) throws IOException {
       StringBuffer cmdout= new StringBuffer();
       Process process= Runtime.getRuntime().exec(cmd); // 执行一个系统命令
       InputStream fis= process.getInputStream();
       BufferedReaderbr = new BufferedReader(new InputStreamReader(fis));
       String line =null;
       
       if (isPrettify== null || isPrettify) {
           while((line = br.readLine()) !=  null) {
              cmdout.append(line);
           }
       } else{
           while((line = br.readLine()) !=  null) {
              cmdout.append(line).append(System.getProperty("line.separator"));
           }
       }
       return cmdout.toString().trim();
    }
}


进程管理器源码: