Runtime详解,一文带你打通Java Runtime类及其相关知识。

本文的宗旨是带你了解最全面的知识,让你能将学到的知识串联起来,构建一个完整的知识体系。

什么是Runtime

Runtime(运行时),每个Java程序在运行时都相当于启动了一个JVM实例,每个JVM实例都对应一个Runtime对象。Runtime对象是由JVM负责实例化的,因此我们无法通过传统的方式实例化一个Runtime对象,只能通过调用getRuntime()方法来获取当前运行时的Runtime对象的引用。

Runtime对象如何获取

Runtime获取
查看Runtime类源码,我们可以发现Runtime只有一个空参的私有构造器,这意味着我们无法使用new来获取对象的实例,那么我们如何获取Runtime对象呢?这就引出了设计模式的概念。
Runtime类使用了设计模式中的饿汉模式来保证只能存在唯一一个对象实例

什么是设计模式?

设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。
拿饿汉模式举例,饿汉模式属于设计模式中的单例设计模式,单例设计模式保证一个类只有一个实例。

回到代码中我们发现,由于Java将Runtime类的构造器设置为private,导致我们无法使用new来获取Runtime对象实例,那么我们就需要对外暴露一个新的方法来让外界可以获取Runtime对象。
这个时候就可以想到,使用static关键字修饰的方法可以通过类名.方法名直接调用,那么我们就可以通过设置一个public的静态方法来让外界调用。

解决了方法调用的问题,我们又遇到问题了,如何保证一个类仅仅只有一个实例呢?
如果我们让类保存自己的实例对象,并且在外界想要获取实例对象时将自己保存的实例对象交付出去,是不是就保证一个类只有一个实例了呢?

现在我们就明白了单例设计模式的基本思路,通过类自己来管理自己的实例,保证实例的唯一性。

值得注意的是,如果想在类中保存一个自己的实例,如果直接new,可能导致StackOverflowError错误,因为Test在实例化的过程中还会再次递归地实例化自己。正确的方法是在构造器中将实例对象赋值给成员变量,或者使用static关键字保证成员变量唯一
在这里插入图片描述在这里插入图片描述

什么是饿汉模式

我们刚刚已经知道了单例设计模式的设计思路,那么我们继续了解两种单例设计模式,懒汉模式和饿汉模式。

  • 懒汉模式
    顾名思义,懒汉模式指这个类非常懒惰,它只有在真正需要这个类的实例时才会去实例化对象。如图所示,只有在调用getInstance()方法时才会实例化Singleton2并将其赋值给成员变量并返回
    在这里插入图片描述
  • 饿汉模式
    饿汉模式指这个类太饿了,它想要早点获得实例对象。可以看到,饿汉模式直接就将实例对象赋给了静态的成员变量。
    在这里插入图片描述
  • 测试是否是单例的
    在这里插入图片描述
    在这里插入图片描述

Runtime类有哪些方法,有什么用?

https://www.geeksforgeeks.org/java-lang-runtime-class-in-java/
以下内容来自机翻,大多数都很好理解,后面会详细介绍常用方法exec()。

方法执行的操作
addShutdownHook(Thread hook)注册新的虚拟机关闭挂钩线程。
availableProcessors()返回 JVM(Java 虚拟机)可用的处理器数
exec(字符串命令)在单独的进程中执行给定的命令
exec(字符串[] cmd)在单独的进程中执行指定的命令和参数。
exec(String 命令, String[] envp, File dir)在具有指定环境和工作目录的单独进程中执行指定的字符串命令。
exec(字符串命令,字符串[] envp)在具有指定环境的单独进程中执行指定的字符串命令。
exec(String[] cmdarray, String[] envp, 文件目录)在具有指定环境和工作目录的单独进程中执行指定的命令和参数。
exec(字符串[] cmdarray, 字符串[] envp)在具有指定环境的单独进程中执行指定的命令和参数。
exit(int 状态)通过启动当前正在运行的 Java 虚拟机的关闭序列来终止该虚拟机。
freeMemory() 函数返回 JVM(Java 虚拟机)中的可用内存量
gc()运行垃圾回收器。调用此方法表明 Java 虚拟机将工作扩展到回收未使用的对象,以便使它们当前占用的内存可供快速重用。
getRuntime() 函数返回与当前 Java 应用程序关联的实例或运行时对象
halt(int 状态)强制终止当前正在运行的 Java 虚拟机。此方法从不正常返回。使用此方法时应格外小心。
load(字符串文件名)将指定的文件名加载为动态库。filename 参数必须是完整的路径名。
loadLibrary(字符串库名)加载具有指定库名称的动态库。包含代码的文件是从本地系统从通常获取库文件的位置加载的。
maxMemory()返回 Java 虚拟机将尝试使用的最大内存量。如果没有固有限制,则将返回值 Long.MAX_VALUE
removeShutdownHook(线程钩子)取消注册以前注册的虚拟机关闭挂钩。
runFinalization()运行任何待定终结的对象的终结方法。它表明 JVM(Java 虚拟机)将工作扩展到运行已发现已丢弃但其 finalize 方法尚未运行的对象的 finalize 方法。
totalMemory()返回 JVM(Java 虚拟机)中的总内存量
traceInstructions(布尔值 a)启用或禁用指令跟踪。如果布尔参数为 true,那么它将建议 JVM 在执行虚拟机中的每条指令时发出调试信息。
traceMethodCalls(布尔值 a)启用或禁用对方法调用的跟踪。如果布尔参数为 true,则它将建议 Java 虚拟机在调用虚拟机时发出虚拟机中每个方法的调试信息。

