InputStream和OutputStream 二

1,124 阅读2分钟

在上一篇文章中我们对InputStream的read方法和OutputStream的write方法进行了讲解,希望能对大家所有帮助。今天我们看下两个类剩余的几个方法

InputStream

mark(int readLimits)
reset()

这两个方法常常组合使用,java文档上说mark方法记录在当前流中的读取位置,其中的参数readLimits表示使mark生效的最大已读取字节数,很拗口,简单来说就是从上次reset起,一旦读取的字节数超过了readLimits,那么mark不再生效。

注意:实际上并不是这样,因为即使从上次reset起读取的字节数超过了readLimits,reset仍然生效,stackoverflow上也有专门讨论:stackoverflow.com/questions/4… 有人建议把上述描述中的最大改成最小,个人表示同意

reset表示将当前读取位置恢复到最近一次mark的位置

注意: InputStream的子类会重写markreset方法,比如ByteArrayInputStreamCharArrayInputStream下面例子会详细说明

  private static void byteArrayCase(){
    try(InputStream inputStream = new ByteArrayInputStream("abcdef".getBytes())){
      //第一次读取一个字节
      int data = inputStream.read();
      System.out.println((char)data);

      //标记当前位置 1 并限制最多读取两个字节
      inputStream.mark(2);

      //继续读取三个字节
      byte[] buffer = new byte[3];
      data = inputStream.read(buffer);
      for(int i=0; i< data; i++){
        System.out.print((char)buffer[i]);
      }
      System.out.println();
      //重置到位置1
      inputStream.reset();

      //第三次读完所有字节
      while ((data = inputStream.read(buffer)) != -1){
        for(int i=0; i< data; i++){
          System.out.print((char)buffer[i]);
        }
      }
    }
    catch (IOException e){
      e.printStackTrace();
    }
  }

输出

a
bcd
bcdef

上例先读取一个字节,然后设置mark,并且设置readLimits为2。此时readLimits大于已读取得的字节数,所以mark生效。然后再读取3个字节,然后调用reset,位置回到1,然后再读取,所以就是上面的结果。 上例readLimites大于已读取的字节数(严格意义上是上次调用了reset后读取的字节数),那如果小于是不是就真的不生效呢?看下例

  private static void byteArrayCase1(){
    try(InputStream inputStream = new ByteArrayInputStream("abcdef".getBytes())){
      //读取三个字节
      byte[] buffer = new byte[3];
      int data = inputStream.read(buffer);
      for(int i=0; i< data; i++){
        System.out.print((char)buffer[i]);
      }
      System.out.println();
      //标记当前位置 3 注意此时readLimits小于已读取得字节数3
      inputStream.mark(2);
      //再读取一个字节
      data = inputStream.read();
      System.out.println((char)data);

      //重置到位置3
      inputStream.reset();

      //读完所有字节
      while ((data = inputStream.read(buffer)) != -1){
        for(int i=0; i< data; i++){
          System.out.print((char)buffer[i]);
        }
      }
    }
    catch (IOException e){
      e.printStackTrace();
    }
  }

输出

abc
d
defa

上例先读取3个字节,记录当前位置,注意此时readLimits小于已读取字节数,然后再读取一个字节,但是我们发现mark和reset仍然生效了。看了一下ByteArrayInputStream源码发现:Note: The readAheadLimit for this class has no meaning.意思就是说这个参数对于该类来说无意义。真是...

注意:CharArrayInputStream也是这个样子的呦

对于使用BufferedInputStream的同学更要注意,因为它还跟BufferedInputStream的初始大小buffer有关系,比如下面代码

  private static void bufferedInputStreamCase2(){
    try(InputStream in = new ByteArrayInputStream("abcdef".getBytes());
        BufferedInputStream bufferedInputStream = new BufferedInputStream(in,2);
    ){
      //读取一个字节
      int data = bufferedInputStream.read();
      System.out.println((char)data);
      //标记位置
      bufferedInputStream.mark(1);
      //读取三个字节
      byte[] buffer = new byte[3];
      data = bufferedInputStream.read(buffer);
      for(int i=0; i<data; i++){
        System.out.print((char)buffer[i]);
      }
      System.out.println();
      bufferedInputStream.reset();
      while ((data = bufferedInputStream.read(buffer)) != -1){
        for(int i=0; i<data; i++){
          System.out.print((char)buffer[i]);
        }
      }
    }
    catch (IOException e){
      e.printStackTrace();
    }
  }

上面代码初始化了一个大小为2字节的BufferedInputStream,并且设置markreadLimits的为1,当读取的字节数超过两者之间的最大值时,就会报IO异常,这个你可以作为练习试试,不断调整这三个值,看看观察的结果是否跟我一致,如果不一致敬请留言。

OutputStream

flush()
close()

这两个方法比较简单,第一个是冲刷OutputStream里的缓存,另一个是关闭输出流,释放资源。注意flush是冲刷输出流中的缓存,比如有的输出流内部维护者一个buffer,假定buffer的长度是1024,如果文件大小只有10字节,那么无论如何也填充不满这1024长度的buffer,那么该输出流就会等待下去,此时如果调用flush就可以得到这个10字节的内容。示例如下

  private static void output(){
    InputStream in = null;
    OutputStream out = null;
    BufferedOutputStream bOut = null;
    try{
      in = new FileInputStream("d:\\temp1.txt");
      out = new FileOutputStream("d:\\temp1.txt");
      bOut = new BufferedOutputStream(out,10);
      bOut.write("Hello".getBytes());
      //bOut.flush();
      showContent(in);
    }
    catch (IOException e){
      e.printStackTrace();
    }
  }
  
private static void showContent(InputStream in) throws IOException{
    int data;
    byte[] buffer = new byte[5];
    while((data = in.read(buffer))!= -1){
      for(int i=0; i< data; i++){
        System.out.print((char)buffer[i]);
      }
    }
  }

上面这个方法不能显示Hello,除非取消注释或者关闭bOut。大家可以作为练习试一下

注意:flush冲刷的是输出流内部的缓存,如果是自己定义的缓存,那么无需调用也可以正常输出,比如下面的代码

  private static void output3(){
    InputStream in = null;
    OutputStream out = null;
    try{
      int data;
      byte[] buffer = new byte[20];
      in = new ByteArrayInputStream("Hello world!".getBytes());
      out = new FileOutputStream("d:\\temp1.txt");
      while ((data = in.read(buffer)) != -1){
        out.write(buffer,0,data);
      }
      showContent(new FileInputStream("d:\\temp1.txt"));
    }
    catch (IOException e){
      e.printStackTrace();
    }
  }

  private static void showContent(InputStream in) throws IOException{
    int data;
    byte[] buffer = new byte[5];
    while((data = in.read(buffer))!= -1){
      for(int i=0; i< data; i++){
        System.out.print((char)buffer[i]);
      }
    }
  }

上面代码仍然可以正常输出Hello world!,注意上面代码没有调用closeflush,这种写法只是为了方便展示效果,你应该及时关闭所有资源,大家可以作为练习来修改。