1. 程式人生 > >Java原始碼分析——System類解析

Java原始碼分析——System類解析

    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方法,我們就能知道該物件實在何時被回收的了。