IO 字节流
1. 框架
2. ByteArrayInputStream/ByteArrayOutputStream
2.1 ByteArrayInputStream
- ByteArrayInputStream 是字节数组输入流,它继承于InputStream。
- ByteArrayInputStream 包含一个内部缓冲区,该缓冲区包含从流中读取的字节,本质就是通过字节数组来实现的
1 | public class ByteArrayInputStream extends InputStream { |
2.2 ByteArrayOutputStream
ByteArrayOutputStream
是字节数组输出流。它继承于OutputStream
ByteArrayOutputStream
中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用toByteArray()
和toString()
获取数据- 通过
ByteArrayOutputStream
()创建的“字节数组输出流”对应的字节数组大小是32。
3. PipedInputStream/PipedOutputStream
PipedOutputStream
和PipedInputStream
分别是管道输出流和管道输入流。 它们的作用是让多线程可以通过管道进行线程间的通讯PipedOutputStream
是基于PipedInputStream
实现,内部持有PipedInputStream
对象PipedOutputStream
和PipedInputStream
必须配套使用,使用connect
连接PipedOutputStream
写入数据,实际调用的是PipedInputStream
的receive()
方法,PipedInputStream
内部缓存区默认大小为1024个字节。当一次性buffer
写入1024个byte后,会先notifyAll()
,再wait(1000)
,目的就是把刚才写入的内容被读出,然后再继续写入
1 | //PipedOutputStream写入数据 |
PipedInputStream
读取数据,每次read()
前,都会判断缓存区是否有数据(依据in
变量判断),如果没有的话就先让出当前的锁(即让写的进程先运行),然后再去读
1 | // 从管道(的缓冲)中读取数据,并将其存入到数组b中 |
4. ObjectInputStream/ObjectOutputStream
- ObjectInputStream 和 ObjectOutputStream 的作用是,对基本数据和对象进行序列化操作支持
- 只能从流中读取支持java.io.Serializable或java.io.Externalizable接口的对象
5. FileInputStream/FileOutputStream
- FileInputStream 是文件输入流,可以从某个文件中获得输入字节
- FileOutputStream 是文件输出流,将数据写入 File 或 FileDescriptor 的输出流
6. FileDescriptor
- FileDescriptor 是“文件描述符”, 可以被用来表示开放文件、开放套接字等
- 不能直接通过FileDescriptor对该文件进行操作;若需要通过FileDescriptor对该文件进行操作,则需要新创建FileDescriptor对应的FileOutputStream,再对文件进行操作
- in、out、err,标准输入输出的句柄,一般不直接使用;Java封装好了相应接口,可以使用System.in, System.out, System.err
1 | public static final FileDescriptor in = standardStream(0);//标准输入(键盘)的描述符 |
源码如下:
1 | private static native long set(int d); |
可以看出in/out/err就是一个FileDescriptor对象,只是其handle不同(long类型),通过set(fd)来设置其handle。“fd=0”代表了“标准输入”,“fd=1”代表了“标准输出”,“fd=2”代表了“标准错误输出”
7. FilterInputStream/FilterOutputStream
- FilterInputStream/FilterOutputStream 的作用是用来“封装其它的输入输出流,并为它们提供额外的功能”。常用的子类有BufferedInputStream/BufferedOutputStream和DataInputStream/DataOutputStream,PrintStream。
- BufferedInputStream/BufferedOutputStream的作用就是为输入/输出流提供缓冲功能,为输入流提供mark()和reset()功能。
- DataInputStream/DataOutputStream 是用来装饰其它输入输出流,它“允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型”。应用程序可以使用DataOutputStream(数据输出流)写入由DataInputStream(数据输入流)读取的数据。
- PrintStream 是用来装饰其它输出流。它能为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。
8. BufferedInputStream/BufferedOutputStream
8.1 BufferedInputStream
- BufferedInputStream 是缓冲输入流。它继承于FilterInputStream
- 为另一个输入流添加一些功能,例如,提供“缓冲功能”以及支持“mark()标记”和“reset()重置方法”
- 。例如,在新建某输入流对应的BufferedInputStream后,当我们通过read()读取输入流的数据时
8.1.1 原理
本质上是通过一个内部缓冲区数组实现的。创建BufferedInputStream时,我们会通过它的构造函数指定某个输入流为参数。BufferedInputStream会将该输入流数据分批读取,每次读取一部分到缓冲中;操作完缓冲中的这部分数据之后,再从输入流中读取下一部分的数据到缓冲区中。 为什么需要缓冲呢?原因很简单,效率问题!缓冲中的数据实际上是保存在内存中,而原始数据可能是保存在硬盘或NandFlash等存储介质中;而我们知道,从内存中读取数据的速度比从硬盘读取数据的速度至少快10倍以上。
8.1.2 源码分析
- read()
1 | //读取下一个字节 |
- fill()
1 | //从输入流中读取数据,并填充到buffer中 |
8.2 BufferedOutputStream
- BufferedOutputStream 是缓冲输出流。它继承于FilterOutputStream。
- BufferedOutputStream 的作用是为另一个输出流提供“缓冲功能”。
- BufferedOutputStream 关闭流前会进行flush(),将数据刷到输出流;当写入数据超过缓冲区大小时,会将全部数据写入输出流,而不经过缓冲区
9. DataInputStream/DataOutputStream
9.1 DataInputStream
DataInputStream 是用来装饰其它输入流,它“允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型”。应用程序可以使用DataOutputStream(数据输出流)写入由DataInputStream(数据输入流)读取的数据。
readUTF()
1 | public final static String readUTF(DataInput in) throws IOException { |
9.2 DataOutputStream
DataOutputStream
是用来装饰其它输出流,将DataOutputStream
和DataInputStream
输入流配合使用,“允许应用程序以与机器无关方式从底层输入流中读写基本 Java 数据类型”。- writeUTF()
1 | // 将String数据以UTF-8类型的形式写入到“输出流out”中 |
10. PrintStream
PrintStream
是打印输出流,它继承于FilterOutputStream
。PrintStream
是用来装饰其它输出流。它能为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。- 与其他输出流不同,
PrintStream
永远不会抛出IOException
;它产生的IOException
会被自身的函数所捕获并设置错误标记, 用户可以通过checkError()
返回错误标记,从而查看PrintStream
内部是否产生了IOException
。 PrintStream
提供了自动flush 和 字符集设置功能。自动flush,就是往PrintStream
写入的数据会立刻调用flush()函数。- print()方法实际上调用的是write()方法
1 | public void print(int i) { |
- System中的in,out,err
1 | public final static InputStream in = null; |
以out为例,获取过程:
- 首先获取标准输出(屏幕)的标识符out(FileDescriptor对象)
- 创建“标准输出(屏幕)”对应的“文件输出流”
- 创建“文件输出流”对应的“缓冲输出流”。目的是为“文件输出流”添加“缓冲”功能。
- 创建“缓冲输出流”对应的“打印输出流”。目的是为“缓冲输出流”提供方便的打印接口,如print(), println(), printf();使其能方便快捷的进行打印输出
- 执行setOut0(ps) ,将ps设置为out静态成员变量