深入分析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類要知道其基本的內部實現以及常用的操作即可。