IO是計算機中Input和Output簡稱,即輸入和輸出。 無論是系統、還是語言的設計中IO的設計都是異常復雜的。JAVA語言在IO設計方面是比較成功的,不僅是面向對象,而且利用裝飾器設計模式(后面會寫針對設計模式的文章)減少了大量的類,提供了較好的擴展性。
那Java IO怎么寫入/讀取數據?
Java IO類庫可以分為輸入流和輸出流,輸入流來讀數據,輸出流來寫數據。
輸出流
實例代碼一:
//輸出流
public static void outputStream() throws IOException{ //創建一個File實例 File file = new File("/home/wenhaibo/IOTest.txt");
//FileOutputStream為文件輸出流 FileOutputStream out = new FileOutputStream(file); //將內容轉換為字節碼輸出 out.write("This is IOTest".getBytes());
//強制輸出內存中所有內容 out.flush();
//關閉輸出流 out.close();
}
輸入流
實例代碼二:
//輸入流
public static void inputStream() throws IOException{ //創建一個File實例 File file = new File("/home/wenhaibo/IOTest.txt");
//FileInputStream為文件輸入流 FileInputStream in = new FileInputStream(file);
byte[] b = new byte[1024];
//將 byte.length 個字節的數據讀入一個 byte 數組中
int len =in.read(b);
//將字節碼轉為字符串打印輸出 System.out.println(new String(b, 0, len));
//關閉輸入流 in.close();
}
字節流和字符流
上面的實例中用的字節流方式寫入/讀取數據,Java IO不僅提供字節流方式還通過了字符流處理方式,處理字符流用Reader、Writer兩個專門操作字符流的類。
Writer
實例代碼三:
//字符流
public static void outputStreamWriter() throws IOException{ //創建一個File實例 File file = new File("/home/wenhaibo/IOTest.txt");
//FileWriter為文件輸出流 Writer out = new FileWriter(file); //直接輸出字符 out.write("This is IOTest");
//強制輸出內存中所有內容 out.flush();
//關閉輸出流 out.close();
}
Reader
實例代碼四:
public static void inputStreamReader() throws IOException{
//創建一個File實例 File file = new File("/home/wenhaibo/IOTest.txt");
//Reader為文件輸入流 Reader in=new FileReader(file);
char[] c=new char[1024];
//將 byte.length 個字節的數據讀入一個 byte 數組中
int len =in.read(c);
//將字節碼轉為字符串打印輸出 System.out.println(new String(c, 0, len));
//關閉輸入流 in.close();
}
緩沖流
Java緩沖流是在輸入流和輸出流之上進行了一次包裝(裝飾器設計模式),目的是解決頻繁寫入/讀取數據時效率差的問題。緩沖流先將數據緩存起來,然后一起寫入或讀取出來。
字節緩沖流類:BufferedInputStream 和 BufferedOutputStream
字符緩沖流類:BufferedReader 和 BufferedWriter
flush()方法
當向文件寫入數據時是先將輸出流寫入到緩沖區,當緩沖區寫滿后才將緩沖區的內容輸出到文件中。但是當主機完成輸出流的輸出后,有可能緩沖區這個時候還沒有被填滿,這樣的話,就會一直等待主機發送內容,這時候,就可以使用flush()方法將緩沖區的內容強制輸出到文件中,清空緩沖區。
所以,一般在關閉輸出流之前,要先調用flush()方法強制緩沖區中的內容輸出,并清空緩沖區。
Java NIO的出現
因為傳統的IO是阻塞而且低效的,JDK 1.4 提供了NIO(New IO)API。
那傳統的IO和NIO有哪些不同?
面向流與面向緩沖
傳統IO處理數據就像“小雞啄米”,而NIO則是“狼吞虎咽”。
NIO中引入了緩沖區的概念,緩沖區作為傳輸數據的基本單位塊,所有對數據的操作都是基于將數據移進/移出緩沖區而來;讀數據的時候從緩沖區中取,寫的時候將數據填入緩沖區。盡管傳統JavaIO中也有相應的緩沖區過濾器流(BufferedInputStream等),但是移進/移出的操作是由程序員來包裝的,它本質是對數據結構化和積累達到處理時的方便,并不是一種提高I/O效率的措施。NIO的緩沖區則不然,對緩沖區的移進/移出操作是由底層操作系統來實現的。
除了效率上的差別外,緩沖區在數據分析和處理上也帶來的很大的便利和靈活性。
阻塞與非阻塞IO
傳統JavaIO是基于阻塞I/O模型。這意味著,當一個線程調用read() 或 write()時,該線程被阻塞,直到有一些數據被讀取,或數據完全寫入。該線程在此期間不能再干任何事情了。 Java NIO的非阻塞模式,使一個線程從某通道發送請求讀取數據,但是它僅能得到目前可用的數據,如果目前沒有數據可用時,就什么都不會獲取。而不是保持線程阻塞,所以直至數據變的可以讀取之前,該線程可以繼續做其他的事情。 非阻塞寫也是如此。一個線程請求寫入一些數據到某通道,但不需要等待它完全寫入,這個線程同時可以去做別的事情。 線程通常將非阻塞IO的空閑時間用于在其它通道上執行IO操作,所以一個單獨的線程現在可以管理多個輸入和輸出通道(channel)。
Java NIO 核心API
Java NIO 中Channel,Buffer 和 Selector 構成了核心的API。
Channel
Channel(通道)和IO中的流是差不多一個等級的。只不過流是單向的,譬如:InputStream, OutputStream.而Channel是雙向的,既可以用來進行讀操作,又可以用來進行寫操作。
NIO中的Channel的主要實現有:
- FileChannel
- DatagramChannel
- SocketChannel
- ServerSocketChannel
分別可以對應文件IO、UDP和TCP(Server和Client)。
Buffer
NIO中的關鍵Buffer實現有:ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer,分別對應基本數據類型: byte, char, double, float, int, long, short。
選擇器(Selectors)
Java NIO的選擇器允許一個單獨的線程來監視多個輸入通道,你可以注冊多個通道使用一個選擇器,然后使用一個單獨的線程來“選擇”通道:這些通道里已經有可以處理的輸入,或者選擇已準備寫入的通道。這種選擇機制,使得一個單獨的線程很容易來管理多個通道。
實例代碼五:
//使用NOI輸出
public static void inputNIOChannel() throws IOException { //創建一個File實例 File file = new File("/home/wenhaibo/IOTest.txt");
//FileInputStream為文件輸入流 FileInputStream in = new FileInputStream(file);
//緩沖器向通道輸入數據 FileChannel fileChannel = in.getChannel();
//創建一個容量為1024字節的ByteBuffer
ByteBuffer buf = ByteBuffer.allocate(1024);
//寫入數據到Buffer int bytesRead = fileChannel.read(buf);
while(bytesRead != -1)
{ //回繞緩沖區(輸出通道會從數據的開頭而不是末尾開始) buf.flip(); while(buf.hasRemaining())
{ System.out.print((char)buf.get());
} /** * 壓縮此緩沖區,compact方法會執行兩個動作 * 1.清除之前寫好的字符
* 2.通過標記位置為0
* 這就為什么要結合filp()使用 */ buf.compact(); //寫入數據到Buffer bytesRead = fileChannel.read(buf);
} }
實際應用中在不考慮并發和讀取/寫入數據使用頻率比較高的情況下Java IO已經可以勝任,但在使用到網絡IO中Java IO已經不能滿足實際需求,Java NIO 無疑是更好的選擇。






