1. 程式人生 > 程式設計 >深入分析java中的System類

深入分析java中的System類

System是一個類,這個System類主要是一些與系統相關的屬性和方法的集合,而且其內部的方法全部是靜態的,所以我們直接使用System直接呼叫就好,比如我們常用的一個System.out.print。這篇文章我們就來分析一下System類。

一、System概述

System就是系統的意思。因此它的主要操作肯定也是和系統資訊有關。這個類位於java.lang包。可能我們都有一個疑惑,我們從來沒見過System被例項化,這是因為System類內部的建構函式是私有的,在外部不能訪問,因此也就不能被例項化了。

他主要有如下功能:

(1)系統資訊的訪問,如外部屬性和環境變數等

(2)垃圾回收相關操作

(3)標準輸入輸出

(4)比較常用的其他操作,比如陣列拷貝

接下來我們就對這些功能進行一個測試與描述:

二、System功能演示

1、獲取設定屬性方法

也就是說我們的System如何獲取系統的屬性,或者說是呼叫哪個方法獲取屬性。

(1)contains(Object value)、containsKey(Object key):判斷給定的引數或屬性關鍵字在屬性表中有定義,返回True或者False;

(2)getProperty(String key)、getProperty(String key,String default):根據引數獲取屬性

(3)list(PrintStream s)、list(PrintWriter w): 在輸出流中輸出屬性表內容;

(4)size():返回當前屬性表中定義的屬性關鍵字個數。

我們當然可以設定屬性:

(1)put(Object key,Object value) :向屬性表中追加屬性關鍵字和關鍵字的值;

(2)remove(Object key) :從屬性表中刪除關鍵字。

2、獲取系統屬性

上面我們可以直接使用System.contains等方法來呼叫,下面我們可以輸入以下引數來獲取系統資訊。

功能 描述
java.version Java 執行時環境版本
java.vendor Java 執行時環境供應商
java.vendor.url Java 供應商的 URL
java.home Java 安裝目錄
java.vm.specification.version Java 虛擬機器器規範版本
java.vm.specification.vendor Java 虛擬機器器規範供應商
java.vm.specification.name Java 虛擬機器器規範名稱
java.vm.version Java 虛擬機器器實現版本
java.vm.vendor Java 虛擬機器器實現供應商
java.vm.name Java 虛擬機器器實現名稱
java.specification.version Java 執行時環境規範版本
java.specification.vendor Java 執行時環境規範供應商
java.specification.name Java 執行時環境規範名稱
java.class.version Java 類格式版本號
java.class.path Java 類路徑
java.library.path 載入庫時搜尋的路徑列表
java.io.tmpdir 預設的臨時檔案路徑
java.compiler 要使用的 JIT 編譯器的名稱
java.ext.dirs 一個或多個擴充套件目錄的路徑
os.name 作業系統的名稱
os.arch 作業系統的架構
os.version 作業系統的版本
file.separator 檔案分隔符(在 UNIX 系統中是“/”)
path.separator 路徑分隔符(在 UNIX 系統中是“:”)
line.separator 行分隔符(在 UNIX 系統中是“/n”)
user.name 使用者的賬戶名稱
user.home 使用者的主目錄
user.dir 使用者的當前工作目錄

然後使用程式碼測試一下幾個比較典型的吧:

public class SystemTest {
	public static void main(String[] args) {
		System.out.println("Java 執行時環境版本         :" + System.getProperty("java.version"));
		System.out.println("Java 執行時環境供應商     :" + System.getProperty("java.vendor"));
		System.out.println("Java 執行時環境規範版本    :" + System.getProperty("java.specification.version"));
		System.out.println("Java 執行時環境規範供應商:" + System.getProperty("java.specification.vendor"));
		System.out.println("Java 執行時環境規範名稱    :" + System.getProperty("java.specification.name"));
		System.out.println("作業系統的名稱:" + System.getProperty("os.name"));
		System.out.println("作業系統的架構:" + System.getProperty("os.arch"));
		System.out.println("作業系統的版本:" + System.getProperty("os.version"));
		System.out.println("使用者的賬戶名稱          :" + System.getProperty("user.name"));
		System.out.println("使用者的主目錄              :" + System.getProperty("user.home"));
		System.out.println("使用者的當前工作目錄  : " + System.getProperty("user.dir"));
	}
}
複製程式碼

