依旧是跟着 feng 师傅的博客进行学习,过一遍feng师傅的笔记,再写到自己的博客上
Runtime 类 ,就是运行时环境
- public class Runtime {
- private static final Runtime currentRuntime = new Runtime();
-
- private static Version version;
-
- public static Runtime getRuntime(){
- return currentRuntime;
- }
- private Runtime(){}
- }
这种涉及模式叫做单例模式:
- 单例模式(Singleton)的目的是为了保证在一个进程中,某个类有且仅有一个实例。
-
- 因为这个类只有一个实例,因此,自然不能让调用方使用new Xyz()来创建实例了。所以,单例的构造方法必须是private,这样就防止了调用方自己创建实例,但是在类的内部,是可以用一个静态字段来引用唯一创建的实例的。
-
- 那么问题来了,外部调用方如何获得这个唯一实例?
-
- 答案是提供一个静态方法,直接返回实例:
-
- 或者直接把static变量暴露给外部:
java 进程种 仅 有一个 Runtime 类对象,这个对象通过 getRuntime 方法 获得
- private static final Runtime currentRuntime = new Runtime();
- public static Runtime getRuntime() {
- return currentRuntime;
- }
命令执行时 就是使用的exec方法,该方法有如下几种重载
- 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)----在指定环境和工作目录的独立进程中执行指定的命令和变量
ctrl + B 进入 exec 跟进源码
- public Process exec(String command) throws IOException {
- return exec(command, null, null);
- }
继续 跟进exec
- public Process exec(String command, String[] envp, File dir)
- throws IOException {
- if (command.isEmpty())
- 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);
- }
继续跟进 exec
- public Process exec(String[] cmdarray, String[] envp, File dir)
- throws IOException {
- return new ProcessBuilder(cmdarray)
- .environment(envp)
- .directory(dir)
- .start();
- }
跟进到这,返回了一个 ProcessBuilder,看这英文,是进程?继续跟进下面的.start()方法

产生了一个 ProcessImpl 对象,调用了他的start方法,跟进:

该方法返回了一个 ProcessImpl对象
至此 链子返回,总体流程是(借用feng师傅的图):

