前言
关于Java,前面我们已经有讲到集合、JUC、JVM等,而今天我们就来说说Java I/O。在我们接触Java的时候,我们使用I/O操作来读写文件,也可以使用它实现Socket的信息传输。而今天我们就来好好总结一下Java I/O。
一、I/O流?
I/O是机器获取和交换信息的主要渠道,而流是完成I/O操作的主要方式。
在计算机中,流是一种信息的转换。流是有序的,因此相对于某一机器或者应用程序而言,我们通常把机器或者应用程序接收外界的信息称为输入流(InputStream),从机器或者应用程序向外输出的信息称为输出流(OutputStream),合称为输入/输出流(I/O Streams)。
机器间或程序间在进行信息交换或者数据交换时,总是先将对象或数据转换为某种形式的流,再通过流的传输,到达指定机器或程序后,再将流转换为对象数据。因此,流就可以被看作是一种数据的载体,通过它可以实现数据交换和传输。
二、IO流的分类
在程序中,流指的是一种信息的交换,我们通常把应用程序接收外界的信息称为输入流(InputStream),从另一端向外输出的信息称为输出流(OutputStream),合称为输入/输出流(I/O Streams)。
而Java也把I/O操作类封装在包java.io下,学过Java的都清楚,其中InputStream、OutputStream以及Reader、Writer类是I/O包中的4个基本类,它们分别处理字节流和字符流。
1、按数据流的方向分为 输入流、输出流
此输入、输出是相对于我们写的代码程序而言,
输入流:从别的地方(本地文件,网络上的资源等)获取资源 输入到 我们的程序中
输出流:从我们的程序中 输出到 别的地方(本地文件),将一个字符串保存到本地文件中,就需要使用输出流。
2、按处理数据单位不同分为 字节流、字符流
1字符 = 2字节 、 1字节(byte) = 8位(bit) 、 一个汉字占两个字节长度
字节流:每次读取(写出)一个字节,当传输的资源文件有中文时,就会出现乱码,
字符流:每次读取(写出)两个字节,有中文时,使用该流就可以正确传输显示中文。
3、按功能不同分为 节点流、处理流
节点流:以从或向一个特定的地方(节点)读写数据。如FileInputStream
处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,
三、字节流、字符流
之前学习Java的时候也做了一个简单的思维导图,如下所示:
在正常情况下,字符到字节必须经过转码,这个过程非常耗时,如果我们不知道编码类型就很容易出现乱码问题。而I/O流提供了一个直接操作字符的接口,方便我们平时对字符进行流操作,下面我们就分别了解下“字节流”和“字符流”。
1、字节流
首先是字节流,那何为字节?
字节(Byte )是计算机信息技术用于计量存储容量的一种计量单位,作为一个单位来处理的一个二进制数字串,是构成信息的一个小单位。
字节流顾名思义就是将我们需要输入输出的文件转化成字节形式,而Java的 InputStream/OutputStream 是字节流的抽象类,这两个抽象类又派生出了若干子类,不同的子类分别处理不同的操作类型,如下所示:
由上图我们简单总结一下:
如果是文件的读写操作,就使用FileInputStream/FileOutputStream;
如果是普通字符串的读写操作,就使用BufferedInputStream/BufferedOutputStream;
如果是数组的读写操作,就使用ByteArrayInputStream/ByteArrayOutputStream;
如果是多线程管道的读写操作,就使用PipedInputStream/PipedOutputStream;
如果是数据的读写操作,就使用DataInputStream/DataOutputStream;
上述的都是节点流,而在字节流中除了节点流之外,如果是要使用处理流,就使用FilterInputStream下的操作类。
2、字符流
接着是字符流,那何为字符?
字符指类字形单位或符号,包括字母、数字、运算符号、标点符号和其他符号,以及一些功能性符号。
上面我们也说了,字符到字节必须经过转码,所以Java针对字符的输入输出流也做了一定处理。
Reader/Writer是字符流的抽象类,这两个抽象类也派生出了若干子类,不同的子类分别处理不同的操作类型,具体内容如下所示:
这里我们吧字符流的Reader/Writer分开简单总结一下:
Reader
Reader 是所有的输入字符流的父类,它是一个抽象类。
CharReader、StringReader 是两种基本的介质流,它们分别将Char 数组、String中读取数据。PipedReader 是从与其它线程共用的管道中读取数据。
BufferedReader 很明显就是一个装饰器,它和其子类负责装饰其它Reader 对象。
FilterReader 是所有自定义具体装饰流的父类,其子类PushbackReader 对Reader 对象进行装饰,会增加一个行号。
InputStreamReader 是一个连接字节流和字符流的桥梁,它将字节流转变为字符流。FileReader 可以说是一个达到此功能、常用的工具类,在其源代码中明显使用了将FileInputStream 转变为Reader 的方法。我们可以从这个类中得到一定的技巧。Reader 中各个类的用途和使用方法基本和InputStream 中的类使用一致。后面会有Reader 与InputStream 的对应关系。
Writer
Writer 是所有的输出字符流的父类,它是一个抽象类。
CharArrayWriter、StringWriter 是两种基本的介质流,它们分别向Char 数组、String 中写入数据。PipedWriter 是向与其它线程共用的管道中写入数据,
BufferedWriter 是一个装饰器为Writer 提供缓冲功能。
PrintWriter 和PrintStream 极其类似,功能和使用也非常相似。
OutputStreamWriter 是OutputStream 到Writer 转换的桥梁,它的子类FileWriter 其实就是一个实现此功能的具体类(具体可以研究一SourceCode)。功能和使用和OutputStream 极其类似,后面会有它们的对应图。
总结
前面我们也说了,字符到字节必须经过转码,那又是怎么实现转换的呢?
Java的I/O流中,具体实现对象如下:
InputStreamReader:字节到字符的桥梁
OutputStreamWriter:字符到字节的桥梁
这两个流对象是字符体系中的成员,它们有转换作用,本身又是字符流,所以在构造的时候需要传入字节流对象进来。
字节流一般用来处理图像,视频,以及PPT,Word类型的文件,而字符流一般用于处理纯文本类型的文件,如TXT文件等,字节流可以用来处理纯文本文件,但是字符流不能用于处理图像视频等非文本类型的文件。
因此,只要是处理纯文本数据,就优先考虑使用字符流,除此之外都使用字节流。
另外,字节流没有缓冲区,是直接输出的,而字符流是输出到缓冲区的。