java.io (1) 基本输入输出流

534 查看

一堆废话(赶快跳过)

在我们的日常开发中 I/O 涉及到我们开发的方方面面,虽然早在 JDK1.5 就新增了 NIO的概念,但是 java.io 的使用以及技巧在我们的日常开发中还是占据很高的比重。

概念(不看也行)

最基本的I/O 流是 java.io 包中的抽象类 java.io.InputStreamjava.io.OutputStream。s有预留的设计比较早,因此并没有采用现在流行的面向接口的编程思路,而是采用了抽象类。新增的I\O相关的API则大量使用了接口。如果留的实现只是对使用者暴漏字节这个层次的细节,则可以直接集成InputStream 或 OutputStream 类,并且提供自己二外的能力。

在接下来的几个章节中,会逐章记录
以下各种流类的详细使用方法

  1. BufferedInputStream, BufferedOutputStream 缓冲复用流
  2. ByteArrayInputStream, ByteArrayOutputStream byte数组缓冲流
  3. DataInputStream, DataOutputStream java原生数据流
  4. ObjectInputStream, ObjectOutputStream 对象序列化流
  5. PushbackInputStream, PushbackOutputStream 推回流
  6. PipedInputStream, PipedOutputStream 管道流
  7. SequenceInputStream, SequenceOutputStream 序列流

和字符流类和字符流的的装饰类

  1. Reader, Writer
  2. InputStreamReader, InputStreamWriter
  3. StringReader, StringWriter
  4. BufferedReader, BufferedWriter
  5. CharArrayReader, CharArrayWriter

同时也会记录一些 java8 融入 Google guava 类库对 流类的支持。

基本输入流

输入流 InputStream类中包含两类功能

输入流的两大功能

  1. 读取流中字节数据的功能是通过 read 方法来完成。该方法有以下3种重载形式

    • public abstract int read() throws IOException
    • public int read(byte b[]) throws IOException
    • public int read(byte b[], int off, int len) throws IOException

比较常见的操作流的方式就是 创建一个字节数组作为缓冲区,然后循环读取,直到read方法返回-1或抛出 java.io.IOException 异常。read方法的返回值是每次调用中成功读取的字节数。 需要要注意的是在读取的过程中,对read方法调用是阻塞的。当流中没有数据可用时,对read方法的调用需要等待。这种阻塞式的特性可能会成为应用中的性能瓶颈。

如果不使用字节数组,那么read方法每次只能读取到一个字节,再有缓冲区的情况下虽然InputStream类每次也只是读取一个字节,但是 InputStream类的子类一般会接受一个缓冲区最为参数的read方法提供更高效的实现。

抽象

正像他的名字一样,字节流是流动的,像流水一样。刘中的字节一旦流过去,就无法重新使用。

  1. 控制流的功能。

    • close 关闭功能
      通过 close 方法,我们可以关闭一个 流,就像拧上水管龙头一样。java7及以后,更推荐使用 try-with-resource 语句来使用流。可以避免显示的调用close方法。
    javatry (
        FileInputStream fInput = new FileInputStream(new File("path/to/exists_file"));
        FileOutputStream fOutput = new FileOutputStream(new File("path/to/new_file"));
    ) {
        // ....fInput -> read -> fOutput
    } catch (IOException e) {
        .....
    } catch (FileNotFoundException e) {
        .....
    }
    
    • skip 跳过若干个字节。相当于保留当前的位置往后移动若干个字节,这个功能可以通过skip方法来实现并不是所有的InputStream累都支持skip方法。

    • mark 读取位置标记

    • reset 读取位置重置
      标记和重置是配合使用的,可以实现可以实现流中的内容重复读取,而不会像一般的读取那样操作,数据流过去之后就无法在次读取。但是并不是所有的流都支持标记功能,因此在使用mark方法来标记当前位置之前,需要通过markSupporetd方法来判断当前流的实现是否支持标记功能。
    • available 获取当没有阻塞情况下,当前流中还有多少字节可供读取。如果我们每次读取调用available方法获取到的字节书,不如在读取一个大文件的同时对文件的内容进行处理,如果每次读取是都不发生阻塞,就可以比较好的平均数据读取和处理的时间。

基本输出流

与InputStream类相对应的OutputStream类表示基本的输出流,用来把数据从程序输出到其他地方。基本OutputStream类的对象也是在自己这个层次上操作。其中最主要的是写入数据的write方法。

  1. write 写入方法。
    public abstract void write(int b) throws IOException
    public void write(byte b[]) throws IOException
    public void write(byte b[], int off, int len) throws IOException

    write 方法也有3种类似的重载形式,可以每次写入一个字节,也可以写入一个字节数组中全部或部分内容。

  2. 控制方法

    • close 方法
      关闭当前输出流
    • flush 方法
      强制要求OutputStream类对象对暂时保持内部缓冲区中的内容立即进行实际的写入操作。因为有些OutputStream类的子类会在内部维护一个缓冲区,通过write方法写入的数据会被首先放在这个缓冲区中,然后再摸个适合的实际执行已经缓冲的内容的写入操作。在一般的场景中OutputStream对象的使用者一般不需要直接调用flush方法保证内部缓冲区数据成功写入。这是因为当OutputStream类对象内部的缓冲区满了以后,会自动执行写入操作。OutputStram类的对象被关闭时,flush方法一般也会被自动调用。