System类是在Java程序中作为一个标准的系统类,实现了控制台与程序之间的输入输出流,系统的初始化与获取系统环境变量、数组的复制、返回一个精准的时间以及一些简单的对虚拟机的操作等。它是一个与Class类一样的直接注册进虚拟机的类,也就是直接与虚拟机打交道的类:
private static native void registerNatives();
static {
registerNatives();
}
private System() {
}
System类是一个不可被继承的类,同事不能由外部直接创建,只能有jvm来创建该类的实例。那么它是如何实现控制台的输入与输出的呢?
public final static InputStream in = null;
public final static PrintStream out = null;
public final static PrintStream err = null;
在其源码中定义了三个静态成员变量,也就是我们常写的System.out、System.in以及System.err。接着:
public static void setIn(InputStream in) {
checkIO();
setIn0(in);
}
public static void setOut(PrintStream out) {
checkIO();
setOut0(out);
}
public static void setErr(PrintStream err) {
checkIO();
setErr0(err);
}
private static void checkIO() {
SecurityManager sm = getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("setIO"));
}
}
private static native void setIn0(InputStream in);
private static native void setOut0(PrintStream out);
private static native void setErr0(PrintStream err);
检查完权限后,将控制台的流用Native方法写进、写出jvm中,System.out、System.in以及System.err三者并不是内部类,只是System类的成员变量,而已。下面介绍System类中常见的方法:
public static native long currentTimeMillis();
public static native long nanoTime();
currentTimeMillis方法返回一个当前的时间戳,是以毫秒为单位的,用来计算时间的,而nanoTime方法是也是返回一个当前的时间戳,是以纳秒为单位的,有的操作系统时间的最小精确度是10毫秒,所以这两个个方法可能会导致一些偏差。
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
arraycopy方法是在阅读Java源码中常见到的数组复制方法,是由jvm直接来复制的,不仅仅是复制数组,它还可以调节数组所占空间的大小。
private static Properties props;
private static native Properties initProperties(Properties props);
public static String getProperty(String key) {
checkKey(key);
SecurityManager sm = getSecurityManager();
if (sm != null) {
sm.checkPropertyAccess(key);
}
return props.getProperty(key);
}
identityHashCode方法是根据内存地址来获取其哈希值的:
public static native int identityHashCode(Object x);
以String类来做例子:
String name1=new String("蕾姆");
String name2=new String("蕾姆");
System.out.println("identityHashCode的值");
System.out.println(System.identityHashCode(name1));
System.out.println(System.identityHashCode(name2));
System.out.println("hashCode的值");
System.out.println(name1.hashCode());
System.out.println(name2.hashCode());
//打印:
//identityHashCode的值
//1836019240
//325040804
//hashCode的值
//1082376
//1082376
得出的结果是identityHashCode的值是不同的,再看另外一个例子:
String name1="蕾姆";
String name2="蕾姆";
System.out.println("identityHashCode的值");
System.out.println(System.identityHashCode(name1));
System.out.println(System.identityHashCode(name2));
System.out.println("hashCode的值");
System.out.println(name1.hashCode());
System.out.println(name2.hashCode());
//打印:
//identityHashCode的值
//1836019240
//1836019240
//hashCode的值
//1082376
//1082376
因为两个"蕾姆"的地址是一样的,所以两者的identityHashCode值相同。getProperty方法获取制定属性的系统变量值的,下面列出常用的系统环境变量值:
键 | 值 |
---|---|
file.separator | 文件分隔符(在 UNIX 系统中是“/”) |
path.separator | 路径分隔符(在 UNIX 系统中是“:”) |
line.separator | 行分隔符(在 UNIX 系统中是“/n”) |
user.name | 用户的账户名称 |
user.home | 用户的主目录 |
user.dir | 用户的当前工作目录 |
比如获取当前用户的主目录:
public static void main(String args[]) throws TestException {
System.out.println(System.getProperty("user.home"));
}
//输出:C:\Users\Suyeq
获取系统环境变量值:
public static String getenv(String name) {
SecurityManager sm = getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("getenv."+name));
}
return ProcessEnvironment.getenv(name);
}
接下来就是一些简单的jvm操作了:
public static void exit(int status) {
Runtime.getRuntime().exit(status);
}
该方法是终止虚拟机的操作,参数解释为状态码,非 0 的状态码表示异常终止。 而且,这个方法永远不会正常返回。 这是唯一一个能够退出程序并不执行finally的情况。因为这时候进程已经被杀死了,如下代码所示:
public static void main(String args[]) throws TestException {
System.out.println("hello");
System.exit(0);
System.out.println("world");
}
public static void main(String args[]) throws TestException {
try{
System.out.println("hello");
System.exit(0);
}catch (Exception e){
e.printStackTrace();
}finally {
System.out.println("world");
}
}
//两者都只会输出hello
System类还提供类调用jvm的垃圾回收器的方法,该方法让 jvm做了一些努力来回收未用对象或失去了所有引用的对象,以便能够快速地重用这些对象当前占用的内存。
public static void gc() {
Runtime.getRuntime().gc();
}
但我们基本不会自己调用该方法来回收内存,将垃圾回收交给jvm就行了。联想到finalize方法,该方法是用来回收没有被引用的对象的,调用System.gc方法便会调用这个方法,但是我们也不会用到finalize方法,因为这个方法设计出来是用来让c++程序员适应的。在Java编程思想中提到了使用到finalize方法可能的场景:
class SSS{
protected void finalize(){
System.out.println("回收了");
}
}
public class Test {
public static void main(String args[]) throws TestException {
new SSS();
System.gc();
}
}
//输出:回收了
也就是说,重写finalize方法,我们就能知道该对象实在何时被回收的了。