當然執行一下我們的控制檯就有結果了:

在這裡只是挑選了一部分進行測試,引數已經列出來了,其他的可以自己測。

三、常見操作

1、拷貝陣列arraycopy

public class SystemTest {
	public static void main(String[] args) {
		int[] arr1 = {1,2,3,4,5 };
        int[] arr2 = { 6,7,8,9,10};
        /*
         * 第一個引數arr1:被複制的陣列
         * 第二個引數1:arr1中要複製的起始位置
         * 第三個引數arr2:目標陣列
         * 第四個引數0:目標陣列的複製起始位置
         * 第五個引數3:目標陣列的複製結束位置
         */
        System.arraycopy(arr1,1,arr2,0,3);
        for (int i = 0; i < 5; i++)
            System.out.print(arr2[i] + " ");
	}
}
複製程式碼

2、獲取系統時間

public class SystemTest {
	public static void main(String[] args) {
		System.out.println(System.currentTimeMillis());
        System.out.println(System.nanoTime());
	}
}
//輸出:1565841056267(時間戳)
//輸出:1130607059454400
複製程式碼

四、垃圾回收相關操作:System.gc

這句話表明運行了垃圾回收器。java虛擬機器器會回收一下系統垃圾,比如說沒有使用的物件。

public class SystemTest {
	public static void main(String[] args) {
		User user = new User();//新建一個物件
		System.out.println(user.toString());
		user=null;//將引用置為空
		System.gc();//垃圾回收
		System.out.println(user.toString());
	}
}
複製程式碼

我們看一下執行結果再來分析

我們可以看到,在進行完垃圾回收之後,再輸入User相關資訊時由於找不到物件,因此報了空指標異常。

我們進入到System.gc內部看一下,看看內部執行了什麼操作,

public static void gc() {
      Runtime.getRuntime().gc();
}
複製程式碼

在這裡我們可以看到其實是執行了Runtime的垃圾回收操作。我們在進入會發現其實垃圾回收就是Runtime做的。

五、原始碼分析

1、初始化

我們進入到System的原始碼中,可以看到首先由這樣的描述:

/* register the natives via the static initializer.
 * VM will invoke the initializeSystemClass method to complete
 * the initialization for this class separated from clinit.
 * Note that to use properties set by the VM,see the constraints
 * described in the initializeSystemClass method.
*/
private static native void registerNatives();
static {
    registerNatives();
}

/** Don't let anyone instantiate this class */
private System() {}
複製程式碼

上面是什麼意思呢?

首先:registerNatives()方法是一個入口方法,註冊成了natives,也就是說該方法會令vm通過呼叫initializeSystemClass方法來完成初始化工作。

然後:建構函式被設定成private,說明我們不能例項化這個類,註釋也已經說明瞭。

既然System初始化的操作是通過initializeSystemClass,我們不如進入到這個類中去看看。

private static void initializeSystemClass() {
    	//第一步:初始化props
        props = new Properties();
        initProperties(props);  // initialized by the VM
    	//第二步:vm儲存刪除一些系統屬性
        sun.misc.VM.saveAndRemoveProperties(props);
    	//第三步:獲取系統分隔符
        lineSeparator = props.getProperty("line.separator");
    	//第四步:初始化系統的一些配置
        sun.misc.Version.init();
    	//第五步:輸入輸出流初始化
        FileInputStream fdIn = new FileInputStream(FileDescriptor.in);
        FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
        FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);
        setIn0(new BufferedInputStream(fdIn));
        setOut0(newPrintStream(fdOut,props.getProperty("sun.stdout.encoding")));
        setErr0(newPrintStream(fdErr,props.getProperty("sun.stderr.encoding")));
        loadLibrary("zip");
        //第六步:設定平臺相關的訊號處理
    	Terminator.setup();
    	//第七步:初始化系統環境
        sun.misc.VM.initializeOSEnvironment();
    	//第八步:把自己新增到執行緒組
        Thread current = Thread.currentThread();
        current.getThreadGroup().add(current);
        //第九步:初始化
    	setJavaLangAccess();
        sun.misc.VM.booted();
 }