addShutdownHookhook(钩子)是一种特殊的消息处理机制,它可以监视系统或者进程中的各种事件消息,截获发往目标窗口的消息并进行处理。简单来说就是hook会监视某些特定的行为,并在这些行为出现时被触发,可以将它想象成一个陷阱。例如各种杀毒软件以及计算机病毒都会使用到这种技术。

native

观察源码我们发现,Runtime类中有许多方法用到了native关键字修饰,那么它有什么用呢?
一个Native修饰的方法是java调用非java代码的接口,比如C++。
他有具体的实现,但是实现的语言不是java,这主要是因为java是运行在JVM虚拟机中的,这虽然让java可以在许多平台上运行,但是如果要调用操作系统或者java环境外的方法时就显得比较无力,这时候就需要使用其他语言来帮助我们完成需要的操作。
简单来说,native方法的实现是借助了其他语言,脱离了java环境。

Runtime.getRuntime().exec()

作用:用于调用外部程序,并重定向外部程序的标准输入、标准输出和标准错误到缓冲池。功能就是和windows的“运行”(键盘按键 WINDOWS + R)一样。
该方法会抛出IOException异常

// 在单独的进程中执行指定的外部可执行程序的启动路径或字符串命令
public Process exec(String command)
// 在单独的进程中执行指定命令和变量
public Process exec(String[] cmdArray)
// 在指定环境的独立进程中执行指定命令和变量
public Process exec(String command, String[] envp)
// 在指定环境的独立进程中执行指定的命令和变量
public Process exec(String[] cmdArray, String[] envp)
// 在指定环境和工作目录的独立进程中执行指定的字符串命令
public Process exec(String command, String[] envp, File dir)
// 在指定环境和工作目录的独立进程中执行指定的命令和变量
public Process exec(String[] cmdarray, String[] envp, File dir

参数说明:

  • cmdarray 包含所调用命令及其参数的数组。数组第一个元素是命令,其余是参数。
  • envp 字符串数组,其中每个元素的环境变量的设置格式为 name=value,如果子进程应该继承当前进程的环境,则该参数为null。
  • dir 子进程的工作目录,如果子进程应该继承当前进程的工作目录,则该参数为null。

比如我们可以通过Runtime.getRuntime().exec("notepad");来调用windows中的记事本。

假如我现在有一个python文件D:\CS\hello.py,作用是输出字符串hello World,我想通过Runtime.getRuntime().exec()调用并获取输出。

        try {
            // 构建命令
            String command = "python D:\\CS\\hello.py";

            // 执行命令并获取输出
            Process process = Runtime.getRuntime().exec(command);
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null){
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

如果你想直接通过python命令来调用python程序,你需要将python添加到系统环境变量中。

ProcessBuilder

观察源码我们知道,Runtime.getRuntime().exec("")是调用ProcessBuilder类实现的,并返回一个Proces对象。这涉及到设计模式中的建造者模式,我将在下一篇文章中对这个类以及设计模式进行详细介绍。

我在使用过程中遇到的坑

重点!!!!!

  1. Runtime.getRuntime().exec()无法实时读取Python脚本的输出,而是要等到Python脚本运行完毕才会读取到输出。
    解决办法:使用 python -u参数,因为Python会默认将输出进行缓存,当使用"-u"选项时,Python将立即将输出发送到标准输出流,而不是等到缓冲区装满后再进行输出。这在某些情况下很有用,特别是当你希望实时查看程序输出时。
  2. 程序阻塞,所有进程都正常运行但是java获取不到输出。
    通过 Process实例.getInputStream() 和 Process实例.getErrorStream() 获取的输入流和错误信息流是缓冲池向当前Java程序提供的,而不是直接获取外部程序的标准输出流和标准错误流。
    而缓冲池的容量是一定的,因此若外部程序在运行过程中不断向缓冲池输出内容,当缓冲池填满,那么外部程序将暂停运行直到缓冲池有空位可接收外部程序的输出内容为止。

    解决办法:新建线程持续读取输入输出流。
  3. 使用cmd命令但启动的新进程无法自动关闭
    使用cmd /k会导致进程不自动关闭,应该使用cmd /c

如果看到这里的话,能帮忙点个赞就太感谢了。如果觉得这篇文章不错,也可以点个关注,之后也会进行更多知识分享。您的支持是我继续创作的动力,十分感谢!

参考资料
https://blog.csdn.net/zjt1388/article/details/82656484
https://blog.csdn.net/qq_30336433/article/details/89634366
https://blog.csdn.net/footless_bird/article/details/116274515
https://blog.csdn.net/zhuwei1035838807/article/details/79464603
https://cloud.tencent.com/developer/article/1414936
https://www.geeksforgeeks.org/java-lang-runtime-class-in-java/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值