歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Java I/O操作入門教程

Java I/O操作入門教程

日期:2017/3/1 9:39:02   编辑:Linux編程

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