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