Java源码分析——System类解析

版权声明:博主GitHub地址https://github.com/suyeq欢迎大家前来交流学习 https://blog.csdn.net/hackersuye/article/details/84259680

    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方法,我们就能知道该对象实在何时被回收的了。

猜你喜欢

转载自blog.csdn.net/hackersuye/article/details/84259680