参考:https://www.cnblogs.com/xll1025/p/6418766.html
这个包我们分为这几个部分来讲:
字节流和字符流:
首先了解下字节流和字符流的区别:
我们知道计算机的所有资源都以二进制存储,那么刚才所说的字节便是用于计量存储容量的一种计量单位,通常情况下一字节等于有八位,那么也就是8位的二进制,而字符流就是存储字符信息。那么我们什么时候用字节流和字符流呢?
字符流一般用来处理文本数据,这样想:既然是处理字符的,字符能被我们人类直接理解,而文本就是给人看的,所以我们用字符流来处理文本数据
字节流一般是用来处理各种媒体信息的,如mp3、视频资源等。我们发现这些资源并不是按照字符显示的,是通过计算机处理来达到我们想要的结果,既然要交给计算机处理那么必然是要通过二进制数来实现,所以这些资源我们用字节流实现
再说下输入流和输出流:
这两个流就相当于一个管道
输入流:从文件中读取数据,可以存储数组中
输出流:将数组的内容写入文件中
先说字节流:
类 InputStream
这是所有字节输入流的超类。
类 OutputStream
表示输出字节流的所有类的超类
类 FileInputStream:
FileInputStream
从文件系统中的某个文件中获得输入字节。FileInputStream
用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader
。
这是API上的解释已经很清楚了我们看它的方法
这是我们最常用的构造方法,我们通过输入字符串(字符串是文件路径)来找到要操作的文件。
常用方法:
这三个方法很简单就是读取流中的内容,区别在于参数和返回的数据。
这个方法就是关闭管道,如果一直不关闭会一直占据内存,浪费空间,所以一定要关闭
示例:
public static void fileInputStreamMethod() throws FileNotFoundException {
FileInputStream inputStream=new FileInputStream("D:\\eslspace\\designpattern\\IO\\src\\inputstream\\1.txt");
byte[] by=new byte[10];
try {
inputStream.read(by);//读取文件内容以字节的形式存入字节流
inputStream.close();//关闭管道
} catch (IOException e) {
e.printStackTrace();
}
for(byte b:by)
System.out.println(b);//这是输出这个字节流的内容
try {
String str =new String(by, "UTF-8");
System.out.println(str);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
这是文件信息
执行后
我们看这句:
String str =new String(by, "UTF-8");
这句的作用是将一个字节数组的内容转为指定编码格式并转为一个字符串对象,
假设有中文,我们必须用utf-8(不一定,GBK也可以,但是看自己设置的编码格式)格式输出
类 FileOutputStream
文件输出流是用于将数据写入 File
或 FileDescriptor
的输出流
也就是说当需要将内容写入文件时,使用这个方法。
常用构造方法
这两个方法区别在于第二个参数的有无。
当使用第一个方法时,我们每次往文件写内容时,会从文件开头开始写,之前的存储的信息将全部被覆盖
使用第二个方法时,我们每次写内容时,会从末尾开始写入,之前存储的信息不会被覆盖。
这些是写入数据的方法,很简单没什么要说的
注意:在outputStream类中多了一个方法
因为我们写入的资源都被保存在流中,并没有被保存在文件,这个方法就是将管道中的数据刷新至文件中
但是实践中发现字节流并不需要刷新也会写入文件
常用方法还有close,这个就不讲解了
装饰者类:
我们看到InputStream和OutputStream下还有一个子类
类 FilterOutputStream
此类是过滤输出流的所有类的超类。本身只是简单地重写那些将所有请求传递给所包含输出流的 OutputStream
的所有方法。FilterOutputStream
的子类可进一步地重写这些方法中的一些方法,并且还可以提供一些额外的方法和字段。
类 FilterInputStream
它将这些流用作其基本数据源,它可以直接传输数据或提供一些额外的功能。FilterInputStream
类本身只是简单地重写那些将所有请求传递给所包含输入流的 InputStream
的所有方法。FilterInputStream
的子类可进一步重写这些方法中的一些方法,并且还可以提供一些额外的方法和字段。
我们能发下两个类的功能差不多,都没有特别的方法,它也写了,主要是通过子类来完成。
这让我们想到了装饰者模式,上图
上面说的两个类就像是Deorator,而它的子类就像是ConcreteDecorator,我们发现这两个类继承自InputStream和OutputStream两个类。那么我们现在找到继承InputStream和OutputStream的类,也就是我们要装饰的类,那么很明显这两个类就是FileInputStream和FileOutputStream(这里只是说有这两个,并没有说只有两个,理论上只要是InputStream和OutputStream的直接子类都可以修饰),我们来看装饰者类
类 BufferedInputStream
BufferedInputStream
为另一个输入流添加一些功能,即缓冲输入以及支持 mark
和 reset
方法的能力。在创建 BufferedInputStream
时,会创建一个内部缓冲区数组。在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。mark
操作记录输入流中的某个点,reset
操作使得在从包含的输入流中获取新字节之前,再次读取自最后一次 mark
操作后读取的所有字节。
我们有必要知道不带缓冲的操作,每读一个字节就要写入一个字节,由于涉及磁盘的IO操作相比内存的操作要慢很多,所以不带缓冲的流效率很低。带缓冲的流,可以一次读很多字节,但不向磁盘中写入,只是先放到内存里。等凑够了缓冲区大小的时候一次性写入磁盘,这种方式可以减少磁盘操作次数,速度就会提高很多!
看类:
我们发现方法上别没什么特别,主要是构造函数上,它可以接受一个InputStream类型的类(可指定长度),进行包装
使用方法如图,并没有什么区别只是提高效率
输出流如下,不多赘述。
再说一个输出流比较有特点的包装类
类 PrintStream
PrintStream
为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。它还提供其他两项功能。与其他输出流不同,PrintStream
永远不会抛出 IOException
;而是,异常情况仅设置可通过 checkError
方法测试的内部标志。另外,为了自动刷新,可以创建一个 PrintStream
;这意味着可在写入 byte 数组之后自动调用 flush
方法,可调用其中一个 println
方法,或写入一个换行符或字节 ('\n'
)。
也就是说通过这个包装类,可以直接通过特有的方法来输出各种想要类型的值形式
我们来看看使用方法
结果:
这简直太方便了,不同将字符数组转为字符串那么麻烦了。
概述:
1、字节流和字符流的基本操作是相同的,但是要想操作媒体流就需要用到字节流。
2、字节流因为操作的是字节,所以可以用来操作媒体文件。(媒体文件也是以字节存储的)
3、读写字节流:InputStream 输入流(读)和OutputStream 输出流(写)
4、字节流操作可以不用刷新流操作,但是用了buffer的装饰类就必须刷新流操作。
字符流:
既然字节流有自己的超类,那么字符流也不例外
类 Reader
用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。
这是字符流读的超类
我们看看我们常用的几个子类。
类 InputStreamReader
InputStreamReader 是字节流通向字符流的桥梁:它使用指定的
读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。 charset
最前面说到,计算机的所有信息都是以二进制存储,那么不可能直接使用字符流,实际上是通过这个类完成转换将字节流转换为字符流。
当我们需要将一个字节流变为字符流,我们就先用inputStream连接我们将读取的文件或者别的,将这个inputStream当作构造参数传给InputStreamReader的构造方法。我们直接调用这个方法从inputstream连接的文件或者其他转为字符形式读取
使用方法:
在第一行接受一个字节流,调用方法存入字符数组,然后通过string输出
这个类当然也有包装类,我们看看它的包装类
类 BufferedReader
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
这个类一般搭配着InputStreamReader类使用,因为它不仅提高效率还有几个方法非常方便
’
就是readLine方法,不用转为字符串再使用,直接读取每一行非常方便
使用方法:
我没写循环
我们来看看写方法的超类
类 Writer
写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。
看下它的方法
并没有什么可说的,我们直接看子类
类 OutputStreamWriter
OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的
将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。charset
每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。注意,传递给 write() 方法的字符没有缓冲。
这里说到的字符流转为字节流这句话我迷惑了很,今天总算搞懂了,我们说过计算机存储数据都是以二进制的形式存储,我们使用输入流键入字符串想要存储到文件中那么必须要将字符串变为二进制,才能存储进去,所以这里的字符流通向字节流的桥梁就是这个意思。
我们使用时,将字符写入字符流中然后转为字节交给传入的OutputStream处理
我们看方法
主要要说的是构造方法,我们发现构造方法需要传入输出字节流。和我们刚说的相结合,这说明它的底层实现应该是,先用 OutputStreamWriter接受字符串转为二进制然后通过OutputStream写入文件中。
使用方法:
结果:
我们来看看它的包装类:
类 BufferedWriter
将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。
该类提供了 newLine() 方法,它使用平台自己的行分隔符概念,此概念由系统属性 line.separator 定义。并非所有平台都使用新行符 ('\n') 来终止各行。因此调用此方法来终止每个输出行要优于直接写入新行符。
这个包装类并没有什么特殊的,我们来看看方法
我们发现有一个方法值得我们注意那就是newLine()方法
它的作用就是调用这个方法后,立马在这个位置输出一个换行符。
使用方法:
结果:
我们再来看另一个包装类
类 PrintWriter
向文本输出流打印对象的格式化表示形式。此类实现在 PrintStream
中的所有 print 方法。它不包含用于写入原始字节的方法,对于这些字节,程序应该使用未编码的字节流进行写入。
构造函数:
方法:
经过这个类包装后我们就能通过这个类的一些特定的方法完成了写入特定数据类型了。
使用方法:
结果:
字符流总结:
1. 字符流简介:
* 字符流中的对象融合了编码表,也就是系统默认的编码表。我们的系统一般都是GBK编码。
* 字符流只用来处理文本数据,字节流用来处理媒体数据。
* 数据最常见的表现方式是文件,字符流用于操作文件的子类一般是FileReader和FileWriter。
2.字符流读写:
注意事项:
* 写入文件后必须要用flush()刷新。
* 用完流后记得要关闭流
* 使用流对象要抛出IO异常
* 定义文件路径时,可以用“/”或者“\\”。
* 在创建一个文件时,如果目录下有同名文件将被覆盖。
* 在读取文件时,必须保证该文件已存在,否则出异常