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