6.2 系统相关


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 秒) - 正常终止
原文地址:https://www.cnblogs.com/weststar/p/12461738.html