首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Java System.in 本質解析:為什麼抽象類 InputStream 能被實例化賦值給 in

     編輯:程式設計綜合問答

問題核心

`System.in` 聲明為 `public final static InputStream in = nullInputStream();`
`InputStream` 是抽象類,不能直接 `new InputStream()`,但 `nullInputStream()` 返回的是**具體子類實例** (或 `null`),而非抽象類本身。

源碼級追蹤 (JDK 8/11/17 核心邏輯一致)

// java.lang.System
public final static InputStream in = nullInputStream();
public final static PrintStream out = nullPrintStream();
public final static PrintStream err = nullPrintStream();

private static InputStream nullInputStream() {
    // 安全檢查:防止類初始化循環依賴
    if (currentTimeMillis() > 0) 
        return null; // 早期返回 null,後續由 initializeSystemStreams() 真正初始化
    throw new NullPointerException();
}

// 真正初始化在私有靜態方法中 (JVM 啟動時調用)
private static void initializeSystemStreams() {
    // 獲取文件描述符 0 (stdin)
    FileDescriptor fdIn = FileDescriptor.in; 
    // 創建 BufferedInputStream 包裝 FileInputStream (具體實現類)
    InputStream in = new BufferedInputStream(new FileInputStream(fdIn));
    // 通過反射設置 final 字段 (JDK 內部特權操作)
    setIn0(in); 
    // out/err 同理包裝為 BufferedOutputStream -> PrintStream
}

關鍵點解析

  1. 多態賦值:`InputStream in = new BufferedInputStream(...)` 完全合法。`BufferedInputStream` 繼承 `FilterInputStream` → `InputStream`,實現了所有抽象方法 (`read()` 等)。
  2. 延遲初始化:`nullInputStream()` 先返回 `null` 避免類初始化死鎖,JVM 啟動後由 `initializeSystemStreams()` (native/反射) 注入真實流。
  3. Native 層支撐:`FileInputStream` 通過 JNI 調用操作系統 `read` 系統調用 (Linux `read(fd, buf, count)`,Windows `ReadFile`),實際讀取鍵盤/管道/重定向輸入。
  4. 緩衝裝飾:`BufferedInputStream` 增加 8KB 緩衝區,減少系統調用次數,提升性能。

應用開發者無需關心的細節

  • 直接使用 `System.in`、`new Scanner(System.in)`、`new BufferedReader(new InputStreamReader(System.in))`。
  • 重定向輸入:`java MyProg < input.txt`,OS 層面已將 fd 0 指向文件,Java 透明感知。
  • 測試時模擬輸入:`System.setIn(new ByteArrayInputStream("test\n".getBytes()))` (利用 `setIn0` 反射修改 final 字段)。

延伸:System.out/err 同理

PrintStream out = new PrintStream(
    new BufferedOutputStream(
        new FileOutputStream(FileDescriptor.out)), true); // autoFlush=true

`true` 參數開啟自動刷新:遇換行、調用 `println`、寫入位元組數組時自動 `flush()`,保證控制台即時輸出。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved