搜尋

首頁  >  問答  >  主體

透過Java IO流的形式實現鍵盤輸入的疑問

巴扎黑巴扎黑2804 天前546

全部回覆(2)我來回復

  • ringa_lee

    ringa_lee2017-04-18 10:56:47

    因為System.in就是一個靜態的InputStream實例,你可以在java api文件上看看

    回覆
    0
  • 天蓬老师

    天蓬老师2017-04-18 10:56:47

    首先,System.in是一個InputStream類型的對象,在原始碼裡是這樣的:

    /**
     * The "standard" input stream. This stream is already
     * open and ready to supply input data. Typically this stream
     * corresponds to keyboard input or another input source specified by
     * the host environment or user.
     */
    public final static InputStream in = null;

    可見System.in屬於標準輸入,可以透過鍵盤或其他方式輸入資料。
    但在原始碼裡,該物件並沒有(顯式)初始化的方法,透過閱讀原始碼,可發現下面這個方法:

    /**
     * Reassigns the "standard" input stream.
     *
     * <p>First, if there is a security manager, its <code>checkPermission</code>
     * method is called with a <code>RuntimePermission("setIO")</code> permission
     *  to see if it's ok to reassign the "standard" input stream.
     * <p>
     *
     * @param in the new standard input stream.
     *
     * @throws SecurityException
     *        if a security manager exists and its
     *        <code>checkPermission</code> method doesn't allow
     *        reassigning of the standard input stream.
     *
     * @see SecurityManager#checkPermission
     * @see java.lang.RuntimePermission
     *
     * @since   JDK1.1
     */
    public static void setIn(InputStream in) {
        checkIO();
        setIn0(in);
    }
    
    private static native void setIn0(InputStream in);

    閱讀註解可見此方法用於設定
    研究setIn0(in),可見該方法是透過呼叫底層介面來實現in的設定,那麼在軟體執行時,是如何初始化的呢?
    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();
    }
    
    /**
     * Initialize the system class.  Called after thread initialization.
     */
    private static void initializeSystemClass() {
    ...
    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));
    setErr0(new PrintStream(new BufferedOutputStream(fdErr, 128), true));
    ...
    }

    由此段程式碼可知,軟體執行時,先運行靜態程式碼區塊,呼叫registerNatives()這個底層方法對System類別進行初始化,該方法則是呼叫initializeSystemClass()方法來初始化System類別的,這兩步驟都是透過VM實現的,然後閱讀initializeSystemClass()這個方法,可見到呼叫setIn0,setOut0,steErr0三段程式碼,分別初始化了標準輸入,標準輸出,標準錯誤三種輸出流,至此System.in、out、err的初始化流程就都弄清楚了。
    標記為native的方法是JVM呼叫其他程式碼實現的功能,這個和底層有關係,我覺得這個感覺就像是Bootstrap Classloader其實是C實現,但由JVM呼叫來載入各個基礎JAR classes相似吧。
    另外你說InputStream是抽象類別(介面),實際上介面類別抽象類別都可以作為參數,但實作肯定不是他們實現的,他們不能被實現,但可以作為參數,畢竟父類別出現的地方子類別都可以替代,所以這裡不存在問題

    回覆
    0
  • 取消回覆