正常的命令执行:
Runtime.getRuntime().exec("whoami");
如果需要获得命令的执行结果的话,还需要处理,设置一个IO流缓冲区来读取:
- public class a {
- public static void main(String[] args) throws Exception{
-
- InputStream in = Runtime.getRuntime().exec("whoami").getInputStream();
- byte bcache [] = new byte[1024]; //设置缓冲区大小
- int readsize = 0;// 读取大小
- try(ByteArrayOutputStream outputStream = new ByteArrayOutputStream()){
- while ((readsize = in.read(bcache)) != -1){
- outputStream.write(bcache,0,readsize);
- }
- System.out.println(outputStream.toString()); //打印
- }}}
exec 方法返回的的是一个 Process 对象,因此他有这些方法:
- destroy()
- 杀掉子进程。
- exitValue()
- 返回子进程的出口值。
- InputStream getErrorStream()
- 获得子进程的错误流。
- InputStream getInputStream()
- 获得子进程的输入流。
- OutputStream getOutputStream()
- 获得子进程的输出流。
- waitFor()
- 导致当前线程等待,如果必要,一直要等到由该 Process 对象表示的进程已经终止。
- public class a {
- public static void main(String[] args) throws Exception {
- //Runtime.getRuntime().exec("whoami");
- Class> clazz = Class.forName("java.lang.Runtime");
- Method execMethod = clazz.getMethod("exec", String.class);
- Method getRuntimeMethod = clazz.getMethod("getRuntime");
- Object runtime = getRuntimeMethod.invoke(Runtime.class);
- Process inProcess = (Process) execMethod.invoke(runtime, "whoami"); //需要类型转换
- //打印 exec命令返回结果
- InputStream in = inProcess.getInputStream();
- byte[] bcache = new byte[1024];
- int readSize = 0;
- try(ByteArrayOutputStream outputStream = new ByteArrayOutputStream()){
- while ((readSize =in.read(bcache))!=-1){
- outputStream.write(bcache,0,readSize);
- }
- System.out.println(outputStream.toString());}}}
- public class a {
- public static void main(String[] args) throws Exception {
- //Runtime.getRuntime().exec("whoami");
- Class> clazz = Class.forName("java.lang.Runtime");
- Constructor> cons = clazz.getDeclaredConstructor();
- cons.setAccessible(true);
- Object runtime = cons.newInstance();
- Method exec = clazz.getMethod("exec", String.class);
- Process inprocess = (Process) exec.invoke(runtime, "whoami");
- //打印返回结果
- InputStream in = inprocess.getInputStream();
- byte[] bcache = new byte[1024];
- int readSize = 0;
- try(ByteArrayOutputStream outputStream = new ByteArrayOutputStream()){
- while ((readSize =in.read(bcache))!=-1){
- outputStream.write(bcache,0,readSize);
- }
- System.out.println(outputStream.toString());
- }}}
现在终于明白 该如何获得 通过这两者的方式获得 对象并调用了呜呜呜。。。这里需要注意的是,因为是单例模式,Runtime类的构造器是private 的,所以需要 setAccessible(true),而且需要用getDeclaredConstructor,getConstructor只能获得public的。
exec 不能应付复杂的命令执行,参考文章:
Java Runtime.getRuntime().exec由表及里 - 先知社区
又是看底层,看不懂, 但一般的解决方法是换一个重载器的exec()
public Process exec(String [] cmdArray)---在单独的进程中执行指定命令和变量
以数组的形式存在:
- String[] command = { "/bin/sh", "-c", "echo 2333 2333 2333 && echo 2333 2333 2333" };
- InputStream in = Runtime.getRuntime().exec(command).getInputStream();
因为 exec 的链子在途中 会调用 类的 Start() 方法
- public Process exec(String[] cmdarray, String[] envp, File dir)
- throws IOException {
- return new ProcessBuilder(cmdarray)
- .environment(envp)
- .directory(dir)
- .start();
- }
因此 我们也可以通过 来实现命令执行,根据逻辑,先创建 ProcessBuilder 对象的时候传入的 参数是要执行的命令,然后调用start方法执行。
ProcessBuilder 的构造器:
- public ProcessBuilder(String... command) {
- this.command = new ArrayList<>(command.length);
- for (String arg : command)
- this.command.add(arg);
- }
-
- public ProcessBuilder command(List
command) { - if (command == null)
- throw new NullPointerException();
- this.command = command;
- return this;
- }
和exec 差不多 可以调用 String的 也可以传入数组。
start 方法也是返回一个 Process对象 :
- public Process start() throws IOException {
- return start(redirects);
- }
- InputStream in = new ProcessBuilder("whoami").start().getInputStream();
- byte[] bcache = new byte[1024];
- int readSize = 0;
- try(ByteArrayOutputStream out = new ByteArrayOutputStream()){
- while( (readSize = in.read(bcache)) != -1){
- out.write(bcache,0,readSize);
- }
- System.out.println(out.toString());
- }
- Class clazz = Class.forName("java.lang.ProcessBuilder");
- Constructor cons = clazz.getConstructor(String[].class);
- Method startMethod = clazz.getMethod("start");
- Object processBuilder = cons.newInstance(new String[][]{{"whoami"}});
- InputStream in = ((Process) startMethod.invoke(processBuilder)).getInputStream();
- byte[] bcache = new byte[1024];
- int readSize = 0;
- try(ByteArrayOutputStream out = new ByteArrayOutputStream()){
- while ( (readSize = in.read(bcache))!= -1){
- out.write(bcache,0,readSize);
- }
- System.out.println(out.toString());
- }
此处:
InputStream in = ((Process) startMethod.invoke(processBuilder)).getInputStream();
因为 newinstance 可以接收一个可变长度的object ,而这个构造器传入的可以是一个可变长度的String,叠加起来是一个二维数组。