System.out.println() 中的 out 到底是什麼?面試時被問懵,事後翻閱源碼發現:out 是 PrintStream 實例,並非內部類。本文從源碼角度剖析 System 類的核心功能、靜態初始化流程、標準流綁定機制。
java.lang.System 為 final 工具類,主要功能五大類:
| 分類 | 代表方法/字段 | 說明 |
|---|---|---|
| 標準流 | in、out、err | InputStream/PrintStream,對應 stdin/stdout/stderr |
| 環境/屬性 | getenv()、getProperties()、setProperties() | 系統環境變量、JVM 系統屬性(-Dkey=val) |
| 動態加載 | load()、loadLibrary() | 加載本地庫(JNI) |
| 數組高效複製 | arraycopy() | native 實現,比循環快數倍,Collections.copy 內部調用 |
| JVM 控制 | gc()、runFinalization()、exit()、currentTimeMillis()、nanoTime() | 委託 Runtime.getRuntime(),建議業務代碼少直接調用 |
private static native void registerNatives();
static {
registerNatives(); // JNI 註冊 native 方法
}
JVM 在類初始化階段調用 registerNatives,進而觸發 initializeSystemClass() 完成核心設置。
private static void initializeSystemClass() {
// 1. 系統屬性初始化
Properties props = new Properties();
initProperties(props); // 填充系統屬性
VM.saveAndRemoveProperties(props); // 交給 VM 內部存儲
// 2. 行分隔符
lineSeparator = props.getProperty("line.separator");
// 3. 標準流綁定(核心!)
FileInputStream fdIn = new FileInputStream(FileDescriptor.in);
FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);
setIn0(new BufferedInputStream(fdIn));
setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true)); // autoFlush=true
setErr0(new PrintStream(new BufferedOutputStream(fdErr, 128), true));
// 4. 加載 zip 庫(支援 JAR/ZIP 讀取)
loadLibrary("zip");
// 5. 信號處理、OS 環境、主線程歸組、共享密鑰註冊、標記 VM 啟動完成
Terminator.setup();
VM.initializeOSEnvironment();
Thread.currentThread().getThreadGroup().add(Thread.currentThread());
setJavaLangAccess();
VM.booted();
}
FileDescriptor.in/out/err 對應 OS 級文件描述符 0/1/2,經 BufferedInputStream/OutputStream 包裝,再由 PrintStream 提供 println/printf 等便利方法。setOut0 為 native 方法,最終將 Java 級 PrintStream 綁定到 VM 內部結構,保證 System.out 全局可見。autoFlush=true:遇換行符/調用 println 自動刷新,保證日誌即時落盤。loadLibrary("zip"):載入 java.util.zip.ZipFile 依賴的本地庫,支援 JAR/War 類加載。System.gc():僅是建議,不保證觸發 Full GC,干擾 JVM 智能調優。System.setOut(new PrintStream(new FileOutputStream("log.txt"))) 可將控制台輸出導向文件,測試/日誌常用。arraycopy 為 native 實現,大數組拷貝優先用它,避免手寫循環。nanoTime() 適合測量耗時(單調遞增),currentTimeMillis() 適合獲取壁鐘時間。System 類是 JVM 與操作系統交互的橋樑,核心在於 靜態初始化階段完成標準流綁定、系統屬性加載、本地庫準備。理解其初始化流程,有助於排查啟動期異常、重定向日誌、優化數組操作。