虽有嘉肴,弗食,不知其旨也;虽有至道,弗学,不知其善也。是故学然后知不足,教然后知困。知不足,然后能自反也;知困,然后能自强也。
Java 中的 I/O 操作主要指使用 Java 进行输入和输出的操作。Java 的 I/O 机制是基于数据流进行输入和输出的,数据流即表示字符或者字节数据的流动序列。初学的时候感觉繁杂,难以摸清规律,而今,对 Java 中的 I/O 作一简单小结。
简介 I/O
数据流即一串连续不断的数据的集合,如同水管里的水流一样,一端一点点地供水,而另一端可以看到源源不断的水流。
数据写入程序时,可以一段段地向数据流管道中写入数据,这些数据会按着先后顺序,形成一个长的数据流。
对数据读取程序来说,看不到数据流在写入时的分段情况,每次可以读取其中任意长度的数据,但是,只能先读取前面的数据,再读取后面的数据 (不能随机读取
)。
此外,注意,无论写入时是将数据分多次写入,还是作为一个整体一次性写入,读取时的效果是完全一样的。
数据流分类:
流序列中的数据既可以是未经加工的原始二进制数据,也可以是经过特定编码处理后,符合某种格式规定的特定数据。
- 字节流:数据流中最小的数据单元是字节
- 字符流:数据流中最小的数据单元是字符
Java 中,字符是 Unicode 编码,一个字符占用两个字节。
java.io 包中,最重要的即 5 个类和 1 个接口。
- 5 个类:OutputStream、InputStream、Writer、Reader 和 File
- 1 个接口:Serializable
分为以下 3 个层次:
- 最主要的流式:OutputStream、InputStream、Writer 和 Reader
- 非流式:比如 File 类、RandomAccessFile 类和 FileDescriptor 类
- 文件读取部分,及与安全相关的类:比如 SerializablePermission 类
如下所示:
详谈 I/O
如下图:
按照来源/去向分类为:
- File:FileInputStream, FileOutputStream, FileReader, FileWriter
- byte[]: ByteArrayInputStream, ByteArrayOutputStream
- Char[]: CharArrayReader, CharArrayWriter
- String: StringBufferInputStream, StringReader, StringWriter
- 网络数据流:InputStream, OutputStream, Reader, Writer
InputStream
输入流,为字节流,二进制格式。抽象类,基于字节的输入操作,是所有输入流的父类,依靠其子类实现各种功能。
上图几种不同的 InputStream:
- FileInputStream:从文件中读取内容,把一个文件作为 InputStream
- FilterInputStream:抽象类,作为所谓“装饰器”的接口
- PipedInputStream:实现了管道化的概念,在线程中使用
- SequenceInputStream:将多个 InputStream 顺序连接起来
- StringBufferInputStream:将一个 String 对象作为 InputStream,已废弃
- ByteArrayInputStream:将内存中的一个缓冲区作为 InputStream 使用
常用方法如下:
- public abstract int read():读取一个字节的数据,返回值是高位补 0 的 int 类型值,若返回 -1,则说明未读取到任何字节,读取工作结束
- public int read(byte b[]):读取 b.length 个字节的数据,放入到 b 数组中,返回值即是读取的字节数
- public int read(byte b[], int off, int len):从输入流中最多读取 len 个字节的数据,存放到偏移量为 off 的 b 数组中
- public int available():返回输入流中可以读取的字节数。注意,若输入阻塞,当前所在的线程会被挂起;若 InputStream 对象调用该方法,只会返回 0。该方法必须由继承 InputStream 类的子类对象调用才有用
- public void close():完成后,需要关闭打开的流
OutputStream
输出流,为字节流,二进制格式。抽象类,基于字节的输出操作,是所有输出流的父类,依靠其子类实现各种功能。
上图几种不同的 OutputStream:
- FileOutputStream:将数据写入到文件中
- FilterOutputStream:抽象类,作为装饰器的接口
- PipedOutputStream:所有写到其中的内容,会自动作为 “PipedInputStream” 的输出
- ByteArrayOutputStream:在内存中创建一个缓冲区,所有输出到“流”的内容都放置在此区域
常用方法如下:
- public abstract void write(int b):先将 int 转换为 byte 类型,把低字节写入到输出流中
- public void write(byte b[]):将参数 b 中的字节写到输出流中
- public void write(byte b[], int off, int len):将参数 b 从偏移量 off 开始的 len 个字节写到输出流中
- public void flush():将数据缓冲区中的数据全部输出,并清空缓冲区
- public void close():关闭输出流,并释放与流相关的系统资源
Reader
与 InputStream 类似,只是 Reader 是针对字符的。
注意两个:
- InputStreamReader:从输入流中读取字节,再将它们转换成字符
- BufferedReader:Reader 对象作为参数,对其添加字符缓冲器,使用 readline() 读取一行
Writer
与 OutputStream 类似,针对字符,多了 append 操作,也是针对字符的。
总结
如何选择合适的 I/O 流,步骤依次为:
确定输入还是输出
输入:InputStream, Reader
输出:OutputStream, Writer
确定操作的数据对象是否为纯文本
是的:字符流,Reader, Writer
不是:字节流,InputStream, OutputStream
确定具体细节
比如文件
读:FileInputStream, FileReader
写:FileOutputStream, FileWriter
确定是否需要转换流
需要的话,使用转换流,如 InputStreamReader, OutputStreamWriter
确定是否需要缓冲提高效率
需要的话,如 BufferedInputStream, BufferedReader 等
确定是否需要格式化输出
至此,关于 Java 中的 I/O 简单小结完毕。
本人才疏学浅,如有疏漏错误之处,望读者中有识之士不吝赐教,谢谢。
1 | Email: [email protected] / WeChat: Wolverine623 |
您也可以关注我个人的微信公众号 :码农六哥,第一时间获得博客的更新通知,或后台留言与我交流。
参考文献
1.https://docs.oracle.com/javase/8/docs/api/java/io/package-summary.html