【知识回顾】Java常用类库-Java Runtime

本文详细介绍了JavaRuntime类的使用,包括其基本概念、常用方法(如获取系统信息、执行外部脚本和动态加载类)、执行系统命令的注意事项,以及如何在实际业务场景中应用。同时提到了Runtime类的单例特性及其在安全性和性能优化中的考虑。
摘要由CSDN通过智能技术生成

在这里插入图片描述

一、快速入门

1.1 Runtime 介绍

  Java Runtime 类是 Java 标准库中的关键类之一,它提供了对当前Java虚拟机实例的访问和控制,允许程序动态地修改和管理运行时环境。每个Java应用程序都有一个Runtime类实例,使得程序能够与其运行的环境相连接。
  Runtime类所在包为java.lang包,因此在使用的时候不需要进行导包。并且Runtime类被public修饰了,因此该类是可以被继承的。

在这里插入图片描述

1.2 常用方法

  程序中一般不能显式的主动实例化一个Runtime实例,而是通过Runtime.getRuntime()来获取当前程序的Runtime实例。通过getRuntime()获取到当前程序的运行环境之后,就可以做很多事了,比如说调用运行环境,执行一些cmd任务,或者外部的脚本等。由于Runtime类封装了虚拟机进程,因此,在程序中通常会通过该类的实例对象来获取当前虚拟机的相关信息。其常用方法和功能如下表所示:

1.2.1 基本方法

方法描述
static Runtime getRuntime()获取当前Java应用程序相关的运行时对象

1.2.2 行为控制类

方法描述
void addShutdownHook(Thread hook)注册新的虚拟机来关闭挂钩
boolean removeShutdownHook(Thread hook)移除一个虚拟机关机任务,与addShutdownHook作用相反
void exit(int status)通过启动虚拟机的关闭序列,终止当前正在运行的Java虚拟机
void gc()调用该方法可以触发垃圾回收器的执行,以回收不再使用的对象占据的内存空间
System.gc()就是调用的Runtime的gc()函数
void halt(int status)强行终止目前正在运行的Java虚拟机

1.2.3 系统信息类

方法说明
int availableProcessors()向 Java 虚拟机返回可用处理器的数目
long freeMemory()返回当前Java虚拟机中的空闲内存量
long maxMemory()返回当前Java虚拟机可以使用的最大内存量,即堆的最大容量
long totalMemory()返回当前Java虚拟机当前已经使用的总内存量

注意:Runtime 类的 freeMemory、totalMemory、maxMemory三个方法反映的都是 Java 这个进程的内存情况,跟操作系统的内存根本没有关系。

1.2.4 exec

重载的exec()函数,可以执行指定的参数来达到执行cmd命令的目的。

方法说明
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)在有指定环境和工作目录的独立进程中执行指定的字符串命令

1.2.5 其他方法

方法说明
load(String filename)加载作为动态库的指定文件名
loadLibrary(String libname)加载具有指定库名的动态库
runFinalization()运行挂起finalization的所有对象的终止方法
traceInstructions(on)启用/禁用指令跟踪
traceMethodCalls(on)启用/禁用方法调用跟踪

1.2.6 注意事项

  这些方法允许Java应用程序与其运行时环境进行交互,获取内存使用情况、执行系统命令等操作。需要注意的是,在使用exec()方法执行系统命令时,可能需要处理输入流和输出流,以便与子进程进行通信,并正确处理可能的异常情况。在runtime执行大点的命令中,输入流和错误流会不断有流进入存储在JVM的缓冲区中,如果缓冲区的流不被读取被填满时,就会造成runtime的阻塞。所以在进行比如:大文件复制等的操作时,我们还需要不断的去读取JVM中的缓冲区的流,来防止Runtime的死锁阻塞。

二、基本使用

2.1 获取当前虚拟机信息

Runtime类可以获取当前Java虚拟机的处理器的个数、空闲内存量、最大可用内存量和内存总量的信息。

public static void main(String[] args) {
    // 获取当前系统的运行环境对象
    Runtime rt = Runtime.getRuntime();
	System.out.println("处理器的个数: " + rt.availableProcessors() + "个");
	System.out.println("空闲内存数量: " + rt.freeMemory() / 1024 / 1024 + "M");
	System.out.println("最大可用内存数量: " + rt.maxMemory() / 1024 / 1024 + "M");
	System.out.println("虚拟机中内存总量: " + rt.totalMemory() / 1024 / 1024 + "M");
}

由于每个人的机器配置不同,该示例的打印结果可能不同,另外空闲内存数、可用最大内存数和内存总量都是以字节为单位计算的,上述运行结果已经将字节换算成了兆(M)。

2.2 操作系统进程

  Runtime类中提供了一个exec()方法,该方法用于执行指定的字符串命令,从而实现和在命令行窗口中输入命令同样的效果。该方法返回一个代表进程的Process对象,可以通过该对象获取进程的输入流、输出流和出错流,并与进程进行交互。例如,通过运行“notepad.exe”命令打开一个Windows自带的记事本程序。程序运行后,会在Windows系统中产生一个新的进程notepad.exe,可以通过任务管理器进行观察。

public static void main(String[] args) throws IOException {
	// 创建Runtime实例对象
	Runtime rt = Runtime.getRuntime();
	// 调用exec()方法
	rt.exec("notepad.exe");
}

  Runtime 是 Java 提供的一个启动子进程来执行命令的方式,它提供了以下六个重载的 exec 方法,用于单独启动一个子进程来执行命令或调用程序。每一个方法最终都返回一个 Process 对象表示一个进程,从该对象中能够获取进程执行的退出状态码、标准输出流和标准错误流,进而获取进程执行的输出内容。

