好記性不如爛筆頭,就拿Java IO來講吧,這部分的基礎類我大學都已經學過一遍了,可是如今忘記的差很少了,因此準備寫一篇博客,講這些東西都回憶一下,而且整理一下。html
首先借用網上的一張圖:java
縱向分爲字節流和字符流.橫向分爲針對讀寫進行劃分編程
在這幅圖中那些很是基本的也就不提了,就提一下須要注意的幾個類。app
1.BufferedXXX 緩衝 不管是讀仍是寫,字符仍是字節流都存在,主要是做爲一個緩衝的做用,起到的做用是減小和操做系統的IO交互,減小性能消耗函數
2.PipedXXX 管道 管道也是存在這四個當中,管道的用法至關於咱們使用的隊列,你往其中塞入數據的話,那麼從另外一端就能夠取出來,不然就會堵塞,下面使用字符流的輸入流做爲例子:性能
package com.hotusm.io.learn.string; import java.io.PipedReader; import java.io.PipedWriter; /** * 字符流取出管道 * 1.PipedReader(PipedWriter src, int pipeSize) 第一個參數爲輸入管道流 第二個參數爲管道的大小 * 若是沒有第二個參數那麼默認的管道大小爲1024 * 2.PipedReader() 沒有參數的構造函數爲還沒初始化的狀態 須要調用connect(PipedWriter src) * 3.read() 會進行堵塞 直到有數據流到達 而後在進行讀取 */ public class PipedReaderTest { public static void main(String[] args) throws Exception{ testPipedReaderConnect(); } public static void testPipedReaderConnect() throws Exception{ PipedReader pipedReader=new PipedReader(); final PipedWriter writer=new PipedWriter(); //這種方式進行鏈接 pipedReader.connect(writer); char[] buff=new char[10]; new Thread(()->{ try { //停留三秒鐘以後 Thread.sleep(3000); writer.write("hello piped"); } catch (Exception e) { e.printStackTrace(); } }).start(); pipedReader.read(buff); System.out.println(buff); } }
。ui
3.字符流中獨有的InputStreamReader 和OutputStreamWriter 。 使用適配器模式將字節流適配成字符流,這樣就能夠將字節流做爲字符流使用。this
4.輸入流中特有的PushbackXXX 看上圖種都是繼承自XXXFilter ,它們都是=使用了裝飾模式爲讀取的時候增長了回退的功能.下面使用字符形式展現其做用:spa
package com.hotusm.io.learn.string; import java.io.CharArrayReader; import java.io.PushbackReader; /** * PushbackReader 可以將流或者字符進行回推到 緩衝區中. 1.PushbackReader(Reader reader,int size) * size: 每一次的read()的個數 ,默認是1 同時也是緩衝區的大小 2.unread()重載方法 * 將一個或者多個的字符回推到緩衝區中,位置就是上一次讀過的結尾 好比 abcd 讀到c 如今推出gf 那麼就是爲 abcgfd 下次讀的話 就會從 * gf開始讀 3.skip(long size) 跳過size 個字符 */ public class FilterReaderAndPushbackReaderTest { public static void main(String[] args) { testFilterReaderUnreadSingleChar(); System.out.println(); testFilterReaderUnreadMutilChar(); testFilterReaderSkip(); } /** * 輸出:abcCd */ public static void testFilterReaderUnreadSingleChar() { String str = "abcd"; try (CharArrayReader charArrayReader = new CharArrayReader(str.toCharArray()); PushbackReader pushbackReader = new PushbackReader(charArrayReader);) { int c; while ((c = pushbackReader.read()) != -1) { System.out.print((char) c); // unread()的用法 將字符給回推到緩衝區中 if (c == 'c') { pushbackReader.unread('C'); } } } catch (Exception e) { e.printStackTrace(); } } /** * 輸出:abcdefFUCgUC */ public static void testFilterReaderUnreadMutilChar() { String str = "abcdefg"; try (CharArrayReader charArrayReader = new CharArrayReader(str.toCharArray()); PushbackReader pushbackReader = new PushbackReader(charArrayReader, 3);) { char[] byteArr = new char[3]; // read方法會一直讀入構造函數中第二個參數中的數量的字符 while ((pushbackReader.read(byteArr)) != -1) { System.out.print(byteArr); // unread()的用法 將字符給回推到緩衝區中 if (new String(byteArr).equals("def")) { // 推回的不能大於緩衝區的 緩衝區就是咱們構造函數的第二個參數 pushbackReader.unread("FUC".toCharArray()); } } } catch (Exception e) { e.printStackTrace(); } } /** * 輸出:abcfg */ public static void testFilterReaderSkip() { String str = "abcdefg"; try (CharArrayReader charArrayReader = new CharArrayReader(str.toCharArray()); PushbackReader pushbackReader = new PushbackReader(charArrayReader, 3);) { char[] byteArr = new char[3]; // read方法會一直讀入構造函數中第二個參數中的 while ((pushbackReader.read(byteArr)) != -1) { System.out.print(byteArr); //這裏是重點!!! pushbackReader.skip(2L); byteArr = new char[3]; } } catch (Exception e) { e.printStackTrace(); } } }
5.輸出流中特有的兩個PrintWriter和PrintStream,和前面不一樣的是,這兩個除了一個是字符一個是字節以外,還有其餘的不一樣點.它們也要一些類似點.下面一一來展現。操作系統
當咱們使用PrintWriter的時候,但咱們設置了可以自動刷新的話,那麼只有在println,printf,format方法調用的時候纔會起做用,這點和字節流的PrintStream是不一樣的。下面是源碼中的描述:
* <p> Unlike the {@link PrintStream} class, if automatic flushing is enabled * it will be done only when one of the <tt>println</tt>, <tt>printf</tt>, or * <tt>format</tt> methods is invoked, rather than whenever a newline character * happens to be output. These methods use the platform's own notion of line * separator rather than the newline character.
而PrintStream是是要調用了println或者是字符中進行了換行('\n')就會自動的刷新,這是和字符流中的不一樣點。源碼中這樣描述:
<code>PrintStream</code> can be created so as to flush * automatically; this means that the <code>flush</code> method is * automatically invoked after a byte array is written, one of the * <code>println</code> methods is invoked, or a newline character or byte * (<code>'\n'</code>) is written.
參考: http://www.cnblogs.com/oubo/archive/2012/01/06/2394638.html
------------ 增長於17.04.29
關於BufferedReader 在Socket編程中出現堵塞的問題。
首先看下面的代碼(其中client對象是Socket):
private void doCommand(String command) throws IOException { byte[] byets = command.getBytes(); OutputStream outputStream = client.getOutputStream(); outputStream.write(byets); outputStream.flush(); InputStream inputStream = client.getInputStream(); BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream)); String line=bufferedReader.readLine();
上面這段代碼是客戶端Socket向服務端發送一個命令而且獲得迴應。上面的寫法看起來並無任何的問題,可是實際上在運行過程當中你就會發現及時是服務端已經將數據發送完畢以後,客戶端代碼仍然堵塞着。
出現這面問題的緣由是BufferedReader只要在遇到流結尾或者是流關閉的時候纔算做完成,可是在Socket中,即便是服務端輸入完畢,客戶端也不能認爲流關閉了,由於服務端還能夠繼續的向其中輸入流,因此BufferedReader就會認爲這個流一直沒有結尾。正確的代碼應該是:
private void doCommand(String command) throws IOException { BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream())); writer.write(command); writer.flush(); client.shutdownOutput(); BufferedInputStream buffer = new BufferedInputStream(client.getInputStream()); StringBuilder sb = new StringBuilder(); while (true) { int c = buffer.read(); if (c == '\r' || c == '\n' || c == -1) { break; } sb.append((char) c); } System.out.println(sb.toString()); client.shutdownInput();}