複製程式碼

通過initializeSystemClass,我們已經能夠明白System是如何初始化的,對於每一步,我們可以繼續深入下去觀察其具體實現,在這裡就不贅述了。

2、類屬性

類屬性其實主要是輸入輸出流

public final static InputStream in = null;
public final static PrintStream out = null;
public final static PrintStream err = null;
複製程式碼

3、類方法

在這裡肯定不能所有的方法都講一遍,在這裡列舉幾個比較重要的方法。

(1)getProperty:獲取系統屬性

public static String getProperty(String key) {
    	//校驗key的值
        checkKey(key);
    	//檢查引數是否安全
        SecurityManager sm = getSecurityManager();
        if (sm != null) {
            sm.checkPropertyAccess(key);
        }
		//獲取系統屬性
        return props.getProperty(key);
}
複製程式碼

我們在這裡發現,其實獲取屬性的操作最關鍵的就是最後一句props.getProperty(key)。我們進入到這個方法看看:

    public String getProperty(String key) {
        Object oval = super.get(key);
        String sval = (oval instanceof String) ? (String)oval : null;
        return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval;
    }
複製程式碼

也就是說其實是 一直是回撥defaults.getProperty(key),讓父類一直不停的去呼叫。最後返回一個String。

(2)checkKey:校驗key

private static void checkKey(String key) {
        if (key == null) {
            throw new NullPointerException("key can't be null");
        }
        if (key.equals("")) {
            throw new IllegalArgumentException("key can't be empty");
        }
}
複製程式碼

裡面很簡單就是看看是否為空。

(3)setProperties:設定系統屬性

   public static void setProperties(Properties props) {
        SecurityManager sm = getSecurityManager();
        if (sm != null) {
            sm.checkPropertiesAccess();
        }
        if (props == null) {
            props = new Properties();
            initProperties(props);
        }
        System.props = props;
    }
複製程式碼

最核心的就是最後一行,但是前面首先檢驗了是否是系統安全的屬性,而且也根據這個屬性初始化了一次。我們進入initProperties。

private static native Properties initProperties(Properties props);
複製程式碼

這是一個native方法。

(4)exit():退出當前的jvm

public static void exit(int status) {
     Runtime.getRuntime().exit(status);
}
複製程式碼

其實呼叫的也是runtime的退出方法。

(5)其他方法

 public static native long currentTimeMillis();
 public static native long nanoTime();
 public static native void arraycopy(Object src,int  srcPos,Object dest,int destPos,int length);
public static native int identityHashCode(Object x);
複製程式碼

我們會發現經常操作的這些方法其實也是native的。

(6)安全管理機制

與之相關的方法有三個

public static void setSecurityManager(final SecurityManager s) {
    try {
        s.checkPackageAccess("java.lang");
    } catch (Exception e) {
    }
    setSecurityManager0(s);
}
複製程式碼

第二個:

private static synchronized void setSecurityManager0(final SecurityManager s) {
    SecurityManager sm = getSecurityManager();
    if (sm != null) {
        sm.checkPermission(new RuntimePermission ("setSecurityManager"));
    }
    if ((s != null) && (s.getClass().getClassLoader() != null)) {
        AccessController.doPrivileged(new PrivilegedAction() {
        	public Object run() {
            	s.getClass().getProtectionDomain().implies(SecurityConstants.ALL_PERMISSION);
            	return null;
        	}
    	});
    }
    security = s;
    InetAddressCachePolicy.setIfNotSet(InetAddressCachePolicy.FOREVER);
}
複製程式碼

還有最後一個

public static SecurityManager getSecurityManager() {
    return security;
}
複製程式碼

OK。原始碼分析也就先說到這裡,對於System類要知道其基本的內部實現以及常用的操作即可。