歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Java I/O操作入門教程

Java 提供的標准模型有 System.in, System.out, System.err。平日裡我們經常用到他們3個,其中用的最多的就是System.out.println()等了,最近突然想到,他們是怎麼實現的呢?

下面是JDK中者三者的定義的源碼:
    public final static InputStream in = nullInputStream();

    public final static PrintStream out = nullPrintStream();

    public final static PrintStream err = nullPrintStream();

        從上面的代碼上我們可以用到,除了in,其他兩者都已經封裝成了PrintStream格式,我們可以直接使用他們。而in則有點特殊,它只是一個沒有被包裝過的未經加工的InputStream(這是Thinking in java的原話,但是在實際我發現,這個InputStream的真實類型應該是BufferedInputStream,有可能是我對這句話的理解有誤導致的吧),鄙人在學C++的時候,依稀記得,當出現cin>>這樣的標識的時候,程序會停下來等待鍵盤的輸入操作,但是在Java中用起來要費勁些,直接這麼簡單粗暴的輸入,是沒有效果的。

大話設計模式(帶目錄完整版) PDF+源代碼 http://www.linuxidc.com/Linux/2014-08/105152.htm

Java中介者設計模式 http://www.linuxidc.com/Linux/2014-07/104319.htm

Java 設計模式之模板方法開發中應用 http://www.linuxidc.com/Linux/2014-07/104318.htm

設計模式之 Java 中的單例模式(Singleton) http://www.linuxidc.com/Linux/2014-06/103542.htm

一般情況下,我們需要對其進行包裝,下面的例子就是其中的一個方法

        InputStream in = System.in;
        BufferedReader stdin = new BufferedReader(new InputStreamReader(in));//對其進行包裝
        System.out.print("請輸入字符:  ");
        String str = stdin.readLine();//等待鍵盤輸入,按回車結束
        System.out.println("你輸入的字符為:  " + str);

  回到開篇講到的內容,我們注意到in,out返回的是方法 nullInputStream()、nullPrintStream()方法執行後的結果,我第一次看的時候,想當然就以為這兩個方法返回的是inputStream、和PrintStream。後來走到源碼中一看,才傻眼了,這兩個方法是這樣的

/**

    * The following two methods exist because in, out, and err must be

    * initialized to null.  The compiler, however, cannot be permitted to

    * inline access to them, since they are later set to more sensible values

    * by initializeSystemClass().

    */

  private static InputStream nullInputStream() throws NullPointerException {

  if (currentTimeMillis() > 0) { //一般情況下,不會出現<=0的情況

      return null;

  }

  throw new NullPointerException();

  }

 

  private static PrintStream nullPrintStream() throws NullPointerException {

  if (currentTimeMillis() > 0) {

      return null;

  }

  throw new NullPointerException();

  }


  從上段代碼中我們可以看到,其實這兩個方法返回的是null(或者是出現一個運行時的異常)。那麼問題來了,竟然是null值,那它們是如何操作與初始化的。

 好在,方法之前有一大段的注釋,說明這連個方法之所以存在時應為in,out等必須初始化為空(why?等待高手解答啊。。。),那麼何時才是真正的執行初始化操作呢?

答案就在initializeSystemClass()這個方法裡

/**

    * Initialize the system class.  Called after thread initialization.

    */

    private static void initializeSystemClass() {

    props = new Properties();

    initProperties(props);

    sun.misc.Version.init();

 

    // Load the zip library now in order to keep java.util.zip.ZipFile

    // from trying to use itself to load this library later.

    loadLibrary("zip");

 

    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));

/**

*  以上只是部分代碼,下面是無關的代碼,略去

*/


  在上面這段代碼裡,setIn0等方法的代碼如下

    private static native void setIn0(InputStream in);
    private static native void setOut0(PrintStream out);
    private static native void setErr0(PrintStream err);

可以看到上面這三個方法都是native方法,那麼我們就不能繼續跟下去了,但是我們可以看setIn,setOut,setErr方法,我們知道,這三個方法用於沖定向標准的I/O流。

public static void setIn(InputStream in) {

    checkIO(); //用於權限的檢查

    setIn0(in);

    }

 

 public static void setOut(PrintStream out) {

    checkIO();

    setOut0(out);

    }

 

  public static void setErr(PrintStream err) {

    checkIO();

    setErr0(err);

    }


  這三個方法都調用了相應的setXX0()方法,由此我們可以推出setIn0等方法用於重定向輸入輸出流。

    那麼重定位到哪呢?  答案就是

new FileInputStream(FileDescriptor.in)

new FileOutputStream(FileDescriptor.out)

new FileOutputStream(FileDescriptor.err)<br><br>

 public static final FileDescriptor in = standardStream(0);
 public static final FileDescriptor out = standardStream(1);
 public static final FileDescriptor err = standardStream(2);

/**
* 返回handle為fd的FileDescriptor; 在傳統的unix的系統中,fd為0,1,2分別表示為標准輸入,標准輸出和錯誤輸出。
*/
  private static FileDescriptor standardStream(int fd) {
        FileDescriptor desc = new FileDescriptor();
        desc.handle = set(fd);
        return desc;
    }

所以 setIn0(new BufferedInputStream(fdIn)); 就是將標准輸入先封裝成文件輸入流(FileInputstream),再封裝成BufferedInputStream(典型的裝飾模式啊)

差不多這些了,大致過程都清楚了,但是仍有一些細節後續需要繼續弄明白,如:為什麼初始化必須為空,什麼時候調用的initializeSystemClass()方法。

更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2014-10/107588p2.htm

Copyright © Linux教程網 All Rights Reserved