前言:
现时你们对“java注销”大致比较注重,咱们都需要剖析一些“java注销”的相关资讯。那么小编同时在网摘上网罗了一些对于“java注销””的相关内容,希望兄弟们能喜欢,大家一起来了解一下吧!本文非原创,转载自:
-----------------------------------------------------
简介
Runtime类封装了运行时的环境。每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。
一般不能实例化一个Runtime对象,应用程序也不能创建自己的 Runtime 类实例,但可以通过 getRuntime 方法获取当前Runtime运行时对象的引用。
一旦得到了一个当前的Runtime对象的引用,就可以调用Runtime对象的方法去控制Java虚拟机的状态和行为。
一、 看源码 jdk9 :
private static final Runtime currentRuntime = new Runtime(); private static Version version; /** * Returns the runtime object associated with the current Java application. * Most of the methods of class {@code Runtime} are instance * methods and must be invoked with respect to the current runtime object. * * @return the {@code Runtime} object associated with the current * Java application. */ public static Runtime getRuntime() { return currentRuntime; } /** Don't let anyone else instantiate this class */ private Runtime() {}
每个Java应用程序都有一个类运行时实例,该实例允许应用程序与运行应用程序的环境进行接口。当前运行时可以从Runtime.getRuntime()方法获得。
应用程序不能创建该类的自身实例。
构造器私有化, 使用了单例模式 之懒汉模式;
这里有一个静态内部类version 还没懂之后再做了解;
二 、几个方法的一般使用
public class RuntimeSample { /* 获取当前JVM的内存信息,返回的值是 字节为单位 */ public static void getFreeMemory() { // 获取可用的内存 long value = Runtime.getRuntime().freeMemory(); System.out.println("可用内存为:" + value / 1024 / 1024 + "MB"); // 获取JVM的内存 总数量, 该值会不断的变化 long totalMemory = Runtime.getRuntime().totalMemory(); System.out.println("全部内存为-Xms:" + (totalMemory / (double) 1024 / 1024) + "MB"); // 获取jvm可以最大使用的内存值,如果没有被限制 返回Long.MAX_VALUE long maxMemory = Runtime.getRuntime().maxMemory(); System.out.println("最大可使用内存为-XmX:" + (maxMemory / (double) 1024 / 1024) + "MB"); } public static void main(String[] args) { //生成钩子线程 FutureTask task = new FutureTask(() -> { System.out.println("运行 shutdown hook "); System.out.println(Thread.currentThread().getName()); System.out.println(Thread.currentThread().getId()); }, "aa"); Thread thread = new Thread(task,"BB"); Runtime.getRuntime().addShutdownHook(thread); getFreeMemory(); // 获取 jvm 可用的 处理器 核数; System.out.println(Runtime.getRuntime().availableProcessors()); // 执行系统命令 try { Runtime.getRuntime().exec("calc"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 加载指定的文件名作为一个动态库 // print when the program starts System.out.println("Program starting..."); // load a library that is Windows/System32 folder System.out.println("Loading Library..."); Runtime.getRuntime().load("C:/Windows/System32/crypt32.dll"); System.out.println("Library Loaded."); // 加载动态库与指定的库名 // print when the program starts System.out.println("Program starting..."); // load a library that is Windows/System32 folder System.out.println("Loading Library..."); Runtime.getRuntime().loadLibrary("C:/Windows/System32/crypt32.dll"); System.out.println("Library Loaded."); System.exit(0); }三、方法的详解
getRuntime()
/** Returns the runtime object associated with the current Java application.Most of the methods of class Runtime are instance methods and must be invoked with respect to the current runtime object. 返回与当前Java应用关联的runtime对象。大多数Runtime类的方法是实例方法,所以必须被调用与具体的当前运行时对象。 @return the Runtime object associated with the current Java application. */ public static Runtime getRuntime() { return currentRuntime; }
availableProcessors()方法
/** Returns the number of processors available to the Java virtual machine. 返回Java虚拟机可用的处理器数。 This value may change during a particular invocation of the virtual machine. Applications that are sensitive to the number of available processors should therefore occasionally poll this property and adjust their resource usage appropriately. @return the maximum number of processors available to the virtual machine; never smaller than one @since 1.4 */ public native int availableProcessors();}
exec(String command)方法
/** Executes the specified string command in a separate process. 在一个单独的进程中执行指定的命令。 This is a convenience method. An invocation of the form exec(command) behaves in exactly the same way as the invocation #exec(String, String[], File) exec(command, null, null). 这是一个方便的方法。以exec(command)形式调用与exec(String,Stringp[],file)的表现是相同的。 @param command a specified system command.【参数:一个指定的系统命令】 @return A new Process object for managing the subprocess【返回值:一个新的用于管理子进程的的Process对象】 @throws SecurityException If a security manager exists and its {@link SecurityManager#checkExec checkExec} method doesn't allow creation of the subprocess @throws IOException If an I/O error occurs @throws NullPointerException If <code>command</code> is <code>null</code> @throws IllegalArgumentException If <code>command</code> is empty @see #exec(String[], String[], File) @see ProcessBuilder */ public Process exec(String command) throws IOException { return exec(command, null, null); }exec(String command,String[] envp, File dir)方法
/** Executes the specified command and arguments in a separate process with the specified environment and working directory. 在具有指定环境和工作目录的单独进程中执行指定的命令和参数。 Given an array of strings {@code cmdarray}, representing the tokens of a command line, and an array of strings {@code envp}, representing "environment" variable settings, this method creates a new process in which to execute the specified command. 给定一个字符串数组{@code cmdarray},表示命令行符号;一个字符串数组{@code envp},表示“环境”变量设置,此方法创建一个新进程,在其中执行指定的命令。 * This method checks that {@code cmdarray} is a valid operating system command. Which commands are valid is system-dependent, but at the very least the command must be a non-empty list of non-null strings. 这个方法检查{@code cmdarray}是否是一个有效的操作系统命令。哪些命令是有效的取决于系统,但至少该命令必须是非空字符串的非空列表。 * If {@code envp} is {@code null}, the subprocess inherits the environment settings of the current process. 如果{@code envp}是{@code null},则子进程继承当前进程的环境设置。 * A minimal set of system dependent environment variables may be required to start a process on some operating systems. As a result, the subprocess may inherit additional environment variable settings beyond those in the specified environment. 在某些操作系统上启动进程可能需要一组最小的系统相关环境变量。因此,子流程可能继承指定环境中设置之外的其他环境变量设置。 * {@link ProcessBuilder#start()} is now the preferred way to start a process with a modified environment. {@link ProcessBuilder#start()}现在是使用修改过的环境启动进程的首选方法。 * The working directory of the new subprocess is specified by {@code dir}.If {@code dir} is {@code null}, the subprocess inherits the current working directory of the current process. 新子进程的工作目录由{@code dir}指定。如果{@code dir}是{@code null},则子进程继承当前进程的当前工作目录。 * If a security manager exists, its {@link SecurityManager#checkExec checkExec} method is invoked with the first component of the array {@code cmdarray} as its argument. This may result in a {@link SecurityException} being thrown. 如果存在安全管理器,它的{@link SecurityManager#checkExec checkExec}方法将使用数组{@code cmdarray}的第一个组件作为参数来调用。这可能导致抛出{@link SecurityException}。 * Starting an operating system process is highly system-dependent. Among the many things that can go wrong are: The operating system program file was not found. Access to the program file was denied. The working directory does not exist. 启动操作系统进程高度依赖于系统。可能出错的事情有: 1. 没有找到操作系统程序文件。 2. 对程序文件的访问被拒绝。 3. 工作目录不存在。 * In such cases an exception will be thrown. The exact nature of the exception is system-dependent, but it will always be a subclass of {@link IOException}. 在这种情况下,将抛出异常。异常的确切性质依赖于系统,但它始终是{@link IOException}的子类。 * If the operating system does not support the creation of processes, an {@link UnsupportedOperationException} will be thrown. 如果操作系统不支持创建进程,将抛出{@link UnsupportedOperationException}。 * * * @param cmdarray array containing the command to call and * its arguments. * @param envp array of strings, each element of which * has environment variable settings in the format * <i>name</i>=<i>value</i>, or * {@code null} if the subprocess should inherit * the environment of the current process. * @param dir the working directory of the subprocess, or * {@code null} if the subprocess should inherit * the working directory of the current process. * @return A new {@link Process} object for managing the subprocess * @throws SecurityException * If a security manager exists and its * {@link SecurityManager#checkExec checkExec} * method doesn't allow creation of the subprocess * @throws UnsupportedOperationException * If the operating system does not support the creation of processes. * @throws IOException * If an I/O error occurs * @throws NullPointerException * If {@code cmdarray} is {@code null}, * or one of the elements of {@code cmdarray} is {@code null}, * or one of the elements of {@code envp} is {@code null} * @throws IndexOutOfBoundsException * If {@code cmdarray} is an empty array * (has length {@code 0}) * @see ProcessBuilder * @since 1.3 */ public Process exec(String[] cmdarray, String[] envp, File dir) throws IOException { return new ProcessBuilder(cmdarray) .environment(envp) .directory(dir) .start(); }
java.lang.Runtime.loadLibrary(String filename) 方法
java.lang.Runtime.loadLibrary(String filename) 方法加载动态库与指定的库名。一个包含本地代码文件从本地文件系统加载的库文件通常被那里得到的地方。这个过程的细节是依赖于实现。从库名到特定文件名的映射在系统特定的方式进行。
首先,如果有安全管理器,checkLink方法调用LibFile作为它的参数。这可能导致一个安全性异常。方法System.loadLibrary(String)是调用此方法的常规的和方便的手段。如果本机方法在类的实现中使用,一个标准的策略是把本机代码库中的文件(称之为LibFile),然后把一个静态初始化:
static { System.loadLibrary("LibFile"); }
类的声明。当类加载和初始化,必要将本机代码执行方法将被加载为好。如果这种方法被称为一次以上具有相同库名称,在第二和后续调用将被忽略。
java.lang.Runtime.exit(int status) 方法
通过发起关闭序列,终止当前正在运行的Java虚拟机。此方法从不正常返回参数用作状态码; 按照惯例,非零的状态码表示异常终止。
/** Terminates the currently running Java virtual machine by initiating its shutdown sequence. This method never returns normally. The argument serves as a status code; by convention, a nonzero status code indicates abnormal termination. 通过启动当前运行的Java虚拟机的关机顺序来终止它。这个方法永远不会正常返回。参数用作状态码;按照惯例,非零状态码表示异常终止。 * The virtual machine's shutdown sequence consists of two phases. In the first phase all registered {@link #addShutdownHook shutdown hooks}, if any, are started in some unspecified order and allowed to run concurrently until they finish. In the second phase all uninvoked finalizers are run if {@link #runFinalizersOnExit finalization-on-exit} has been enabled. Once this is done the virtual machine {@link #halt halts}. 虚拟机的关机顺序包括两个阶段。在第一阶段中,所有已注册的{@link #addShutdownHook shutdown hooks}(如果有的话)都以某种未指定的顺序启动,并允许并发运行,直到它们完成为止。在第二阶段,如果{@link #runFinalizersOnExit finalize -on-exit}已启用,则运行所有未调用的终结器。完成此操作后,虚拟机{@link #halt halts}。 * If this method is invoked after the virtual machine has begun its shutdown sequence then if shutdown hooks are being run this method will block indefinitely. If shutdown hooks have already been run and on-exit finalization has been enabled then this method halts the virtual machine with the given status code if the status is nonzero; otherwise, it blocks indefinitely. 如果这个方法是在虚拟机开始其关机顺序之后调用的,那么如果正在运行关机钩子,这个方法将无限期阻塞。如果已经运行了shutdown钩子,并且启用了on-exit finalization,那么如果状态为非零,则该方法使用给定的状态代码暂停虚拟机;否则,它将无限期地阻塞。 * The {@link System#exit(int) System.exit} method is the conventional and convenient means of invoking this method. {@link System#exit(int)系统。exit}方法是调用此方法的常规且方便的方法。 * * @param status * Termination status. By convention, a nonzero status code * indicates abnormal termination. * * @throws SecurityException * If a security manager is present and its * {@link SecurityManager#checkExit checkExit} method does not permit * exiting with the specified status * * @see java.lang.SecurityException * @see java.lang.SecurityManager#checkExit(int) * @see #addShutdownHook * @see #removeShutdownHook * @see #runFinalizersOnExit * @see #halt(int) */ public void exit(int status) { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkExit(status); } Shutdown.exit(status); }
addShutdownHook(Thread hook) 方法
/** * Registers a new virtual-machine shutdown hook. * The Java virtual machine shuts down in response to two kinds of events: Java虚拟机会在两种情况下关闭: * The program exits normally, when the last non-daemon thread exits or when the {@link #exit exit} (equivalently, {@link System#exit(int) System.exit}) method is invoked, or The virtual machine is terminated in response to a user interrupt, such as typing {@code ^C}, or a system-wide event, such as user logoff or system shutdown. 程序正常退出,当最后一个非守护线程退出或当{@link #退出退出}(等价于{@link系统#退出(int) System . exit})方法被调用时,终止或虚拟机来响应用户中断,如输入{@code C ^},或一个系统范围的事件,如用户下线或系统关闭。 * A hutdown hook is simply an initialized but unstarted thread. When the virtual machine begins its shutdown sequence it will start all registered shutdown hooks in some unspecified order and let them run concurrently. When all the hooks have finished it will then run all uninvoked finalizers if finalization-on-exit has been enabled. Finally, the virtual machine will halt. Note that daemon threads will continue to run during the shutdown sequence, as will non-daemon threads if shutdown was initiated by invoking the {@link #exit exit} method. hutdown hook 只是一个初始化但未启动的线程。当虚拟机开始其关机顺序时,它将以某种未指定的顺序启动所有已注册的关机挂钩,并让它们并发运行。当所有钩子都完成时,如果启用了退出时终结器,那么它将运行所有未调用的终结器。最后,虚拟机将停止。请注意,守护进程线程将在关机序列期间继续运行,如果通过调用{@link #exit exit}方法启动关机,那么非守护进程线程也将继续运行。 * Once the shutdown sequence has begun it can be stopped only by invoking the {@link #halt halt} method, which forcibly terminates the virtual machine. 一旦关闭序列开始,就只能通过调用{@link #halt halt}方法来停止它,该方法强制终止虚拟机。一旦关闭序列开始,就只能通过调用{@link #halt halt}方法来停止它,该方法强制终止虚拟机。 * Once the shutdown sequence has begun it is impossible to register a new shutdown hook or de-register a previously-registered hook. Attempting either of these operations will cause an {@link IllegalStateException} to be thrown. 一旦关闭序列开始,就不可能注册新的关闭钩子或注销以前注册的钩子。尝试这两种操作中的任何一种都会引发{@link IllegalStateException}。 * Shutdown hooks run at a delicate time in the life cycle of a virtual machine and should therefore be coded defensively. They should, in particular, be written to be thread-safe and to avoid deadlocks insofar as possible. They should also not rely blindly upon services that may have registered their own shutdown hooks and therefore may themselves in the process of shutting down. Attempts to use other thread-based services such as the AWT event-dispatch thread, for example, may lead to deadlocks. 关机钩子在虚拟机生命周期中的一个微妙时刻运行,因此应该进行防御性编码。尤其应该将它们编写为线程安全的,并尽可能避免死锁。它们也不应该盲目地依赖那些可能注册了自己的关机钩子的服务,因此它们自己可能正在关机过程中。例如,尝试使用其他基于线程的服务,如AWT事件分派线程,可能会导致死锁。 * Shutdown hooks should also finish their work quickly. When a program invokes {@link #exit exit} the expectation is that the virtual machine will promptly shut down and exit. When the virtual machine is terminated due to user logoff or system shutdown the underlying operating system may only allow a fixed amount of time in which to shut down and exit. It is therefore inadvisable to attempt any user interaction or to perform a long-running computation in a shutdown hook. 关闭钩子也应该快速完成它们的工作。当程序调用{@link #exit exit}时,预期虚拟机将立即关闭并退出。当虚拟机由于用户下线或系统关闭而终止时,底层操作系统可能只允许在固定的时间内关闭和退出。因此,不建议尝试任何用户交互或在关机钩子中执行长时间运行的计算。 * Uncaught exceptions are handled in shutdown hooks just as in any other thread, by invoking the {@link ThreadGroup#uncaughtException uncaughtException} method of the thread's {@link ThreadGroup} object. The default implementation of this method prints the exception's stack trace to {@link System#err} and terminates the thread; it does not cause the virtual machine to exit or halt. 通过调用线程的{@link ThreadGroup}对象的{@link ThreadGroup#uncaughtException函数,可以像在任何其他线程中一样,在shutdown钩子中处理未捕获的异常。该方法的默认实现将异常的堆栈跟踪打印到{@link System#err}并终止线程;它不会导致虚拟机退出或停止。 * In rare circumstances the virtual machine may abort, that is, stop running without shutting down cleanly. This occurs when the virtual machine is terminated externally, for example with the {@code SIGKILL} signal on Unix or the {@code TerminateProcess} call on Microsoft Windows. The virtual machine may also abort if a native method goes awry by, for example, corrupting internal data structures or attempting to access nonexistent memory. If the virtual machine aborts then no guarantee can be made about whether or not any shutdown hooks will be run. 在极少数情况下,虚拟机可能会中止,也就是说,在没有完全关闭的情况下停止运行。当虚拟机在外部终止时,例如在Unix上使用{@code SIGKILL}信号或在Microsoft Windows上使用{@code TerminateProcess}调用时,就会发生这种情况。如果本机方法出错,例如破坏内部数据结构或试图访问不存在的内存,虚拟机也可能中止。如果虚拟机中止,则无法保证是否会运行任何关机挂钩。 * * @param hook * An initialized but unstarted {@link Thread} object * 一个初始化但未启动的{@link Thread}对象 * * @throws IllegalArgumentException If the specified hook has already been registered,or if it can be determined that the hook is already running or has already been run; 如果指定的钩子已经注册,或者可以确定钩子已经运行中 或已经运行过了 * @throws IllegalStateException * If the virtual machine is already in the process * of shutting down * * @throws SecurityException * If a security manager is present and it denies * {@link RuntimePermission}("shutdownHooks") * * @see #removeShutdownHook * @see #halt(int) * @see #exit(int) * @since 1.3 */ public void addShutdownHook(Thread hook) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new RuntimePermission("shutdownHooks")); } ApplicationShutdownHooks.add(hook); }
halt(int status) 方法
/** Forcibly terminates the currently running Java virtual machine. This method never returns normally. 强制终止当前运行的Java虚拟机。这个方法永远不会正常返回。 * This method should be used with extreme caution. Unlike the {@link #exit exit} method, this method does not cause shutdown hooks to be started and does not run uninvoked finalizers if finalization-on-exit has been enabled. If the shutdown sequence has already been initiated then this method does not wait for any running shutdown hooks or finalizers to finish their work. 这种方法使用时应极为谨慎。与{@link #exit exit}方法不同,此方法不会启动关闭挂钩,并且如果启用了exit上的终结器,则不会运行未调用的终结器。如果已启动关机序列,则此方法不会等待任何正在运行的关机钩子或终结器完成其工作。 * * @param status Termination status. By convention, a nonzero status code indicates abnormal termination. If the {@link Runtime#exit exit} (equivalently, {@link System#exit(int) System.exit}) method has already been invoked then this status code will override the status code passed to that method. 终止状态。按照惯例,非零状态码表示异常终止。如果{@link Runtime#exit}(相当于{@link System#exit(int) System.exit})方法已经被调用,那么这个状态代码将覆盖传递给该方法的状态代码。 * * @throws SecurityException If a security manager is present and its {@link SecurityManager#checkExit checkExit} method does not permit an exit with the specified status 如果存在安全管理器,并且它的{@link SecurityManager#checkExit}方法不允许具有指定状态的退出 * * @see #exit * @see #addShutdownHook * @see #removeShutdownHook * @since 1.3 */ public void halt(int status) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkExit(status); } Shutdown.halt(status); }
runFinalization() 方法
/** Runs the finalization methods of any objects pending finalization.Calling this method suggests that the Java virtual machine expend effort toward running the {@code finalize} methods of objects that have been found to be discarded but whose {@code finalize} methods have not yet been run. When control returns from the method call, the virtual machine has made a best effort to complete all outstanding finalizations. 运行任何对象的终止方法。调用此方法意味着Java虚拟机将精力花在运行{@code finalize}方法上,这些对象已被发现被丢弃,但其{@code finalize}方法尚未运行。当控件从方法调用返回时,虚拟机已尽最大努力完成所有未完成的收尾工作。 * <p> The virtual machine performs the finalization process automatically as needed, in a separate thread, if the {@code runFinalization} method is not invoked explicitly. 如果没有显式调用{@code runFinalization}方法,虚拟机将根据需要在单独的线程中自动执行终结过程。 * <p> The method {@link System#runFinalization()} i s the conventionaland convenient means of invoking this method. 方法{@link System#runFinalization()}是调用此方法的常规且方便的方法。 * * @see java.lang.Object#finalize() */ public void runFinalization() { runFinalization0(); }version()方法 java9才有
/** * Returns the version of the Java Runtime Environment as a {@link Version}. * * @return the {@link Version} of the Java Runtime Environment * * @since 9 */ public static Version version() { if (version == null) { version = new Version(VersionProps.versionNumbers(), VersionProps.pre(), VersionProps.build(), VersionProps.optional()); } return version; }四、Process的主要方法
destroy(): 杀掉子进程。
exitValue(): 返回子进程的出口值。
InputStream getErrorStream(): 获得子进程的错误流。
InputStream getInputStream(): 获得子进程的输入流。
OutputStream getOutputStream(): 获得子进程的输出流。
waitFor(): 导致当前线程等待,如果必要,一直要等到由该 Process 对象表示的进程已经终止。
–一般执行linux命令使用比较多的是waitFor(),诸塞等待子进程完成该linux命令。然后再继续线程后续的代码。
–以上基本简单把Runtime的应用机制介绍了下,虽然不算深入,但是平常简单的调用linux命令这种简单的工作就能做到基本心里有底了,下面继续说说在应用过程中遇到的小坑。
五 坑
坑一:如何在exec执行多条linux命令
不熟悉时可能会把一段linux命令当作输入直接执行,然后发现并没有执行,这是为什么呢?比如”mv dir1/. dir2;wait;gzip dir2/.;” 这段命令直接在linux上直接执行是没问题的,但是放到exec执行会发现根本没能执行,原因是exec最终会对command进行一次字符串分割,默认以” \t\n\r\f”(前有一个空格,引号不是)为分割符,上面语句不符合格式。
public Process exec(String command,String[] envp, File dir) throws IOException { 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); }
正确用法是用 && 连接起来”mv dir1/. dir2&&wait&&gzip dir2/.”,或者把三个命令加入String数组,调用publicProcess exec(String cmdarray[]) throws IOException这个重载方法。
–补充修正,如何在exec执行多条linux命令
调用public Process exec(String cmdarray[]) throws IOException这个重载方法是无法执行多条命令的,数组的作用只是让你手工把命令和传入参数切割开来,以便后续子进程的调用,另外”mv dir1/. dir2&&wait&&gzip dir2/.”这种用&&连接的多命令也是不可用的,想要 调用多条命令只能一个个命令执行,或者 把多条命令写入脚本,exec直接执行该脚本。
坑二:为什么如果在java代码中不调用waitFor()阻塞等待linux中命令执行结束会出现部分命令并没有执行的情况。
比如我遇到的问题,在java中执行了下面两行代码
Runtime.getRuntime().exec(“mv dir1/file.txt dir2”); Runtime.getRuntime().exec(“gzip dir2/ file.txt”);
多线程执行一段时间后会发现有部分文件没有按希望去压缩,这是什么原因,百思不得其解,目前还没搞得太清楚,初步认为是主进程创建了两个子进程,如果不诸塞的话,可能当进程1的文件还没挪到dir2,进程2已经执行了,所以这部分文件没有按希望进行压缩。但是为什么进程二没有报文件不存在的错误呢?这个也是跟疑问。不管怎么说解决之道就是加阻塞。
Runtime.getRuntime().exec(“mv dir1/file.txtdir2”) .waitFor(); Runtime.getRuntime().exec(“gzip dir2/ file.txt”).waitFor();123
加上.waitFor()后,发现之前的问题解决了,文件全部都进行了压缩。
写在最后
看看源码蛮好的,就是好花时间; 一直听人说 钩子, 现在知道了,原来在这里。
标签: #java注销