Java I/O 流

流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。JavaI/O 是建立在流(Stream)之上的,输入流读取数据,输出流写入数据。流是同步的,也就是说当请求流读一段数据时,阻塞等待直到有数据。Java 还支持使用通道和缓冲区的非阻塞 I/ONIO),暂不讨论。

流的分类

按流向分类

  • 输入流: 程序可以从中读取数据的流
  • 输出流: 程序能向其中写入数据的流

按数据传输单位分类

  • 字节流
    以字节(8 位二进制)为单位进行处理。主要用于读写诸如图像或声音的二进制数据。
  • 字符流
    字符流中的对象融合了编码表,以字符为单位,根据码表映射字符,一次可能读多个字节,字符流只能处理字符文本类型的数据。字符流是对字节流进行了封装,方便操作,在最底层所有的输入输出都是字节形式的。后缀是 Stream 是字节流,而后缀是 Reader, Writer 是字符流。

按功能分类

  • 节点流
    从特定的地方读写的流类,如磁盘或者一块内存区域,直接与数据源相连读入或读出。
  • 处理流
    使用节点流作为输入或输出,处理流是使用一个已经存在的输入流或者输出流连接创建的。

流操作的类或接口

基本类和接口

  • File:文件类
  • RandomAccessFile:随机存取文件类
  • InputStream:字节输入流
  • OutputStream:字节输出流
  • Reader:字符输入流
  • Writer:字符输出流

流类的结构图

0037-io-stream.jpg

各个类基本介绍:

  • 文件操作
    FileInputStream, FileOutputStream, FileReader, FileWriter
  • 管道操作
    PipedInputStream, PipedOutStream, PipedReader, PipedWriterPipedInputStream 的实例必须要和 PipedOutputStream 的实例共同使用,共同完成管道的读取写入操作,主要用于线程操作。
  • 字节/字符数组
    ByteArrayInputStream, ByteArrayOutputStream, CharArrayReader, CharArrayWriter,在内存中开辟了一个字节或字符数组。
  • 缓冲流
    BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter,是带缓冲区的处理流。缓冲区的作用的主要目的是:避免每次和硬盘打交道,提高数据访问的效率。
  • 转化流
    InputStreamReader, OutputStreamWriter,把字节转化成字符。
  • 过滤流
    FilterInputStream, FileOutputStream,装饰模式,用来装饰基本流。
  • 数据流
    DataInputStream, DataOutputStream,字节流只能单字节的输出,但是 long 类型(8 字节)或 float 类型(4 字节)是多字节的,需要逐字节或者转换为字符串输出,数据流提供了这个解决方案。数据流可以用二进制格式读写 Java 的基本数据类型和字符串。
  • 打印流
    PrintStream, PrintWriter,一般是打印到控制台。
  • 对象流
    ObjectInputStream, ObjectOutputStream,把封装的对象直接输出,而不是一个个在转换成字符串再输出。
  • 序列化流
    SequenceInputStream,把对象序列化,直接转换成二进制,写入介质中。

流分类表格

0037-io-stream-classific.png

  • Java IO 是采用的是装饰模式,即采用处理流来包装节点流的方式,来达到代码通用性
  • 处理流和节点流的区分方法,节点流在新建时需要一个数据源(文件、网络)作为参数,而处理流需要一个节点流作为参数
  • 处理流的作用就是提高代码通用性,编写代码的便捷性,提高性能
  • 节点流都是对应抽象基类的实现类,它们都实现了抽象基类的基础读写方法

常见输入输出流

Java 提供的基本输入输出流中,字节流类为 java.io.InputStream/OutputStream,字符流类为 java.io.Reader/Writer,它们都是抽象类,提供了读写数据需要的基本方法。

