Java 进程间通信机制汇总
1.Socket
使用套接字除了可以实现网络间不同主机间的通信外,还可以实现同一主机的不同进程间的通信,且建立的通信是双向的通信。
例如:
客户端:
- import java.io.DataInputStream;
- import java.io.DataOutputStream;
- import java.io.IOException;
- import java.net.Socket;
- public class Socket_Client {
- public static void main(String[] args) throws InterruptedException {
- // TODO Auto-generated method stub
- Socket socket = null;
- String str = null;
- DataOutputStream out = null;
- DataInputStream in = null;
- try{
- socket = new Socket("127.0.0.1", 4331);
- in = new DataInputStream(socket.getInputStream());
- out = new DataOutputStream(socket.getOutputStream());
- for(int i = 0; i < 10; i=i+1)
- {
- out.writeUTF("" + i);
- str = in.readUTF();
- System.out.println("收到:" + str);
- Thread.sleep(500);
- }
- }
- catch(IOException e){
- System.out.println(e.toString());
- }
- }
- }
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class Socket_Client {
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
Socket socket = null;
String str = null;
DataOutputStream out = null;
DataInputStream in = null;
try{
socket = new Socket("127.0.0.1", 4331);
in = new DataInputStream(socket.getInputStream());
out = new DataOutputStream(socket.getOutputStream());
for(int i = 0; i < 10; i=i+1)
{
out.writeUTF("" + i);
str = in.readUTF();
System.out.println("收到:" + str);
Thread.sleep(500);
}
}
catch(IOException e){
System.out.println(e.toString());
}
}
}
服务器端:
- import java.io.DataInputStream;
- import java.io.DataOutputStream;
- import java.net.ServerSocket;
- import java.net.Socket;
- public class Socket_Server {
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- ServerSocket server = null;
- Socket socket = null;
- String str = null;
- DataOutputStream out = null;
- DataInputStream in = null;
- try{
- server = new ServerSocket(4331);
- System.out.println("等待用户呼叫");
- socket = server.accept();
- out = new DataOutputStream(socket.getOutputStream());
- in = new DataInputStream(socket.getInputStream());
- while(true){
- str = in.readUTF();
- out.writeUTF("服务器呼叫");
- System.out.println("收到:"+str);
- Thread.sleep(500);
- }
- }
- catch(Exception e)
- {
- System.out.println(e.toString());
- }
- }
- }
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Socket_Server {
public static void main(String[] args) {
// TODO Auto-generated method stub
ServerSocket server = null;
Socket socket = null;
String str = null;
DataOutputStream out = null;
DataInputStream in = null;
try{
server = new ServerSocket(4331);
System.out.println("等待用户呼叫");
socket = server.accept();
out = new DataOutputStream(socket.getOutputStream());
in = new DataInputStream(socket.getInputStream());
while(true){
str = in.readUTF();
out.writeUTF("服务器呼叫");
System.out.println("收到:"+str);
Thread.sleep(500);
}
}
catch(Exception e)
{
System.out.println(e.toString());
}
}
}
2.RMI
RMI(Remote Method Invocation)远程方法调用是java的核心技术之一。是Enterprise JavaBeans的基础技术,是java建立分布式应用程序的强大支柱。RMI允许一个应用程序访问另外一个服务器或虚拟机上的对象,方法和服务,它使远程方法调用就像在本地调用一样简单。它为用户屏蔽了底层的网络传输细节,使用的时候只需适当处理异常即可。所以RMI是非常容易使用的,但同时是非常强大的。
例如:
接口:
- import java.rmi.Remote;
- import java.rmi.RemoteException;
- public interface HelloImp extends Remote{
- public String helloWorld() throws RemoteException;
- public String sayHelloToSomeBody(String someBodyName) throws RemoteException;
- }
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface HelloImp extends Remote{
public String helloWorld() throws RemoteException;
public String sayHelloToSomeBody(String someBodyName) throws RemoteException;
}
类:
- import java.rmi.RemoteException;
- import java.rmi.server.UnicastRemoteObject;
- public class Hello extends UnicastRemoteObject implements HelloImp{
- public Hello() throws RemoteException {
- }
- public String helloWorld() throws RemoteException {
- return "Hello World!";
- }
- public String sayHelloToSomeBody(String someBodyName) throws RemoteException {
- return "你好," + someBodyName + "!";
- }
- }
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class Hello extends UnicastRemoteObject implements HelloImp{
public Hello() throws RemoteException {
}
public String helloWorld() throws RemoteException {
return "Hello World!";
}
public String sayHelloToSomeBody(String someBodyName) throws RemoteException {
return "你好," + someBodyName + "!";
}
}
客户端:
- import java.net.MalformedURLException;
- import java.rmi.Naming;
- import java.rmi.NotBoundException;
- import java.rmi.RemoteException;
- public class RMI_Client {
- public static void main(String[] args) {
- try {
- //在RMI服务注册表中查找名称为RHello的对象,并调用其上的方法
- HelloImp rhello =(HelloImp) Naming.lookup("rmi://localhost:8888/RHello");
- System.out.println(rhello.helloWorld());
- System.out.println(rhello.sayHelloToSomeBody("Hello!SomeBody"));
- } catch (NotBoundException e) {
- e.printStackTrace();
- } catch (MalformedURLException e) {
- e.printStackTrace();
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- }
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
public class RMI_Client {
public static void main(String[] args) {
try {
//在RMI服务注册表中查找名称为RHello的对象,并调用其上的方法
HelloImp rhello =(HelloImp) Naming.lookup("rmi://localhost:8888/RHello");
System.out.println(rhello.helloWorld());
System.out.println(rhello.sayHelloToSomeBody("Hello!SomeBody"));
} catch (NotBoundException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
服务器端:
- import java.net.MalformedURLException;
- import java.rmi.AlreadyBoundException;
- import java.rmi.Naming;
- import java.rmi.RemoteException;
- import java.rmi.registry.LocateRegistry;
- public class RMI_Server {
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- try {
- //创建一个远程对象
- HelloImp rhello = new Hello();
- //本地主机上的远程对象注册表Registry的实例,并指定端口为8888,这一步必不可少(Java默认端口是1099),必不可缺的一步,缺少注册表创建,则无法绑定对象到远程注册表上
- LocateRegistry.createRegistry(8888);
- //把远程对象注册到RMI注册服务器上,并命名为RHello
- //绑定的URL标准格式为:rmi://host:port/name(其中协议名可以省略,下面两种写法都是正确的)
- Naming.bind("rmi://localhost:8888/RHello",rhello);
- System.out.println(">>>>>INFO:远程Hello对象绑定成功!");
- } catch (RemoteException e) {
- System.out.println("创建远程对象发生异常!");
- e.printStackTrace();
- } catch (AlreadyBoundException e) {
- System.out.println("发生重复绑定对象异常!");
- e.printStackTrace();
- } catch (MalformedURLException e) {
- System.out.println("发生URL畸形异常!");
- e.printStackTrace();
- }
- }
import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
public class RMI_Server {
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
//创建一个远程对象
HelloImp rhello = new Hello();
//本地主机上的远程对象注册表Registry的实例,并指定端口为8888,这一步必不可少(Java默认端口是1099),必不可缺的一步,缺少注册表创建,则无法绑定对象到远程注册表上
LocateRegistry.createRegistry(8888);
//把远程对象注册到RMI注册服务器上,并命名为RHello
//绑定的URL标准格式为:rmi://host:port/name(其中协议名可以省略,下面两种写法都是正确的)
Naming.bind("rmi://localhost:8888/RHello",rhello);
System.out.println(">>>>>INFO:远程Hello对象绑定成功!");
} catch (RemoteException e) {
System.out.println("创建远程对象发生异常!");
e.printStackTrace();
} catch (AlreadyBoundException e) {
System.out.println("发生重复绑定对象异常!");
e.printStackTrace();
} catch (MalformedURLException e) {
System.out.println("发生URL畸形异常!");
e.printStackTrace();
}
}
3.内存映射
Java 利用NIO(New I/O)来实现共享内存,就是将不同进程的内存映射文件关联到同一个物理文件,NIO的内存映射文件(MappedByteBuffer)总是与某个物理文件相关,不管是从FileInputStream、FileOutputStream之类还是RandomAccessFile得来的FileChannel,都由map()得到的内存映射文件MappedByteBuffer。严格来说,其实就是两个Java进程通过中间文件来交换数据,用中间文件使得两个进程的两块内存区域的内容得到及时的同步。
代码 WriteShareMemory.java 往映射文件中依次写入 A、B、C ... Z,ReadShareMemory.java 逐个读出来,打印到屏幕上。代码对交换文件 swap.txt 的第一个字节作了读写标志,分别是 0-可读,1-正在写,2-可读。RandomAccessFile 得到的 Channel 能够灵活的进行读或写,并且不会破坏原有文件内容,而 FileInputStream 或 FileOutputStream 取得的 Channel 则很难达到这一功效,所以使用了 RandomAccessFile 来获得 FileChannel。
WriteShareMemory.java:
- import java.io.RandomAccessFile;
- import java.nio.MappedByteBuffer;
- import java.nio.channels.FileChannel;
- import java.nio.channels.FileChannel.MapMode;
- /**
- * 往 "共享内存" 写入数据
- * @author Unmi
- */
- public class WriteShareMemory {
- /**
- * @param args
- * @throws Exception
- */
- public static void main(String[] args) throws Exception {
- RandomAccessFile raf = new RandomAccessFile("f:/swap.txt", "rw");
- FileChannel fc = raf.getChannel();
- MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 0, 1024);
- //清除文件内容
- for(int i=0;i<1024;i++){
- mbb.put(i,(byte)0);
- }
- //从文件的第二个字节开始,依次写入 A-Z 字母,第一个字节指明了当前操作的位置
- for(int i=65;i<91;i++){
- int index = i-63;
- int flag = mbb.get(0); //可读标置第一个字节为 0
- if(flag != 0){ //不是可写标示 0,则重复循环,等待
- i --;
- continue;
- }
- mbb.put(0,(byte)1); //正在写数据,标志第一个字节为 1
- mbb.put(1,(byte)(index)); //写数据的位置
- System.out.println("程序 WriteShareMemory:"+System.currentTimeMillis() +
- ":位置:" + index +" 写入数据:" + (char)i);
- mbb.put(index,(byte)i);//index 位置写入数据
- mbb.put(0,(byte)2); //置可读数据标志第一个字节为 2
- Thread.sleep(513);
- }
- raf.close();
- }
- }
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
/**
* 往 "共享内存" 写入数据
* @author Unmi
*/
public class WriteShareMemory {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
RandomAccessFile raf = new RandomAccessFile("f:/swap.txt", "rw");
FileChannel fc = raf.getChannel();
MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 0, 1024);
//清除文件内容
for(int i=0;i<1024;i++){
mbb.put(i,(byte)0);
}
//从文件的第二个字节开始,依次写入 A-Z 字母,第一个字节指明了当前操作的位置
for(int i=65;i<91;i++){
int index = i-63;
int flag = mbb.get(0); //可读标置第一个字节为 0
if(flag != 0){ //不是可写标示 0,则重复循环,等待
i --;
continue;
}
mbb.put(0,(byte)1); //正在写数据,标志第一个字节为 1
mbb.put(1,(byte)(index)); //写数据的位置
System.out.println("程序 WriteShareMemory:"+System.currentTimeMillis() +
":位置:" + index +" 写入数据:" + (char)i);
mbb.put(index,(byte)i);//index 位置写入数据
mbb.put(0,(byte)2); //置可读数据标志第一个字节为 2
Thread.sleep(513);
}
raf.close();
}
}
ReadSharedMemory.java:
- import java.io.RandomAccessFile;
- import java.nio.MappedByteBuffer;
- import java.nio.channels.FileChannel;
- import java.nio.channels.FileChannel.MapMode;
- /**
- * 从 "共享内存" 读出数据
- * @author Unmi
- */
- public class ReadShareMemory {
- /**
- * @param args
- * @throws Exception
- */
- public static void main(String[] args) throws Exception {
- RandomAccessFile raf = new RandomAccessFile("f:/swap.txt", "rw");
- FileChannel fc = raf.getChannel();
- MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 0, 1024);
- int lastIndex = 0;
- for(int i=1;i<27;i++){
- int flag = mbb.get(0); //取读写数据的标志
- int index = mbb.get(1); //读取数据的位置,2 为可读
- if(flag != 2 || index == lastIndex){ //假如不可读,或未写入新数据时重复循环
- i--;
- continue;
- }
- lastIndex = index;
- System.out.println("程序 ReadShareMemory:" + System.currentTimeMillis() +
- ":位置:" + index +" 读出数据:" + (char)mbb.get(index));
- mbb.put(0,(byte)0); //置第一个字节为可读标志为 0
- if(index == 27){ //读完数据后退出
- break;
- }
- }
- raf.close();
- }
- }
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
/**
* 从 "共享内存" 读出数据
* @author Unmi
*/
public class ReadShareMemory {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
RandomAccessFile raf = new RandomAccessFile("f:/swap.txt", "rw");
FileChannel fc = raf.getChannel();
MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 0, 1024);
int lastIndex = 0;
for(int i=1;i<27;i++){
int flag = mbb.get(0); //取读写数据的标志
int index = mbb.get(1); //读取数据的位置,2 为可读
if(flag != 2 || index == lastIndex){ //假如不可读,或未写入新数据时重复循环
i--;
continue;
}
lastIndex = index;
System.out.println("程序 ReadShareMemory:" + System.currentTimeMillis() +
":位置:" + index +" 读出数据:" + (char)mbb.get(index));
mbb.put(0,(byte)0); //置第一个字节为可读标志为 0
if(index == 27){ //读完数据后退出
break;
}
}
raf.close();
}
}
Java的多进程运行模式分析
一般我们在java中运行其它类中的方法时,无论是静态调用,还是动态调用,都是在当前的进程中执行的,也就是说,只有一个java虚拟机实例在运行。而有的时候,我们需要通过java代码启动多个java子进程。这样做虽然占用了一些系统资源,但会使程序更加稳定,因为新启动的程序是在不同的虚拟机进程中运行的,如果有一个进程发生异常,并不影响其它的子进程。
在Java中我们可以使用两种方法来实现这种要求。最简单的方法就是通过Runtime中的exec方法执行java classname.如果执行成功,这个方法返回一个Process对象,如果执行失败,将抛出一个IOException错误。下面让我们来看一个简单的例子。
// Test1.java文件
import java.io.*;
public class Test
{
public static void main(String[] args)
{
FileOutputStream fOut = new FileOutputStream("c://Test1.txt");
fOut.close();
System.out.println("被调用成功!");
}
}
// Test_Exec.java
public class Test_Exec
{
public static void main(String[] args)
{
Runtime run = Runtime.getRuntime();
Process p = run.exec("java test1");
}
}
通过java Test_Exec运行程序后,发现在C盘多了个Test1.txt文件,但在控制台中并未出现"被调用成功!"的输出信息。因此可以断定,Test已经被执行成功,但因为某种原因,Test的输出信息未在Test_Exec的控制台中输出。这个原因也很简单,因为使用exec建立的是Test_Exec的子进程,这个子进程并没有自己的控制台,因此,它并不会输出任何信息。
如果要输出子进程的输出信息,可以通过Process中的getInputStream得到子进程的输出流(在子进程中输出,在父进程中就是输入),然后将子进程中的输出流从父进程的控制台输出。具体的实现代码如下如示:
// Test_Exec_Out.java
import java.io.*;
public class Test_Exec_Out
{
public static void main(String[] args)
{
Runtime run = Runtime.getRuntime();
Process p = run.exec("java test1");
BufferedInputStream in = new BufferedInputStream(p.getInputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String s;
while ((s = br.readLine()) != null)
System.out.println(s);
}
}
从上面的代码可以看出,在Test_Exec_Out.java中通过按行读取子进程的输出信息,然后在Test_Exec_Out中按每行进行输出。 上面讨论的是如何得到子进程的输出信息。那么,除了输出信息,还有输入信息。既然子进程没有自己的控制台,那么输入信息也得由父进程提供。我们可以通过Process的getOutputStream方法来为子进程提供输入信息(即由父进程向子进程输入信息,而不是由控制台输入信息)。我们可以看看如下的代码:
// Test2.java文件
import java.io.*;
public class Test
{
public static void main(String[] args)
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("由父进程输入的信息:" + br.readLine());
}
}
// Test_Exec_In.java
import java.io.*;
public class Test_Exec_In
{
public static void main(String[] args)
{
Runtime run = Runtime.getRuntime();
Process p = run.exec("java test2");
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
bw.write("向子进程输出信息");
bw.flush();
bw.close(); // 必须得关闭流,否则无法向子进程中输入信息
// System.in.read();
}
}
从以上代码可以看出,Test1得到由Test_Exec_In发过来的信息,并将其输出。当你不加bw.flash()和bw.close()时,信息将无法到达子进程,也就是说子进程进入阻塞状态,但由于父进程已经退出了,因此,子进程也跟着退出了。如果要证明这一点,可以在最后加上System.in.read(),然后通过任务管理器(在windows下)查看java进程,你会发现如果加上bw.flush()和bw.close(),只有一个java进程存在,如果去掉它们,就有两个java进程存在。这是因为,如果将信息传给Test2,在得到信息后,Test2就退出了。在这里有一点需要说明一下,exec的执行是异步的,并不会因为执行的某个程序阻塞而停止执行下面的代码。因此,可以在运行test2后,仍可以执行下面的代码。
exec方法经过了多次的重载。上面使用的只是它的一种重载。它还可以将命令和参数分开,如exec("java.test2")可以写成exec("java", "test2")。exec还可以通过指定的环境变量运行不同配置的java虚拟机。
除了使用Runtime的exec方法建立子进程外,还可以通过ProcessBuilder建立子进程。ProcessBuilder的使用方法如下:
// Test_Exec_Out.java
import java.io.*;
public class Test_Exec_Out
{
public static void main(String[] args)
{
ProcessBuilder pb = new ProcessBuilder("java", "test1");
Process p = pb.start();
… …
}
}
在建立子进程上,ProcessBuilder和Runtime类似,不同的ProcessBuilder使用start()方法启动子进程,而Runtime使用exec方法启动子进程。得到Process后,它们的操作就完全一样的。
ProcessBuilder和Runtime一样,也可设置可执行文件的环境信息、工作目录等。下面的例子描述了如何使用ProcessBuilder设置这些信息。
ProcessBuilder pb = new ProcessBuilder("Command", "arg2", "arg2", ''');
// 设置环境变量
Map<String, String> env = pb.environment();
env.put("key1", "value1");
env.remove("key2");
env.put("key2", env.get("key1") + "_test");
pb.directory("……/abcd"); // 设置工作目录
Process p = pb.start(); // 建立子进程
Java中获取进程ID以及杀死进程的方法
taskkill介绍
taskkill是用来终止进程的。具体的命令规则如下: TASKKILL [/S system [/U username [/P [password]]]] { [/FI filter] [/PID processid | /IM imagename] } [/F] [/T] 描述: 这个命令行工具可用来结束至少一个进程。 可以根据进程 id 或图像名来结束进程。 参数列表:
/S system 指定要连接到的远程系统。
/U [domain\]user 指定应该在哪个用户上下文 执行这个命令。
/P [password] 为提供的用户上下文指定密码。如果忽略,提示输入。
/F 指定要强行终止的进程。 /FI filter 指定筛选进或筛选出查询的的任务。
/PID process id 指定要终止的进程的PID。
/IM image name 指定要终止的进程的图像名。通配符 '*'可用来指定所有图像名。
/T Tree kill: 终止指定的进程和任何由此启动的子进程。
/? 显示帮助/用法。 筛选器: 筛选器名 有效运算符 有效值
Java实现
private String getPID() {
String pid = null;
String cmd = "tasklist /nh /FI \"IMAGENAME eq eclipse.exe\"";
try {
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(cmd);
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = null;
while((line=br.readLine()) != null){
if(line.indexOf("eclipse.exe") != -1){
String[] lineArray = line.split(" ");
pid = lineArray[17].trim();
if(pid.equals(RFT_ECLIPSE_PID)){
continue;
} else {
return pid;
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return pid;
}
try {
RBD_ECLIPSE_PID = getPID();
Runtime.getRuntime().exec("taskkill /F /PID "+RBD_ECLIPSE_PID);
} catch (IOException e1) {
e1.printStackTrace();
}
获取当前运行Java的进程标识符(PID)
C:\Users>jps
7152 Jps
4440 后面是应用程序启动类的类名
C:\Users>
可以在新建一个批处理,在文件中输入JPS,然后在JAVA中用ProcessBuilder执行批处量命令后得到进程对象,通过输入对象可以读取到控制台中的内容.如下所示:
ProcessBuilder此类用于创建操作系统进程。
每个 ProcessBuilder
实例管理一个进程属性集。start()
方法利用这些属性创建一个新的Process
实例。start()
方法可以从同一实例重复调用,以利用相同的或相关的属性创建新的子进程。
修改进程构建器的属性将影响后续由该对象的 start()
方法启动的进程,但从不会影响以前启动的进程或 Java 自身的进程。
ProcessBuilder pb = new ProcessBuilder("批处理文件名带后缀");//该文件在工程目录下就行了.
pb.directory(System.getProperty("user.dir") );//得到当前工程所在的绝对路径.
Map<String, String> env = pb.environment();
env.put("VAR1", "myValue");
env.remove("OTHERVAR");
env.put("VAR2", env.get("VAR1") + "suffix");
Process p = pb.start();
BufferedInputStream in = new BufferedInputStream(p.getInputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String s;
while ((s = br.readLine()) != null)
System.out.println(s);
}
}
启动java并得到当前JVM进程的pid
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
public class ShowOwnPID {
public static void main(String[] args) throws Exception {
int pid = getPid();
System.out.println("pid: " + pid);
System.in.read(); // block the program so that we can do some probing on it
}
private static int getPid() {
RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
String name = runtime.getName(); // format: "pid@hostname"
try {
return Integer.parseInt(name.substring(0, name.indexOf('@')));
} catch (Exception e) {
return -1;
}
}
}
Java的系统环境变量
java.version | Java 运行时环境版本 |
java.vendor | Java 运行时环境供应商 |
java.vendor.url | Java 供应商的 URL |
java.home | Java 安装目录 |
java.vm.specification.version | Java 虚拟机规范版本 |
java.vm.specification.vendor | Java 虚拟机规范供应商 |
java.vm.specification.name | Java 虚拟机规范名称 |
java.vm.version | Java 虚拟机实现版本 |
java.vm.vendor | Java 虚拟机实现供应商 |
java.vm.name | Java 虚拟机实现名称 |
java.specification.version | Java 运行时环境规范版本 |
java.specification.vendor | Java 运行时环境规范供应商 |
java.specification.name | Java 运行时环境规范名称 |
java.class.version | Java 类格式版本号 |
java.class.path | Java 类路径 |
java.library.path | 加载库时搜索的路径列表 |
java.io.tmpdir | 默认的临时文件路径 |
java.compiler | 要使用的 JIT 编译器的名称 |
java.ext.dirs | 一个或多个扩展目录的路径 |
os.name | 操作系统的名称 |
os.arch | 操作系统的架构 |
os.version | 操作系统的版本 |
file.separator | 文件分隔符(在 UNIX 系统中是“/”) |
path.separator | 路径分隔符(在 UNIX 系统中是“:”) |
line.separator | 行分隔符(在 UNIX 系统中是“/n”) |
user.name | 用户的账户名称 |
user.home | 用户的主目录 |
user.dir | 用户的当前工作目录 |
可以通过System.getenv()获取系统环境变量;
System.getenv("JAVA_HOME"); // 获取JAVA_HOME环境变量
System.getenv("CLASSPATH"); // 获取CLASSPATH环境变量
-----------------------------------------------------------------------------------
Java启动时,如果设置了-classpath或-cp参数的话,直接取这个参数后面的值,设置到java.class.path系统变量中
否则查询CLASSPATH环境变量的值,设置到java.class.path系统变量中
如果没有定义CLASSPATH环境变量或CLASSPATH环境变量为.,则把.设置到java.class.path系统变量中
在Lanuer生成AppClassLoader类加载器的时候,处理如下
String str = System.getProperty("java.class.path"); // 先拿到类加载路径
File[] arrayOfFile = str == null ? new File[0] : Launcher.getClassPath(str); // 转换为File
URL[] arrayOfURL = this.val$s == null ? new URL[0] : Launcher.pathToURLs(arrayOfFile); // 把File转换成URL
private static URL[] pathToURLs(File[] paramArrayOfFile)
{
URL[] arrayOfURL = new URL[paramArrayOfFile.length];
for (int i = 0; i < paramArrayOfFile.length; i++) {
arrayOfURL[i] = getFileURL(paramArrayOfFile[i]); // 获取URL
}
return arrayOfURL;
}
static URL getFileURL(File paramFile)
{
try
{
paramFile = paramFile.getCanonicalFile(); // 如果File为.,则直接返回的URL为当前目录,如D:\work_taobao\workspace\Main
} catch (IOException localIOException) {
}
try {
return ParseUtil.fileToEncodedURL(paramFile);
} catch (MalformedURLException localMalformedURLException) {
}
throw new InternalError();
}