Java系统操作与进程创建全解析
1. System类概述
System类提供了静态方法来操作系统状态,并作为系统级资源的存储库。它主要在四个方面提供功能:
- 标准I/O流
- 系统属性操作
- 访问当前Runtime的实用工具和便捷方法
- 安全相关(暂不详细介绍)
1.1 标准I/O流
System类的静态字段提供了标准输入、输出和错误流:
public static final InputStream in; // 标准输入流,用于读取数据
public static final PrintStream out; // 标准输出流,用于打印消息
public static final PrintStream err; // 标准错误流,用于打印错误消息
由于历史原因,
out
和
err
是
PrintStream
对象,而非
PrintWriter
对象。虽然这些标准流引用被声明为
final
,但可以使用
setIn
、
setOut
和
setErr
方法重新定义它们绑定的实际流,不过这些方法会进行安全检查,若没有权限更改标准流,会抛出
SecurityException
。
1.2 系统属性
系统属性定义了系统环境,存储在
Properties
对象中。属性名由多个部分组成,用句点分隔。以下是一些标准系统属性的示例:
java.version=1.5.0_02
java.vendor=Sun Microsystems Inc.
java.vendor.url=http://java.sun.com/
...
System类中处理系统属性的方法如下:
| 方法 | 描述 |
| — | — |
|
public static Properties getProperties()
| 获取定义所有系统属性的
Properties
对象 |
|
public static String getProperty(String key)
| 返回指定键的系统属性值 |
|
public static String getProperty(String key, String defaultValue)
| 返回指定键的系统属性值,若未定义则返回默认值 |
|
public static String setProperty(String key, String value)
| 设置指定键的系统属性值,并返回先前的值 |
|
public static String clearProperty(String key)
| 移除指定键的系统属性,并返回先前的值 |
|
public static void setProperties(Properties props)
| 设置定义所有系统属性的
Properties
对象 |
这些方法都进行安全检查,可能抛出
SecurityException
。属性值以字符串形式存储,但可以表示其他类型,如整数或布尔值。有一些方法可以将属性值解码为基本类型:
public static boolean Boolean.getBoolean(String name);
public static Integer Integer.getInteger(String name);
public static Integer Integer.getInteger(String name, Integer def);
public static Integer Integer.getInteger(String name, int def);
public static Long Long.getLong(String name);
public static Long Long.getLong(String name, Long def);
public static Long Long.getLong(String name, long def);
getBoolean
方法与其他方法不同,它返回布尔值,若属性不存在则返回
false
,其他方法返回
null
。
Character
、
Byte
、
Short
、
Float
和
Double
类没有属性获取方法,可以先获取字符串值,再进行类型转换。
1.3 实用工具方法
System类还包含一些实用工具方法:
public static long currentTimeMillis(); // 返回自1970年1月1日00:00:00 GMT以来的当前时间(以毫秒为单位)
public static long nanoTime(); // 返回可用的最精确系统计时器的当前值(以纳秒为单位)
public static void arraycopy(Object src, int srcPos, Object dst, int dstPos, int count); // 复制源数组的内容到目标数组
public static int identityHashCode(Object obj); // 使用Object.hashCode定义的算法返回对象的哈希码
此外,System类还有一些便捷方法操作当前的Runtime对象,如
exit
、
gc
、
runFinalization
、
loadLibrary
和
load
,这些方法的调用等同于
Runtime.getRuntime().method
。
2. 创建进程
运行的系统可以有多个执行线程,大多数托管Java虚拟机的系统也可以运行多个程序。可以使用
Runtime.exec
方法执行新程序,每次成功调用
exec
会创建一个新的
Process
对象,代表在其自己的进程中运行的程序。
2.1 Process类
exec
方法有两种基本形式:
public Process exec(String[] cmdArray) throws IOException; // 在当前系统上运行cmdArray中的命令
public Process exec(String command) throws IOException; // 等同于将命令字符串按空格分割为数组后调用数组形式的exec
Process
对象以两种方式表示子进程:
- 提供获取子进程输入、输出和错误流的方法:
public abstract OutputStream getOutputStream(); // 连接到子进程的标准输入
public abstract InputStream getInputStream(); // 连接到子进程的标准输出
public abstract InputStream getErrorStream(); // 连接到子进程的错误输出
- 提供控制进程和查询其终止状态的方法:
public abstract int waitFor() throws InterruptedException; // 等待进程完成并返回退出值
public abstract int exitValue(); // 返回进程的退出值,若进程未完成会抛出IllegalThreadStateException
public abstract void destroy(); // 终止进程
以下是一个获取
ls
命令输出的示例:
import java.io.*;
import java.util.*;
public class LSExample {
public String[] ls(String dir, String opts) throws LSFailedException {
try {
String[] cmdArray = { "/bin/ls", opts, dir };
Process child = Runtime.getRuntime().exec(cmdArray);
InputStream lsOut = child.getInputStream();
InputStreamReader r = new InputStreamReader(lsOut);
BufferedReader in = new BufferedReader(r);
List<String> lines = new ArrayList<String>();
String line;
while ((line = in.readLine()) != null) {
lines.add(line);
}
if (child.waitFor() != 0) {
throw new LSFailedException(child.exitValue());
}
return lines.toArray(new String[lines.size()]);
} catch (LSFailedException e) {
throw e;
} catch (Exception e) {
throw new LSFailedException(e.toString());
}
}
}
2.2 进程环境
Runtime.exec
还有另外两种形式,可以指定一组环境变量:
public Process exec(String[] cmdArray, String[] env) throws IOException;
public Process exec(String command, String[] env) throws IOException;
环境变量以
name=value
的形式存储在字符串数组中,传递给
exec
方法的第二个参数。若传递
null
,子进程将继承其父进程的环境变量。还可以指定子进程的初始工作目录:
public Process exec(String[] cmdArray, String[] env, File dir) throws IOException;
public Process exec(String command, String[] env, File dir) throws IOException;
2.3 ProcessBuilder类
Runtime
的
exec
方法是使用更通用的
ProcessBuilder
类的便捷方法。
ProcessBuilder
封装了外部进程的三个关键属性:命令、环境和当前工作目录。它有两个构造函数:
public ProcessBuilder(String... command);
public ProcessBuilder(List<String> command);
ProcessBuilder
还可以控制进程的标准错误流是否重定向到标准输出流。它提供了查询和设置这些属性的方法,例如:
public File directory(); // 返回工作目录
public Map<String, String> environment(); // 返回包含所有环境变量设置的映射
public boolean redirectErrorStream(); // 返回标准错误流是否应重定向到标准输出流
以下是使用
ProcessBuilder
重写
ls
示例并重定向错误流的代码:
public String[] ls(String dir, String opts) throws LSFailedException {
try {
Process child = new ProcessBuilder("/bin/ls", opts, dir).redirectErrorStream(true).start();
// ... 后续代码与之前示例类似 ...
} catch (Exception e) {
throw new LSFailedException(e.toString());
}
}
2.4 可移植性
使用
exec
或
ProcessBuilder
的程序并非在所有系统上都可移植,因为并非所有环境都支持进程,且不同系统的命令和语法可能差异很大。此外,运行任意命令会带来安全问题,系统可能不允许所有程序启动进程,因此在使用时需谨慎。
2.5 练习
-
编写
plugTogether方法(需要使用线程) -
编写一个程序,在命令行参数上运行
exec并打印命令输出,每行输出前加上行号 -
编写一个程序,在命令行参数上运行
exec并打印命令输出,当输出中出现特定字符串时终止命令
下面是创建进程的流程mermaid图:
graph TD;
A[开始] --> B[调用Runtime.exec方法];
B --> C{执行成功?};
C -- 是 --> D[创建Process对象];
C -- 否 --> E[抛出IOException];
D --> F[获取进程状态和控制进程];
F --> G[获取输入、输出和错误流];
F --> H[等待进程完成或终止进程];
G --> I[处理流数据];
H --> J[结束];
I --> J;
以上就是关于Java系统操作和进程创建的详细介绍,涵盖了System类的各种功能以及如何使用
Runtime.exec
和
ProcessBuilder
创建和控制进程。通过这些方法,可以更好地与操作系统进行交互,实现更复杂的功能。
3. 系统操作与进程创建的综合应用
3.1 系统属性的实际应用
系统属性在实际开发中具有广泛的应用场景。例如,在配置文件的加载方面,我们可以利用系统属性来确定配置文件的位置。以下是一个示例代码:
import java.io.File;
public class ConfigLoader {
public static File loadConfig(String configFileName) {
String home = System.getProperty("user.home");
if (home == null) {
return null;
}
return new File(home, configFileName);
}
}
在这个示例中,我们通过
System.getProperty("user.home")
获取用户的主目录,然后结合配置文件的名称来构建配置文件的路径。这样可以使程序更加灵活,不同用户可以根据自己的需求将配置文件放置在自己的主目录下。
3.2 进程创建的常见场景
进程创建在很多场景下都非常有用,比如执行外部脚本、调用系统命令等。下面是一个使用
Runtime.exec
执行Python脚本的示例:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class PythonScriptExecutor {
public static void executePythonScript(String scriptPath) {
try {
String[] cmdArray = {"python", scriptPath};
Process child = Runtime.getRuntime().exec(cmdArray);
BufferedReader reader = new BufferedReader(new InputStreamReader(child.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
int exitCode = child.waitFor();
if (exitCode != 0) {
System.err.println("Python script execution failed with exit code: " + exitCode);
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
在这个示例中,我们使用
Runtime.exec
方法执行Python脚本,并读取脚本的输出。通过
waitFor
方法等待脚本执行完成,并获取其退出码,根据退出码判断脚本是否执行成功。
3.3 性能优化与注意事项
在使用
System
类和进程创建相关功能时,有一些性能优化和注意事项需要我们关注:
-
系统属性的使用
:尽量避免频繁地获取系统属性,因为每次获取都可能涉及到系统调用,会带来一定的性能开销。可以在程序启动时将需要的系统属性缓存起来,后续直接使用缓存的值。
-
进程创建的开销
:创建新进程是一个相对昂贵的操作,因为需要分配系统资源。在需要频繁执行某个命令时,尽量避免每次都创建新进程,可以考虑使用线程池或复用已有的进程。
-
资源管理
:在使用
Process
对象时,要确保及时关闭输入、输出和错误流,避免资源泄漏。同时,在进程执行完成后,要及时调用
destroy
方法销毁进程,释放系统资源。
以下是一个资源管理的示例代码:
import java.io.IOException;
public class ProcessResourceManagement {
public static void executeCommand(String command) {
Process process = null;
try {
process = Runtime.getRuntime().exec(command);
// 处理输入、输出和错误流
// ...
} catch (IOException e) {
e.printStackTrace();
} finally {
if (process != null) {
process.destroy();
}
}
}
}
3.4 总结
通过对
System
类和进程创建相关内容的学习,我们了解到Java提供了强大的系统操作和进程控制功能。
System
类的标准I/O流、系统属性操作和实用工具方法,以及
Runtime.exec
和
ProcessBuilder
类的进程创建功能,为我们与操作系统进行交互提供了丰富的手段。
在实际开发中,我们可以根据具体的需求选择合适的方法。例如,在需要简单地执行系统命令时,可以使用
Runtime.exec
;而在需要更灵活地配置进程的环境和工作目录时,可以使用
ProcessBuilder
。同时,要注意性能优化和资源管理,确保程序的稳定性和高效性。
3.5 对比表格
| 功能 |
Runtime.exec
|
ProcessBuilder
|
|---|---|---|
| 灵活性 | 相对较低,配置环境和工作目录较麻烦 | 高,可以方便地配置命令、环境和工作目录 |
| 易用性 | 简单,适合简单的命令执行 | 稍复杂,但提供了更多的控制选项 |
| 错误流处理 | 需手动处理 | 可以方便地重定向错误流 |
3.6 系统操作与进程创建的进阶流程mermaid图
graph TD;
A[开始] --> B[系统属性配置];
B --> C{是否需要创建进程?};
C -- 是 --> D[选择创建方式];
C -- 否 --> E[执行系统操作];
D --> F{使用Runtime.exec?};
F -- 是 --> G[执行Runtime.exec];
F -- 否 --> H[使用ProcessBuilder];
G --> I[创建Process对象];
H --> I;
I --> J[获取进程资源];
J --> K{进程执行成功?};
K -- 是 --> L[处理进程输出];
K -- 否 --> M[处理异常];
L --> N[释放进程资源];
M --> N;
N --> O[结束];
E --> O;
综上所述,掌握Java的系统操作和进程创建功能,能够让我们更好地与操作系统进行交互,开发出更加高效、灵活的Java应用程序。在实际应用中,要根据具体的需求和场景,合理选择和使用这些功能,并注意性能优化和资源管理,以确保程序的质量和稳定性。
超级会员免费看

被折叠的 条评论
为什么被折叠?