字节流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public abstract class InputStream implements Closeable {
// 从流中读取数据,返回值为读取的长度,-1 表示结束
// read() 默认是读取一个字节
public abstract int read() throws IOException;
public int read(byte b[]) throws IOException {...}
public int read(byte b[], int off, int len) throws IOException {...}
// 跳过指定长度数据,返回真实跳过长度
public long skip(long n) throws IOException {...}
// 返回流中数据长度的估计值,不会阻塞
public int available() throws IOException;
// 关闭流
public void close() throws IOException;

public abstract class OutputStream implements Closeable, Flushable {
// 向流中写入数据,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 {...}
// 如果是带有缓存数据的流,刷新才能发出缓存数据
public void flush() throws IOException;
// 关闭流
public void close() throws IOException;
}

字符流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public abstract class Reader implements Readable, Closeable {
// 阻塞读字符
public int read(java.nio.CharBuffer target) throws IOException {...}
public int read() throws IOException {...}
public int read(char cbuf[]) throws IOException {...}
abstract public int read(char cbuf[], int off, int len) throws IOException;
// 跳过指定长度字符,返回实际跳过长度
public long skip(long n) throws IOException {...}
// 流是否已经准备好可以读
public boolean ready() throws IOException {...}
// 关闭流
abstract public void close() throws IOException;
...
}


public abstract class Writer implements Appendable, Closeable, Flushable {
// 写字符
public void write(int c) throws IOException {...}
public void write(char cbuf[]) throws IOException {...}
abstract public void write(char cbuf[], int off, int len) throws IOException;
public void write(String str) throws IOException {...}
public void write(String str, int off, int len) throws IOException {...}
// 追加字符到当前流中并写入
public Writer append(CharSequence csq) throws IOException {
public Writer append(CharSequence csq, int start, int end) throws IOException {...}
public Writer append(char c) throws IOException {...}
// 刷新写入流
abstract public void flush() throws IOException;
// 关闭流
abstract public void close() throws IOException;
...
}

字节字符转换流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class InputStreamReader extends Reader {
public InputStreamReader(InputStream in) {...}
public InputStreamReader(InputStream in, String charsetName)
throws UnsupportedEncodingException{...}
public InputStreamReader(InputStream in, Charset cs) {...}
public InputStreamReader(InputStream in, CharsetDecoder dec) {...}
// 返回流的编码类型
public String getEncoding() {...}
}

public class OutputStreamWriter extends Writer {
public OutputStreamWriter(OutputStream out, String charsetName)
throws UnsupportedEncodingException{...}
public OutputStreamWriter(OutputStream out) {...}
public OutputStreamWriter(OutputStream out, Charset cs) {...}
public OutputStreamWriter(OutputStream out, CharsetEncoder enc) {...}
// 返回流的编码类型
public String getEncoding() {...}
}

字节字符转换,需要在构造方法中指定编解码类型,如果不指定使用平台默认。

  • InputStreamReader
    可对读取到的字节数据经过指定编码转换成字符。
  • OutputStreamWriter
    可对读取到的字符数据经过指定编码转换成字节。

过滤流

过滤流是装饰模式中的装饰者,基本输入输出流是被装饰者,过滤流用来装饰其他基本流。过滤器流以链的形式进行连接,通过构造方法把流绑定,这种连接是永久的,过滤器无法无法与流断开连接。链中除了最后一个过滤器外,不能从中间流读取数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class FilterInputStream extends InputStream {
/**
* The input stream to be filtered.
*/
protected volatile InputStream in;

public int read() throws IOException {
return in.read();
}
...
}

public class FilterOutputStream extends OutputStream {
/**
* The underlying output stream to be filtered.
*/
protected OutputStream out;

public void write(int b) throws IOException {
out.write(b);
}
...
}

缓冲流

不带缓冲的操作,每读一个字节就要写入一个字节。由于涉及磁盘的 IO 操作相比内存的操作要慢很多,所以不带缓冲的流效率很低。带缓冲的流,可以一次读很多字节,但不向磁盘中写入,只是先放到内存里。等凑够了缓冲区大小的时候一次性写入磁盘,这种方式可以减少磁盘操作次数,速度就会提高很多!
缓冲流将写入的数据存储在缓冲区中,直到缓冲区满或者刷新输出流,然后将数据一次全部写入低层输出流。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class BufferedInputStream extends FilterInputStream {
public BufferedInputStream(InputStream in) {...}
public BufferedInputStream(InputStream in, int size) {...}
...
}

public class BufferedOutputStream extends FilterOutputStream {
public BufferedOutputStream(OutputStream out) {...}
public BufferedOutputStream(OutputStream out, int size) {...}
...
}

public class BufferedReader extends Reader {
public BufferedReader(Reader in) {...}
public BufferedReader(Reader in, int sz) {...}
}

public class BufferedWriter extends Writer {
public BufferedWriter(Writer out) {...}
public BufferedWriter(Writer out, int sz) {...}
}

参数解析:

  • InputStream/OutputStream/Reader/Writer
    底层字节/字符流,可以从中读取或写入未缓冲的数据。字符缓冲流中的参数,通常会使用字节字符转换流作为参数 InputStreamReader/OutputStreamWriter
  • size
    缓冲区的大小,默认为 8192 个字节。

参考文档

  1. Java 源码
  2. Java IO流学习总结
  3. Java IO流分析整理
0%