好记性不如烂笔头,就拿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();}