Java程序在不同Java程序运行平台上运行时,可能需要获取平台的属性,或者调用平台命令来完成特定的功能。Java提供了System类和Runtime类来与程序的平台进行交互。
一、System类——java.lang.System
System类代表当前Java程序的平台,程序不能创建System对象,但System类提供了一系列的类变量和类方法,允许通过System类来调用这些类变量和类方法。
</font color=red>System类提供了标准输入、标准输出、错误输出的类变量,并提供了一些静态方法用于访问环境变量、系统属性的方法,还提供了加载文件和动态链接库的方法。
1、访问环境变量和系统属性
import java.io.*;
import java.util.*;
public class SystemTest
{
public static void main(String[] args) throws Exception
{
// 获取系统所有的环境变量
Map<String, String> env = System.getenv();
for (var name : env.keySet())
{
System.out.println(name + " ---> " + env.get(name));
}
// 获取指定环境变量的值
System.out.println(System.getenv("JAVA_HOME"));
// 获取所有的系统属性
Properties props = System.getProperties();
// 将所有系统属性保存到props.txt文件中
props.store(new FileOutputStream("props.txt")
, "System Properties");
// 输出特定的系统属性
System.out.println(System.getProperty("os.name"));
}
}
...
CommonProgramFiles(x86) ---> C:Program Files (x86)Common Files
ProgramFiles ---> C:Program Files
PUBLIC ---> C:UsersPublic
NUMBER_OF_PROCESSORS ---> 4
windir ---> C:Windows
=:: ---> ::
D:Javajdk-12
Windows 10
2、加载文件和动态链接库
加载文件和动态链接库主要对native方法有用,对一些特殊功能(如访问系统的底层硬件设备等)需要借助C语言来完成,此时需要C语言为Java方法提供实现:
(1)native方法只有签名没有实体。需要使用javax -h来编译该Java程序,将生成一个.h头文件和.class文件。
(2)写一个.cpp文件实现native方法,需要包含(1)中生成的.h文件(这个.h文件又包含JDK自带的jin.h文件)。
(3)将第(2)步的.cpp文件编译成动态链接库文件。
(4)在Java中用System类的loadLibrary..()方法或Runtime类的loadLibrary()方法加载第3步生成的动态链接库文件,java程序就可以调用这个native方法啦。
3、垃圾回收方法gc()和通知系统进行资源清理的runFinalization()
在垃圾回收机制回收垃圾对象之前,总会先调用它的finalization()方法,该方法可能导致对象重新获得引用,从而导致垃圾回收机制取消回收。
class GcTest
{
public static void main(String[] args)
{
for (int i=0;i<4 ;i++ )
{
new GcTest();//创建了对象但是没有引用,等待垃圾回收机制回收
System.gc();//强制系统进行垃圾回收
//Rumtime.getRuntime.gc();
}
}
@Override
public void finalize()
{
System.out.println("系统正在清理GcTest对象的资源");
}
}
---------- 运行Java捕获输出窗 ----------
系统正在清理GcTest对象的资源
系统正在清理GcTest对象的资源
输出完成 (耗时 0 秒) - 正常终止
4、获取系统当前时间
currentTimeMillis()和nanoTime(),它们都返回long型整数。代表的是当前时间与UTC 1970年1月1日午夜的时间差,前者以毫秒(1ms=10-3s)为单位,后者以微秒(1ns=10-9s)为单位。
这两个方法返回时间粒度取决与底层操作系统,可能所在的操作系统不支持以毫秒、纳秒作为计时单位。
//import java.lang.System;
//import java.lang.Math;
class SystemTimeTest
{
public static void main(String[] args)
{
long t=System.currentTimeMillis();
System.out.println(t);
double year=t/(Math.pow(10,3)*3600*24*365)+1970;
System.out.println(year);
}
}
---------- 运行Java捕获输出窗 ----------
1583902307999
2020.225212709253
输出完成 (耗时 0 秒) - 正常终止
5、标准输入输出、错误输出流
System类的in、out、err分别代表标准输入(通常是键盘)、标准输出(显示器)、错误输出流,并提供setIn()、setOut()、setErr()来改变系统的标准输入、标准输出和错误输出流。以后介绍
6、 对象的精确hashCode值
System.identityHashCode(Object x)方法,该方法返回指定对象精确的hashCode值,根据该对象的地址计算得到的hashCode值。当某个对象hashCode()方法被重写了,该方法依然可以获得它的hashCode值。如果两个对象的identityHashCode值相同,则它们指向同一个对象。
public class IdentityHashCodeTest
{
public static void main(String[] args)
{
// 下面程序中s1和s2是两个不同对象
var s1 = new String("Hello");
var s2 = new String("Hello");
// String重写了hashCode()方法——改为根据字符序列计算hashCode值,
// 因为s1和s2的字符序列相同,所以它们的hashCode方法返回值相同
System.out.println(s1.hashCode()
+ "----" + s2.hashCode());
// s1和s2是不同的字符串对象,所以它们的identityHashCode值不同
System.out.println(System.identityHashCode(s1)
+ "----" + System.identityHashCode(s2));
var s3 = "Java";
var s4 = "Java";
// s3和s4是相同的字符串对象,所以它们的identityHashCode值相同
System.out.println(System.identityHashCode(s3)
+ "----" + System.identityHashCode(s4));
}
}
---------- 运行Java捕获输出窗 ----------
69609650----69609650
1826771953----1406718218
245257410----245257410
输出完成 (耗时 0 秒) - 正常终止
二、Runtime类和Java 9的ProcessHandle
Runtime类代表Java程序的运行环境,每个Java程序都有一个与之对应的Runtime实例,运用程序通过该对象与之相连。应用程序不能创建自己的Rumtime实例,但可以通过getRumtime()方法获取与之相关联的Rumtime对象。
Runtime类也提供了gc()方法和runFinalization()方法通知系统进行垃圾回收、清理系统资源,并提供了load(String filename)和loadLibrary(String libname)方法来加载文件和动态链接库。
1、访问JVM的运行环境
Runtime类代表Java程序运行环境,可以访问JVM的相关信息,如处理器数量、内存信息等。如下程序
public class RuntimeTest
{
public static void main(String[] args)
{
//获取Java程序关联的运行对象
var rt=Runtime.getRuntime();
System.out.println("处理器的数量:"+rt.availableProcessors());
System.out.println("空闲的内存数:"+rt.freeMemory());
System.out.println("总内存数:"+rt.totalMemory());
System.out.println("可用最大内存数:"+rt.maxMemory());
}
}
---------- 运行Java捕获输出窗 ----------
处理器的数量:4
空闲的内存数:199982344
总内存数:201326592
可用最大内存数:3198156800
输出完成 (耗时 0 秒) - 正常终止
2、单独启动一个进程来运行操作系统命令
class ExecTest
{
public static void main(String[] args)
throws Exception
{
var rt=Runtime.getRuntime();
//运行记事本程序
rt.exec("notepad.exe");
}
}
上述代码将会启动windows系统的记事本程序。Runtime()提供了一系列的exec()方法来运行操作系统命令。
3、进程相关
通过exec启动平台命令后,他会变成一个进程,Java使用Process代表进程。Java 9新增了ProcessHand接口,通过该接口可以获得进程的ID、父进程和后代进程;通过接口的onExit()方法可在进程结束时完成某些行为。
import java.util.concurrent.*;
public class ProcessHandleTest
{
public static void main(String[] args)
throws Exception
{
var rt = Runtime.getRuntime();
// 运行记事本程序
Process p = rt.exec("notepad.exe");
ProcessHandle ph = p.toHandle();
System.out.println("进程是否运行: " + ph.isAlive());
System.out.println("进程ID: " + ph.pid());
System.out.println("父进程: " + ph.parent());
// 获取ProcessHandle.Info信息
ProcessHandle.Info info = ph.info();
// 通过ProcessHandle.Info信息获取进程相关信息
System.out.println("进程命令: " + info.command());
System.out.println("进程参数: " + info.arguments());
System.out.println("进程启动时间: " + info.startInstant());
System.out.println("进程累计运行时间: " + info.totalCpuDuration());
// 通过CompletableFuture在进程结束时运行某个任务
CompletableFuture<ProcessHandle> cf = ph.onExit();
cf.thenRunAsync(()->{
System.out.println("程序退出");
});
Thread.sleep(5000);
}
}
---------- 运行Java捕获输出窗 ----------
进程是否运行: true
进程ID: 6192
父进程: Optional[6804]
进程命令: Optional[C:WindowsSystem32
otepad.exe]
进程参数: Optional.empty
进程启动时间: Optional[2020-03-11T07:27:38.006Z]
进程累计运行时间: Optional[PT0.125S]
输出完成 (耗时 6 秒) - 正常终止