public Process exec(String command) throws IOException
public Process exec(String command, String[] envp) throws IOException
public Process exec(String command, String[] envp, File dir) throws IOException
public Process exec(String cmdarray[]) throws IOException
public Process exec(String[] cmdarray, String[] envp) throws IOException
public Process exec(String[] cmdarray, String[] envp, File dir) throws IOException

  其中,前五个方法都是间接调用最后一个方法,该方法有 3 个参数:

参数说明
cmdarray命令字符串数组
envp字符串数组(可以为空),表示命令执行过程中设置的环境变量
dir一个File对象(可以为空),表示命令执行的工作目录。
如果命令中有使用到类似于./的相对路径,则该相对路径就是基于dir的

这里需要特殊说明一下,单字符串命令和命令数组的区别,总结如下:

  • 如果执行的命令的参数中包含空格一定要使用 exec(String[] cmdarray, String[] envp, File dir),否则将导致命令执行失败。
  • 如果执行的命令的参数中没有空格不会导致参数分裂,则两个方法都一样

2.3 Process对象

  查阅API文档会发现,Runtime类的exec()方法返回一个Process对象,该对象就是exec()所生成的新进程,通过该对象可以对产生的新进程进行管理,如关闭此进程只需任务管理器中终止记事本进程的命令即可。具有代码如下所示:

public static void main(String[] args) {
	try {
		Process process = Runtime.getRuntime().exec("notepad.exe");

		// 休眠3秒
		Thread.sleep(3000);

		// 执行任务管理器中终止记事本进程的命令
		Runtime.getRuntime().exec("taskkill /F /IM notepad.exe");
	} catch (IOException e) {
		e.printStackTrace();
	} catch (InterruptedException e) {
		e.printStackTrace();
	}
}

Process的几种方法

方法说明
destroy()杀掉子进程
exitValue()返回子进程的出口值,值0表示正常终止
getErrorStream()获取子进程的错误流
getInputStream()获取子进程的输入流
getOutputStream()获取子进程的输出流
waitFor()导致当前线程等待,如有必要,一直要等到由该Process对象表示的进程已经终止。
如果已终止该子进程,此方法立即返回。
如果没有终止该子进程,调用的线程将被阻塞,直到退出子进程,根据惯例,0表示正常终止

三、业务场景实战

3.1 执行外部脚本

下面是一个演示如何使用 Runtime 类来执行外部脚本的 Java 实例程序:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class ExecuteScriptExample {
    public static void main(String[] args) {
        Runtime runtime = Runtime.getRuntime();
        try {
            // 使用 runtime.exec() 方法执行外部脚本
            Process process = runtime.exec("python script.py");

            // 获取外部脚本的输入流,并创建 BufferedReader 对象用于读取输出结果
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            
            String line;
            while ((line = reader.readLine()) != null) {
                // 处理外部脚本输出的每一行
                System.out.println(line);
            }

            // 等待外部进程结束并获取退出值
            int exitCode = process.waitFor();
            
            if (exitCode == 0) {
                // 外部脚本执行成功
                System.out.println("External script executed successfully.");
            } else {
                // 外部脚本执行失败
                System.err.println("External script execution failed. Exit code: " + exitCode);
            }
        } catch (IOException | InterruptedException e) {
            // 处理异常
            e.printStackTrace();
        }
    }
}

  这个实例展示了如何在 Java 中使用 Runtime 类来执行外部脚本,并处理输出结果和异常情况。注意,使用 runtime.exec() 执行外部命令需要谨慎处理,特别是针对从用户输入或不受信任的源代码中派生的命令。建议对脚本及其参数进行适当的验证和限制,以确保安全性。

3.2 动态加载类

下面是一个演示如何使用 Runtime 类结合反射机制来动态加载类的Java实例程序:

import java.lang.reflect.Method;

public class DynamicClassLoadingExample {
    public static void main(String[] args) {
        try {
            // 创建Runtime对象
            Runtime runtime = Runtime.getRuntime();

            // 加载需要动态加载的类的全限定名
            String className = "com.example.MyClass";

            // 使用ClassLoader动态加载类
            Class<?> dynamicClass = runtime.getClass().getClassLoader().loadClass(className);

            // 创建类的实例
            Object instance = dynamicClass.newInstance();

            // 调用类中的方法
            Method method = dynamicClass.getDeclaredMethod("doSomething");
            method.invoke(instance);
        } catch (ClassNotFoundException e) {
            // 处理类未找到异常
            e.printStackTrace();
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            // 处理其他异常
            e.printStackTrace();
        }
    }
}

  这个实例演示了如何使用 Runtime 类动态加载类,并调用其中的方法。需要注意,动态加载类在某些场景下是有用的,但也应谨慎使用。合理地处理异常情况以及对类名、方法等进行适当的验证和限制是非常重要的。

四、小结

  在学习面向对象概念时曾经强调过一个概念,一个类中至少会存在一个构造方法, 如果本类没有定义任何构造方法,那么会自动生成一个无参的构造方法。 但是当打开 Runtime类时会发现一个问题,在这个类中并没有构造方法的定义说明,可是这个类的构造方法却是真实存在的,因为其在声明时对构造方法进行了封装 所以Runtime类是一个典型的单例设计模式。
  Java Runtime 类作为Java虚拟机的实例,对于动态地控制运行时环境至关重要。通过提供各种功能和方法,例如执行外部命令、管理内存和垃圾回收,以及动态加载和卸载类,Runtime 类使得Java程序能够更加灵活地与底层系统进行交互。然而,开发人员需要注意安全性、性能优化及平台依赖性等方面,以确保代码的可靠性和效率。熟练使用 Runtime 类,并根据具体需求进行合理的优化,将有助于实现更强大和高效的Java应用程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

独泪了无痕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值